基于力导向算法,Java实现星形拓扑图

基于力导向算法,Java实现星形拓扑图_第1张图片
产品最终效果图1

D3.js里面有个force类可以实现对网络拓扑图的绘画,其根本基础是基于力导向算法来实现的。
相关API信息如下:
https://github.com/d3/d3/blob/master/API.md#forces-d3-force

力导向算法借用力学概念
系统中的每个节点都可以看成是一个带有一定能量的放电粒子。

  • 粒子与粒子之间存在某种库仑斥力,使它们两两相互排斥。
  • 同时,有些粒子间被一些“边”所牵连,这些边产生类似弹簧的胡克引力,又紧紧牵制着“边”两端的粒子。
  • 在粒子间斥力和引力的不断作用下,粒子们从随机无序的初态不断发生位移,逐渐趋于平衡有序的终态。
  • 同时整个物理系统的能量也在不断消耗。
  • 经过数次迭代后,粒子之间几乎不再发生相对位移,整个系统达到一种稳定平衡的状态,即能量趋于零。

算法初始化时会设置一个相互作用力,随机让点分布在画布上,通过相互作用力的牵引和排斥关系使得点进行运动。(这边是通过点与点之间的距离来计算,所以有可能会导致线出现交叉)

每次运动在算法里都是一次递归的过程。

最后会处于趋向于平衡的状态。但是不会完全平衡。

使用D3实现的网络拓扑:
数据基于网络拓扑算法计算的结果
数据: topo.sql
相关算法:[email protected]:chenbo1/topoAlgorithm.git
相关文章:Topo算法思路

基于力导向算法,Java实现星形拓扑图_第2张图片
D3 力导向图

d3源码

  
    
          
        力导向图  
   


      
          
          
        
      
  

用Java实现:

同样的数据,基于HTML5 canvas实现的简易画图
算法代码:[email protected]:chenbo1/canvas.git
后端接口swaggger自动生成,starTopo为星形拓扑,treeTopo还未完全实现
基于spring boot启动HTTP服务,直接执行Swagger2SpringBoot即可启动jetty服务
前端为了解决跨域问题,基于node.js作为web服务器,node service.js就可以启动web服务

基于力导向算法,Java实现星形拓扑图_第3张图片

关键代码:

//初始化数据,数据封装
public class Topo {
     
    public static List calculateTopo(List lineTempList) {
        //初始化数据
        Set nodes= new HashSet<>();
        Set edges= new HashSet<>();
        for (int i = 0; i < lineTempList.size(); i++) {
            Node node1 = new Node();
            node1.setId(lineTempList.get(i).getUplinkNodeId().toString());
            nodes.add(node1);
            Node node2 = new Node();
            node2.setId(lineTempList.get(i).getNodeId().toString());
            nodes.add(node2);
            Edge edge = new Edge();
            edge.setId1(lineTempList.get(i).getUplinkNodeId().toString());
            edge.setId2(lineTempList.get(i).getNodeId().toString());
            edges.add(edge);
        }
         
        Spring sp = new Spring();
        List lNodes = new ArrayList(nodes);
        List lEdges = new ArrayList(edges);
        //1.set Node(x,y) , Random 随机分布初始节点位置
        //canvas size 1024*768
        double start_x, start_y, initSize = 40.0;
        for (Node node:lNodes) {
            start_x = 0 + 1024 * .5;
            start_y = 0 + 768 * .5;
            node.setX(start_x + initSize * (Math.random() - .5));
            node.setY(start_y + initSize * (Math.random() - .5));
        }
         
         
        List reSetNodes = sp.springLayout(lNodes,lEdges);
        //4.反复2,3步 迭代300次,迭代次数越多,整张图的平衡性越好
        for(int i=0; i<300; i++){
            reSetNodes = sp.springLayout(reSetNodes,lEdges);
        }
         
        return reSetNodes;
    }
}
//递归代码
public class Spring {
    public List springLayout(List nodes,List edges) {
       //2计算每次迭代局部区域内两两节点间的斥力所产生的单位位移(一般为正值)
        int area = 1024 * 768;
        double k = Math.sqrt(area / (double)nodes.size());
        double  diffx, diffy, diff;
         
        Map dispx = new HashMap();
        Map dispy = new HashMap();
             
        int ejectfactor = 6;//斥力,点与点之间的最短距离
        for (int v = 0; v < nodes.size(); v++) {
            dispx.put(nodes.get(v).getId(), 0.0);
            dispy.put(nodes.get(v).getId(), 0.0);
            for (int u = 0; u < nodes.size();  u++) {
                if (u != v) {
                    diffx = nodes.get(v).getX() - nodes.get(u).getX();
                    diffy = nodes.get(v).getY() - nodes.get(u).getY();
                    diff = Math.sqrt(diffx * diffx + diffy * diffy);
                  
                    if (diff < 30)
                        ejectfactor = 5;//斥力,点与点之间的最短距离
                    if (diff > 0 && diff < 250) {
                        String id = nodes.get(v).getId();
                        dispx.put(id,dispx.get(id) + diffx / diff * k * k / diff * ejectfactor);
                        dispy.put(id,dispy.get(id) + diffy / diff * k * k / diff* ejectfactor);
                    }
                }
            }
        }      
        //3. 计算每次迭代每条边的引力对两端节点所产生的单位位移(一般为负值)     
        int condensefactor = 3;//引力,意味着点与点之间的最长距离,否则会被引力牵引拉拢
        Node visnodeS = null, visnodeE = null;
         
        for (int e = 0; e < edges.size(); e++) {
            String eStartID = edges.get(e).getId1();
            String eEndID = edges.get(e).getId2();    
            visnodeS = getNodeById(nodes,eStartID);
            visnodeE = getNodeById(nodes,eEndID);
            diffx = visnodeS.getX() - visnodeE.getX();
            diffy = visnodeS.getY() - visnodeE.getY();
            diff = Math.sqrt(diffx * diffx + diffy * diffy);
            dispx.put(eStartID,dispx.get(eStartID) - diffx * diff / k * condensefactor);
            dispy.put(eStartID,dispy.get(eStartID) - diffy * diff / k* condensefactor);
            dispx.put(eEndID,dispx.get(eEndID) + diffx * diff / k * condensefactor);
            dispy.put(eEndID,dispy.get(eEndID) + diffy * diff / k * condensefactor);
        }
      
        //set x,y
        int maxt = 4 ,maxty = 3;
        for (int v = 0; v < nodes.size(); v++) {
            Node node = nodes.get(v);
            Double dx = dispx.get(node.getId());
            Double dy = dispy.get(node.getId());
             
            int disppx = (int) Math.floor(dx);
            int disppy = (int) Math.floor(dy);
            if (disppx < -maxt)
                disppx = -maxt;
            if (disppx > maxt)
                disppx = maxt;
            if (disppy < -maxty)
                disppy = -maxty;
            if (disppy > maxty)
                disppy = maxty;
             
            node.setX((node.getX()+disppx));
            node.setY((node.getY()+disppy));
        }    
        return nodes;
    }
     
    private Node getNodeById(List nodes,String id){
        for(Node node:nodes){
            if(node.getId().equals(id)){
                return node;
            }
        }
        return null;
    }
}

Question

在实际项目下面存在节点组的概念。
因为节点生成的时候是随机坐标的,所以可能会存在组的框覆盖整个拓扑图的情况。
解决方案为:
1、给组成员节点加一条虚拟的引力线,即edges添加Cn2穷举的组合。
2、孤岛节点默认设置随机坐标范围为
x=500+500Math.random()
y=500+500
Math.random()
ps:画布以0为中心坐标,画布尺寸为20002000,使得孤岛节点出现在右下角占整个图1/16的一个500500的区域内。

基于力导向算法,Java实现星形拓扑图_第4张图片
优化前

优化后,所有组成员会收缩在一个小的区域,效果如下:

基于力导向算法,Java实现星形拓扑图_第5张图片
优化后

相关资料:

力导向算法简单实现
http://zhenghaoju700.blog.163.com/blog/static/13585951820114153548541/
D3 API
https://github.com/d3/d3/blob/master/API.md#forces-d3-force
力导向算法百度百科
http://baike.baidu.com/link?url=L63AZXfHeisnzoAnZWGlCPKF9My182L0BQCS-9orBE2sPM_gJAA1gWUhMzf4Rw-dIhyRuq9q-B8Os685kiMTHE6fVMpjAbxRbaUt4nUsDKloPsrpuQR1PH6lybncXeUpElDcfYUxJBQu52N8qSPp9K
力导向算法的两种实现与性能分析
http://www.ibm.com/developerworks/cn/web/0909_fudl_flashsocial/#major3

你可能感兴趣的:(基于力导向算法,Java实现星形拓扑图)