岁月流转,往昔空明

C++博客 首页 新随笔 联系 聚合 管理
  118 Posts :: 3 Stories :: 413 Comments :: 0 Trackbacks
呃,怎么说呢,这个和vczh的同名文章是互为补充的。这是最近老板的要求,所以就写了这么个东西。

vczh的方法生成的树是sparse的,而我这里树则要紧凑一些,所使用的坐标系也与之不同。

效果(看起来挺菜,哇咔咔)


布局分为水平布局和竖直布局两个部分,我这里先进行了水平布局再进行了竖直布局。

一般来讲紧凑的树主要指同级节点方向上的紧凑性。由于这里树是父节点在上,子结点在下,因此水平方向上的节点要尽量紧凑。那么便需要我将节点尽量往左布局。
如果自左往右布置节点,那么很显然,左边的节点的位置一旦固定下来就不需要再行调整。
同时,为了保持树的美观,父节点的水平中心应当是子结点的水平中心,这样子结点就会对称分布在父节点的下方,有利于美观。
然而父节点能正常布局的位置,子结点可能无法正常布局(子结点的宽度比父节点宽得多)。因此我们还需要调整子结点的布局。
由于是自左向右布局的,此时子结点的左边的节点都已经确定了,正确的布局很容易便可以通过右移得到。
为了保证正确性,还需要把父节点也右移。因为父节点的右边没有布局限制,因而可以放心的右移。这样一直传递到根节点就可以了。
那么很明显,整体来说,布局顺序就是,自下而上,自左而右。

为了让节点在布局的时候知道自己能被安排的最左位置,需要为每一层保存最左可布局位置的坐标。
一旦有节点被安排妥当,便更新节点所在层次的最左可布局位置。后来的节点只要在这个位置的右方布局,就不会与已布置的节点冲突。

竖直布局并不复杂,算出每一层的Top就可以了。

代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

//布局算法:
//采用平行节点最左布局,父子节点对中布局的原则。

/****
 *  void Layout(int lyr)
 *  {
 *     
 *  }
 * 
****
*/
namespace CrowdTree
{
    
class LayoutTreeLayerInfo
    {
        
//可安排布局的最左坐标
        private Dictionary<intfloat> lyrLefts = new Dictionary<int,float>();
        
private Dictionary<intfloat> heights = new Dictionary<int,float>();

        
public void Reset()
        {
            lyrLefts 
= new Dictionary<intfloat>();
            heights 
= new Dictionary<intfloat>();
        }

        
public float GetLayoutableLeft(int level)
        {
            
if (lyrLefts.ContainsKey(level))
            {
                
return lyrLefts[level];
            }
            
return 0;
        }

        
public void SetLayoutableLeft(int level, float left)
        {
            
if (lyrLefts.ContainsKey(level))
            {
                lyrLefts[level] 
= Math.Max(left, lyrLefts[level]);
            }
            
else
            {
                lyrLefts[level] 
= Math.Max(0, left);
            }
        }

        
public void SetLayoutLevelMinHeight(int level, float height)
        {
            
if (heights.ContainsKey(level))
            {
                heights[level] 
= Math.Max(heights[level], height);
            }
            
else
            {
                heights[level] 
= height;
            }
        }

        
public float GetLayoutLevelMinHeight(int level)
        {
            
if (heights.ContainsKey(level))
            {
                
return heights[level];
            }
            
return 0;
        }
    }

    
class LayoutTreeNode
    {
        
protected LayoutTree owner_;
        
protected LayoutTreeLayerInfo lyrInfo_;
        
protected int lyrLevel_;
        
protected RectangleF rect_;
        
protected List<LayoutTreeNode> children_ = new List<LayoutTreeNode>();

        
public int Level
        {
            
get { return lyrLevel_; }
            
set { lyrLevel_ = value; }
        }

        
public LayoutTree Owner
        {
            
get { return owner_; }
            
set { owner_ = value; }
        }

        
public LayoutTreeLayerInfo LayerInfo
        {
            
set { lyrInfo_ = value; }
        }

        
public List<LayoutTreeNode> Children
        {
            
get { return children_; }
        }

        
public RectangleF Extent
        {
            
get { return rect_; }
        }

        
protected void LayoutHorizontal()
        {
            
//获取当前节点能够排布的最左位置,并预先安排当前节点。
            float originLeft = lyrInfo_.GetLayoutableLeft(lyrLevel_);
            rect_.X 
= originLeft;

            
//根据当前结点的坐标,安排子结点,并需要根据子结点的安置情况重新调整父节点的安置
            if (children_.Count > 0)
            {
                
//计算子结点最左可以安放的位置
                float childrenTotalWidth = 0.0F;
                
foreach (LayoutTreeNode child in children_)
                {
                    childrenTotalWidth 
+= child.Extent.Width;
                }
                childrenTotalWidth 
+= ((children_.Count - 1* owner_.HorizontalSpacer);
                
float childLeftest = originLeft + (rect_.Width / 2.0f- (childrenTotalWidth / 2.0F);

                
//设置子结点安放的最左位
                lyrInfo_.SetLayoutableLeft(lyrLevel_ + 1, childLeftest);

                
//安放子结点
                for (int idxChild = 0; idxChild < children_.Count; ++idxChild)
                {
                    children_[idxChild].LayoutHorizontal();
                }

                 
//利用子结点重新对中安置当前节点
                float center = (children_[0].Extent.Left + children_[children_.Count - 1].Extent.Right) / 2.0F;
                rect_.X 
= center - rect_.Width / 2.0F;
            }

            
//利用节点坐标设置该层其他子结点所能安放的最左位置,并设置一下当前层元素的最大高度
            lyrInfo_.SetLayoutableLeft(lyrLevel_, this.rect_.Right + owner_.HorizontalSpacer);
            lyrInfo_.SetLayoutLevelMinHeight(lyrLevel_, 
this.rect_.Height);
        }

        
protected void LayoutVertical(float top)
        {
            rect_.Y 
= top;
            
foreach (LayoutTreeNode child in children_)
            {
                child.LayoutVertical(top 
+ lyrInfo_.GetLayoutLevelMinHeight(lyrLevel_) + owner_.VerticalSpacer);
            }
        }

        
public void Layout()
        {
            LayoutHorizontal();
            LayoutVertical(
0.0f);
        }

        
public virtual void CalculateSize(float maxWidth, Font font, Graphics g)
        {
        }

        
public virtual void CalculateSizeRecursive(float maxWidth, Font font, Graphics g)
        {
        }

        
public virtual void Draw(Graphics g) { }
    }

    
class TextLayoutTreeNode: LayoutTreeNode
    {
        
private string text;
        
private StringFormat strFmt = new StringFormat();
        
private Font font;

        
public String Text
        {
            
get { return text; }
            
set { text = value; }
        }

        
public override void CalculateSize(float maxWidth, Font font, Graphics g)
        {
            strFmt.Alignment 
= StringAlignment.Center;
            strFmt.LineAlignment 
= StringAlignment.Center;

            rect_.Size 
= g.MeasureString(text, font, (int)maxWidth, strFmt);
            
this.font = font;
        }

        
public override void CalculateSizeRecursive(float maxWidth, Font font, Graphics g)
        {
            CalculateSize(maxWidth, font, g);
            
foreach (LayoutTreeNode node in children_)
            {
                node.CalculateSizeRecursive(maxWidth, font, g);
            }
        }

        
public override void Draw(Graphics g)
        {
            
//外轮廓,内容,连线
            g.DrawRectangle(Pens.Black, Rectangle.Round(Extent));
            g.DrawString(text, font, Brushes.Red, Extent);

            
foreach (LayoutTreeNode childNode in children_)
            {
                
//绘制斜线
                
                
float childX = (childNode.Extent.Left + childNode.Extent.Right) / 2.0F;
                
float childY = childNode.Extent.Top;
                
                
float curX = (Extent.Left + Extent.Right) / 2.0f;
                
float curY = Extent.Bottom;

                g.DrawLine(Pens.Black, 
new PointF(childX, childY), new PointF(curX, curY));
            }
        }
    }

    
class LayoutTree
    {
        
private float vertical_spacer;
        
private float horizontal_spacer;

        
private LayoutTreeNode root;
        
private LayoutTreeLayerInfo lyrInfo = new LayoutTreeLayerInfo();

        
public float VerticalSpacer
        {
            
get { return vertical_spacer; }
            
set { vertical_spacer = value; }
        }

        
public float HorizontalSpacer
        {
            
get { return horizontal_spacer; }
            
set { horizontal_spacer = value; }
        }

        
public LayoutTreeNode Root
        {
            
get { return root; }
            
set { root = value; }
        }

        
public void ResetLayout()
        {
            lyrInfo.Reset();
        }

        
public T CreateNode<T> (LayoutTreeNode parent) where T:LayoutTreeNode, new()
        {
            T retNode 
= new T();
            
            retNode.Owner 
= this;
            retNode.LayerInfo 
= this.lyrInfo;

            
if (parent == null)
            {
                
this.root = retNode;
                retNode.Level 
= 0;
            }
            
else
            {
                parent.Children.Add(retNode);
                retNode.Level 
= parent.Level + 1;
            }
            
return retNode;
        }
    }
}

posted on 2008-09-13 12:42 空明流转 阅读(1571) 评论(2)  编辑 收藏 引用

评论

# re: 将树型数据结构转换为图片并自动排版 2008-09-13 15:49 陈梓瀚(vczh)
嗯嗯,很好……  回复  更多评论
  

# re: 将树型数据结构转换为图片并自动排版 2008-11-11 23:01 李华君(鲤鱼潜水)
路过,加油噢  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理