画一个优美的树

画一个优美的树

  • OGDF
  • 代码
    • 树结构
    • TreeChart
  • 其它

我的目的是想画一个结构紧凑、没有冲突、节点分布均匀的树。例如 画一个优美的树_第1张图片
pymag-trees是一种画树算法的python实现。 这是这种算法的介绍和具体分析。

OGDF

OGDF,Open Graph Drawing Framework,是一个强大的,独立的c++类库,用于图表的自动布局。她不能直接画图但可以输出svg等格式的文件。

代码

用于布局的代码是我从ogdf中“截取的”,再加上一个树结构保存布局完成后的节点坐标及其它信息。得到坐标后就可以用各种gui框架画出来了,例如qt。

树结构

tree.hh

TreeChart

TreeChart.h

#pragma once

#include 
#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::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::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::iterator_base &subtree, bool upDown);
	void apportion(TreeStructure &ts, tree::iterator_base &subtree, tree::iterator_base &defaultAncestor, bool upDown);
	void secondWalkX(TreeStructure &ts, tree::iterator_base &subtree, double modifierSum);
	void secondWalkY(TreeStructure &ts, tree::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 m_Tree;
	tree::iterator root;

	bool m_bDown;

	int xMove;
	int yMove;


};

TreeChart.cpp

#include "pch.h"
#include "TreeChart.h"

#include 

struct STreeChart::TreeStructure {

	//GraphAttributes &m_ga;
	std::map::iterator_base, int, tree::iterator_base_less> m_number;         //!< Consecutive numbers for children.

	std::map::iterator_base, tree::iterator_base, tree::iterator_base_less> m_parent;        //!< Parent node, 0 if root.
	std::map::iterator_base, tree::iterator_base, tree::iterator_base_less> m_leftSibling;   //!< Left sibling, 0 if none.
	std::map::iterator_base, tree::iterator_base, tree::iterator_base_less> m_firstChild;    //!< Leftmost child, 0 if leaf.
	std::map::iterator_base, tree::iterator_base, tree::iterator_base_less> m_lastChild;	 //!< Rightmost child, 0 if leaf.
	std::map::iterator_base, tree::iterator_base, tree::iterator_base_less> m_thread;        //!< Thread, 0 if none.
	std::map::iterator_base, tree::iterator_base, tree::iterator_base_less> m_ancestor;      //!< Actual highest ancestor.

	std::map::iterator_base, double, tree::iterator_base_less> m_preliminary; //!< Preliminary x-coordinates.
	std::map::iterator_base, double, tree::iterator_base_less> m_modifier;    //!< Modifier of x-coordinates.
	std::map::iterator_base, double, tree::iterator_base_less> m_change;      //!< Change of shift applied to subtrees.
	std::map::iterator_base, double, tree::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 &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::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::iterator_base()));
			m_leftSibling.insert(std::make_pair(poit, tree::iterator_base()));
			m_firstChild.insert(std::make_pair(poit, tree::iterator_base()));
			m_lastChild.insert(std::make_pair(poit, tree::iterator_base()));
			m_thread.insert(std::make_pair(poit, tree::iterator_base()));
			m_ancestor.insert(std::make_pair(poit, tree::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::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::iterator_base();
					m_leftSibling[v] = tree::iterator_base();
				}
				else {
					m_firstChild[v] = m_lastChild[v] = tree::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::iterator_base();
					m_leftSibling[v] = tree::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::sibling_iterator sit, psit;
				m_firstChild[v] = sit = psit = v.begin();
				m_number[m_firstChild[v]] = childCounter = 0;
				m_leftSibling[m_firstChild[v]] = tree::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::iterator_base &v) const
	{
		//OGDF_ASSERT(v != nullptr);

		// node v is a leaf if and only if no edge leaves v
		return tree::number_of_children(v) == 0;
	}

	// returns the successor of node v on the left contour
	// returns 0 if there is none
	tree::iterator_base nextOnLeftContour(tree &m_Tree, tree::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::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::iterator_base nextOnRightContour(tree &m_Tree, tree::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::iterator_base &subtree, tree::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::iterator_base leftAncestor, rightAncestor;

	// start the traversal at the actual level
	tree::iterator_base leftContourOut = ts.m_firstChild[ts.m_parent[subtree]];
	tree::iterator_base leftContourIn = ts.m_leftSibling[subtree];
	tree::iterator_base rightContourIn = subtree;
	tree::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::iterator tmp1(ts.m_parent[ts.m_ancestor[leftContourIn]]);
				tree::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::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::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::iterator_base defaultAncestor = ts.m_firstChild[subtree];
		
		// apply firstwalk and apportion to the children
		tree::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::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::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::sibling_iterator sit;
	for (sit = subtree.begin(); sit != subtree.end(); ++sit) {
		secondWalkX(ts, sit, modifierSum);
	}
}

void STreeChart::secondWalkY(TreeStructure &ts, tree::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::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::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::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::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::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::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::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::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::iterator it;
			for (it = m_Tree.begin(); it != m_Tree.end(); ++it) {
				it->x = -it->x;
			}
		}

	}
}

其它

有需要的欢迎交流。

你可能感兴趣的:(树)