Node-Link可视化图中移动Node后自动布局调整算法
如果按工程来说,HyperGraph的可视化,用Node-Link可以说已经比成熟了,不论是D3.js还是各种其他JavaScript库诸如sigma.js等。不过大部分的库还是SVG的,可能一方面是SVG比较方便绑定事件,另一方面SVG也便于定义CSS去扩展样式。基于Canvas的实现也不少,感觉目前比较好看点的是GoJS,虽然free但是不是那么开源的至少js文件uglify过,开源的很好的是VIS.js。不过最喜欢的还是TensorFlow的可视化,每个block可以收缩和展开:
后面自己想用一个超图可视化的工具去管理自己的事情,于是就冲动是魔鬼地自己写起来。目前的实现还不是特别完整,先放出来后面有时间慢慢打磨。
https://github.com/dna2github/dna2petal/blob/master/codon/petal-codon.js
设计
在功能上,Codon是想有block container的收缩和展开功能的,这个牵涉到图的操作,作为一个基础lib,就暂时不管了。只要想办法做到,如果一个container被收缩(内部节点和相关边从可视列表删除)或者被展开(内部节点和相关边加入可视列表),那么这个container相应的大小需要自己调整,为了让节点都可以容易被操作,它们不应该堆叠起来,就是要做到像Force Layout那样。当移动一个节点以后,如果节点间有堆叠,需要重新计算整个layout。当然,可视化里的Zoom和Pan也是必要的。还有一个功能就是自动对可视对象进行排序,这样才能更好的展现节点间的关系(比如上图hello和world节点相连,world节点是container节点的子节点,如果hello和world相连的边在绘画时排序不当被container遮盖住,就会让人以为是hello和container相连了)。
实现
Zoom和Pan的话在代码里还未实现,但是canvas context 2d里有scale和translate函数,还是比较方便的,到时候paint前save,后restore,掌握好节奏就没有问题。
绘画时的排序也不难,拓扑排序,有了parent-children关系,先画parent,然后画和children相关的边,最后画children就可以了。
节点移动后layout调整,如果不考虑效率,还是容易的。
下面就详细说一说layout的调整。
初始化时有7个节点A B C D E F G,其中B C是A的子节点,F G是E的子节点。
当移动C以后,因为C是A的子节点,所以A要能显示出包含C,那么就要重新计算A的框。
下面就是在C的那一层进行冲突判断,因为C移动后,和A的所有其他子节点没有位置冲突,所以B C完成layout。
然后跑到A这一层判断冲突,A可以看作是整个canvas的子节点,那么它和另外两个节点D E判断后,发现D和A有冲突。这时以A的矩形中心为圆心,连接D的中心,将D沿着连接后的射线远离A移动,用倍增距离的形式先搜索到最大的无冲突位置,然后再在区域内二分查找得到最小移动后的无冲突位置。
这时这一层A和C的layout完成,到E时发现和C有冲突,同样的,圆心还是A的中心,对F进行位置重计算。
完成E的位置重计算后,就是对D和E的子节点进行相应平移。D中没有子节点,E中有F和G,所以调整F和G的位置,F和G都没有子节点,不用recursive了。
最终得到了新的layout。
代码中给出的版本,二分搜索的最大次数计算还不是很好,如果节点多了,可能会有些节点被弹开的距离比较大。