正交布局算法思想(画树布局思想)
由于公司需要给用户展示逻辑图,于是要定义一套正交布局算法实现布局。笔者在网上找了一些例子都没有具体写清楚,于是翻遍了论文,并且整理了一套适用的算法。
这是我实现的结果,正交布局其实就是画一颗树。知道这一点对于设计有很大的帮助。首先介绍下实现树布局的几个原则:
P1.树的边不应该相互交叉。
P2.相同深度的所有节点赢绘制在同一水平线上。
P3.同一层级的树节点应该有一定的空隙。
P4.父母应该集中在子节点身上。
根据这几个原则,画树的时候分成两步:
1.初始化树数据(此步会记录树的上联下联节点数据,以及所有叶子节点的数据)。
2.画树(此步会根据树的后序遍历方法计算子节点位置,并调整父节点的位置,保持父节点相对子节点居中)。
笔者使用的是简单的winform的控件实现了一下思路,使用Button代表树节点,并Paint响应事件重绘窗口画红色的线。
下面是自己定义的树节点类,代码如下:
public class TreeNode{/// <summary>/// 树节点编号/// </summary>public uint ID { get; set; }/// <summary>/// 树节点深度(从零开始)/// </summary>public int Deep { get; set; }/// <summary>/// 树节点子节点/// </summary>public List<TreeNode> Children { get; set; }/// <summary>/// 树节点父节点/// </summary>public TreeNode Parent { get; set; }/// <summary>/// 树节点横坐标/// </summary>public int X { get; set; }/// <summary>/// 树节点纵坐标/// </summary>public int Y { get; set; }public TreeNode() {Children = new List<TreeNode>();}}
根据图一初始化的树,会有个全局变量记录根节点,既是节点2,3,4,7,9,10,11这几个节点。后序遍历树的顺序为:2-3-4-1-7-6-9-10-8-5-11-0,这里不懂的需要去复习下数据结构树的后序遍历。这里明显可以看出只要树的子节点之间做好步长空隙计算就可以保证计算完成之后满足所有设计原则。下面是实现这个思路的代码展示:
/// <summary>/// 控件横向步长/// </summary>private const int STEP_X = 100;/// <summary>/// 控件纵向步长/// </summary>private const int STEP_Y = 30;/// <summary>/// 叶子节点集合/// </summary>private List<TreeNode> _leafNodesList;/// <summary>/// 树对象/// </summary>private TreeNode _root;/// <summary>/// 连线端点元组集合/// </summary>private List<Tuple<Point, Point>> _lineList = new List<Tuple<Point,Point>>();private int _halfNodeSum;private int _startY;private int _startX;public Form1(){InitializeComponent();_root = InitTree();_halfNodeSum = _leafNodesList.Count / 2;_startY = Height / 2;_startX = Width / 2 + 30;DrawTree(_root);this.Paint += Form1_Paint;}private void Form1_Paint(object sender, PaintEventArgs e){Pen pen = new Pen(Color.Red);Graphics g = e.Graphics;foreach (var kvItem in _lineList){g.DrawLine(pen, kvItem.Item1, kvItem.Item2);}}/// <summary>/// 初始化树/// </summary>private TreeNode InitTree() {TreeNode root = CreateTreeNode(0, 0, null);TreeNode root1 = CreateTreeNode(1, 1, root);root.Children.Add(root1);TreeNode root2 = CreateTreeNode(2, 2, root1);root1.Children.Add(root2);TreeNode root3 = CreateTreeNode(3, 2, root1);root1.Children.Add(root3);TreeNode root4 = CreateTreeNode(4, 2, root1);root1.Children.Add(root4);TreeNode root5 = CreateTreeNode(5, 1, root);root.Children.Add(root5);TreeNode root6 = CreateTreeNode(6, 2, root5);root5.Children.Add(root6);TreeNode root7 = CreateTreeNode(7, 3, root6);root6.Children.Add(root7);TreeNode root8 = CreateTreeNode(8, 2, root5);root5.Children.Add(root8);TreeNode root9 = CreateTreeNode(9, 3, root8);root8.Children.Add(root9);TreeNode root10 = CreateTreeNode(10, 3, root8);root8.Children.Add(root10);TreeNode root11 = CreateTreeNode(11, 1, root);root.Children.Add(root11);_leafNodesList = new List<TreeNode>() { root2, root3, root4, root7, root9, root10, root11 };return root;}/// <summary>/// 创建树节点/// </summary>private TreeNode CreateTreeNode(uint id, int deep, TreeNode pTreeNode) {TreeNode treeNode = new TreeNode() { ID = id, Deep = deep, X = deep * STEP_X, Parent = pTreeNode };return treeNode;}private void DrawTree(TreeNode treeNode) {for (int i = 0; i < treeNode.Children.Count; i++){DrawTree(treeNode.Children[i]);}int y = GetCoordinateY(treeNode);treeNode.Y = y;DrawButton(treeNode.ID.ToString(), treeNode.X, y);for (int i = 0; i < treeNode.Children.Count; i++){_lineList.Add(new Tuple<Point, Point>(new Point(treeNode.X + 30, treeNode.Y + 20), new Point(treeNode.Children[i].X + 30, treeNode.Children[i].Y + 20)));}}private int GetCoordinateY(TreeNode treeNode) {int coordinateX = 0;if (treeNode.Children.Count > 1){coordinateX = (treeNode.Children[0].Y + treeNode.Children[treeNode.Children.Count - 1].Y) / 2;}else if (treeNode.Children.Count == 1){coordinateX = treeNode.Children[0].Y;}else{coordinateX = GetLeafCoordinate(treeNode.ID);}return coordinateX;}/// <summary>/// 获取叶子节点的Y轴坐标/// </summary>private int GetLeafCoordinate(uint id) {int coordinate = 0;for (int i = 0; i < _leafNodesList.Count; i++){if (_leafNodesList[i].ID != id) continue;if (i <= _halfNodeSum){coordinate = _startY + STEP_Y * (_halfNodeSum - i);}else{coordinate = _startY - STEP_Y * (i - _halfNodeSum);}break;}return coordinate;}private void DrawButton(string name, int x, int y) {Button button = new Button();button.Size = new System.Drawing.Size(60, 20); button.Location = new Point(x + 30, y + 10);button.Text = name;button.UseVisualStyleBackColor = true;this.Controls.Add(button);}}
好了,这就是所有实现过程,笔者想要再优化一下,考虑怎么实现树的纵向展示与横向展示切换。
遇到的问题:笔者在画线的时候犯了个愚蠢的错误,使用控件的CreateGraphics方法并没有真正的重绘窗口,必须在Paint事件中重绘才会有效果。
正交布局算法思想(画树布局思想)相关推荐
- IOS应用管理学习,进阶,涉及字典转模型,工厂方法,面向对象思想,页面布局等
IOS应用管理学习,进阶,涉及字典转模型,工厂方法,面向对象思想,页面布局等 前言:人为规定的参数 每一个 小view视图 宽度 80 高度 90 数据类型 CGFloat 定义, 3 列,数据类型 ...
- 算法学习之——矩形切割思想
算法学习之--矩形切割思想 MPS [定义 Define] 对于求解若干个矩形的面积的交集 ...
- 心灵之光、思想之树:推荐《作家曰》
<作家曰:深圳晚8点文学对话录>由海天出版社出版了,这是我所见过的国内出版最快的一本书,从稿件齐全到出书上市一共不到半个月时间,绝对是深圳速度,令人惊叹.这本书是深圳文化学者胡野秋先生主持 ...
- android梅花形布局,技法介绍——画梅花时的布局
原标题:技法介绍--画梅花时的布局 技法介绍--画梅花时的布局 作画的布局,要在理解了花卉的生长规律.结构特征.并懂得了基本造型方法和笔墨要求后,进入摹写或创作时,必然会碰到的问题.同样是一种花卉,为 ...
- LSM树——Log-Structured Merge-Tree数据结构、LSM树设计思想、LSM的数据写入操作、LSM的数据查询操作
LSM树数据结构 简介 传统关系型数据库,一般都选择使用B+树作为索引结构,而在大数据场景下,HBase.Kudu这些存储引擎选择的是LSM树.LSM树,即日志结构合并树(Log-Structured ...
- 有向有环图的层次布局算法(一)
[本文介绍的有向有环图的层次布局算法是Sugiyama和Gansner的算法.] 目标 有向有环图(Directed Cycline Graph DCG)的定义就不解释了,这里首先列出DCG布局的期望 ...
- HBase是如何应用LSM树存储思想的
前言 想了解HBase存储原理,最直观的方式是先了解它使用的存储思想,然后通过类比法,即可秒懂HBase为什么要这么设计了.所以再学习HBase如何实现存取的时候,最好先理解LSM树存储思想. 研究一 ...
- 从无到有算法养成篇-利⽤栈思想解决问题
一.什么时候用到栈思想? 栈的思想应⽤数据是线性的,问题可以利⽤栈的特性先进后出去解决问题! 二:思想实战 1.括号匹配检验:假设表达式中允许包含两种括号:圆括号与⽅括号,其嵌套顺序随意,即() 或者 ...
- python3画圆、直线_Bresenham直线算法与画圆算法
在我们内部开发使用的一个工具中,我们需要几乎从 0 开始实现一个高效的二维图像渲染引擎.比较幸运的是,我们只需要画直线.圆以及矩形,其中比较复杂的是画直线和圆.画直线和圆已经有非常多的成熟的算法了,我 ...
最新文章
- 关系数据理论中的范式
- 企业级工作流解决方案(八)--微服务Tcp消息传输模型之服务端处理
- 什么是进程控制块,他有什么用?
- [BUUCTF-pwn]——ciscn_2019_ne_5
- Python内置数据类型之Dict
- plist java_帮助plist文件
- Linux 按行分割文件(转载)
- 3.9 使用标尺工具拉直倾斜的图片 [Ps教程]
- synchronized锁住的对象
- 基于CIM的新型智慧城市发展政策解析
- 手机OTG 我的世界_手机触摸失灵不更换屏幕,如何把手机里面的资料导出来,值得收藏...
- 一个不能吃的中秋月饼
- 记小米路由器Pro(R3P)刷机openwrt、潘多拉、lean-openwrt、x-wrt
- 愚人节巧用CSS开个极客式玩笑以chrome为例
- PTA 7-44 本题要求编写程序,输入一个三位数,输出其个位数字、十位数字和百位数字。
- Aop切面编程原理和Spring实现
- Visual Studio 2019 详细安装和使用教程
- 发票预制和过账冻结与付款冻结的关联
- LOJ6001 - 「网络流 24 题」太空飞行计划
- 东莞塑料是四大工程材料