出处:http://www.fengchang.cc/post/24前端
家谱的数据结构并不复杂,逻辑上能够抽象成一种图,节点为人物,边为人物关系,关系粗略分为两类,一类是跨层级的亲子关系(如父子,父女,母子,母女),另外一类为同层级的夫妻关系(其实若是要加上更多的也能够)。有了这两类关系,就能够彻底地描述一个家谱人物关系。那么在数据库中表示只须要两张表就够了,一个person表,一个relation表算法
person表的形式能够为(id, name, sex ...), relation表的形式能够为(id, from_person_id, to_person_id, relation_name)数据库
这种存储方式能够很方便地查询到一个完整地家谱,固然也有关系型数据库固有的缺点,就是很差作单人的连续的层级遍历,例如找一我的祖上十八代,那必定是对应大量的table join, 不过我这里不考虑这个问题,只专一于如何表征一个完整的家谱,并能自动排版在前端展现,最终要达到的一个效果如图数据结构
这张图在数据库层面就是按照如上描述存储的,然而前端要绘制成这样的树形结构则须要花一点点小功夫。post
我这里使用d3的force directed graph进行绘制。d3的example图是这样的:网站
看起来是否是乱得一塌糊涂?若是你只按照上面的表关系创建好数据,而后直接用d3画图,结果也必然是这个样子。那如何把它变成看起来比较干净整洁的相似树形图呢?d3是没办法按照咱们的要求自动排版的,缘由很简单,咱们的要求有三个,第一要分层级(父母在上子女在下),第二,线条交叉要尽可能少,不要太杂乱无章,第三,树看起来比较平衡(例如不要很右边的父节点连到图最左边的子节点,难看的很),很显然,这种要求属于高度定制的要求,d3是不可能自动给你排的,那怎么作呢?个人思路是经过某种算法,肯定图中每个人物的坐标(x,y),使得知足上面的3个要求,则天然结果图可以整洁。是否是废话?待我细细说来。。。.net
第一步,计算层级。blog
思路以下,先定一个记录标准:最上层为1层,其子所在层为2,再往下一层为3,以此类推。那么在给定一个图以后,只要这个图是连通图,那么从一个节点沿着关系必定能走到任意其余节点,基于这个前提,我用一种想象中的染色法,想象图中全部节点一开始都是白色,而后选定任意一个节点开始,随意标记一个层级,例如10,染成红色,而后从该红色节点出发,沿着其全部关系递归遍历其余节点,遍历时,若是是向上走,则走到的节点层级减1,向下走,则走到的节点层级加1,同层走,则走到的节点层级相同,直到全部节点都变成红色。这样递归完成后,全部的层级都定下来了,可是因为初始节点的层级是随便取的,最终获得的结果多是10,11,12,13。。这样的层次,只要再作个“归一”,即把最小的层级变成1(例如若是层级列表为(10,11,12),那么只要统一减去9,便可"归一"为(1,2,3))。递归
第二部:减小线条交织,自动调整层高get
第一步作完之后,全部的节点都被分到了对应的层级,但仅仅这样画出来的图必定仍是很差看,例如一对夫妻在同层级,可是若是一个放在图最左,一个放在图最右,中间还放了不少其余兄弟节点,那么这就很难看,亦或是A放在B的左边,而后A的后代却放在B的后代的右边,那么可想而知这里又会有不少没必要要的线条交织,影响美观,因此要作到几件事,包括:一、把夫妻要并在一块儿放置,若是A和B是夫妻,C和D是夫妻,那么应该是ABCD这样的排布ok,但若是是ACDB这样就不行。二、若是甲和乙是亲兄弟,甲在乙的左边,那么甲的后代必须也在乙的后代的左边,递归传下去。3,每层的层间距也不该固定,例如古代皇帝,有些有一百多个儿子,有些就一两个儿子,那么稍微想象,也能知道,画前者的层间距应该大于后者的层间距才好看,不然儿女多的人发散出去的线条会画得很是扁平。
第三步,树的平衡
这一步是基于前两步来作的,最终可以肯定每一个节点的列位置,若是说第一步肯定了层位置,第二步粗粒度肯定了列位置(排好了层级内每一个节点的位序),那么这一步则是细粒度最终肯定了列位置。根据实测,最终定了3个原则来惟一肯定一个节点的列位置,第一,位序靠后的节点列坐标必定要大于位序靠前的列坐标,例如某层内根据第二步肯定好的位序为(A,B,C,D,E)则,B的列坐标必定大于A的列坐标,C的必定大于B的,以此类推。第二,一个节点的后代叶子节点数越多,它占领的该层的列空间就要越大,同时与下层的距离也要越大,每层和下一层的最终距离为该层全部节点的这种距离中的最大者。第三,一个节点的位置还受到其父节点的影响,父节点如有n个后代叶子节点,则本节点的列坐标不该该小于父节点的列坐标-n/2。根据这三个原则,就能肯定惟一的节点位置。
具体的算法代码用到了大量的记帐式递归(recursion+memoization),例如计算层级,计算某节点的后代叶子节点数,拆开来看都不算复杂,拼在一块儿会有点绕
所得结果的演示,已有网站成品:http://www.familytreesea.com/public-tree-detail/11
欢迎你们体验和使用
--------------------- 本文来自 无产光辉指 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/xunileida/article/details/80250608?utm_source=copy