• vis.js绘图库的一个BUG以及源码修正


    1. BUG

    1.1 BUG触发情况

      在使用vis.js绘图时,加入两个节点A和B之间既存在一条从A指向B的边,同时也存在一条从B指向A的边,那么这个绘图库就会崩溃。

    1.2 BUG解析

      vis.js是动态计算两个节点之间位置的,然后将其在页面上展示出来,而在上述介绍的情况下,在计算A和B之间的距离时,代码中会出现除0的BUG,导致程序崩溃。

    2. 解决方案

      源码有3万多行,为了找这个BUG花了一周的时间,最终确定了出问题的函数。

      在vis.js源码第12258行(版本更新后位置可能不同),有个_calculateNodeForces()函数,源码如下:

      

    /**
       * Calculate the forces the nodes apply on eachother based on a repulsion field.
       * This field is linearly approximated.
       *
       * @private
       */
      _calculateNodeForces: function () {
        var dx, dy, angle, distance, fx, fy, combinedClusterSize,
          repulsingForce, node1, node2, i, j;
    
        var nodes = this.calculationNodes;
        var nodeIndices = this.calculationNodeIndices;
    
        // approximation constants
        var a_base = -2 / 3;
        var b = 4 / 3;
    
        // repulsing forces between nodes
        var nodeDistance = this.constants.physics.repulsion.nodeDistance;
        var minimumDistance = nodeDistance;
    
        // we loop from i over all but the last entree in the array
        // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j
        for (i = 0; i < nodeIndices.length - 1; i++) {
          node1 = nodes[nodeIndices[i]];
          for (j = i + 1; j < nodeIndices.length; j++) {
            node2 = nodes[nodeIndices[j]];
            combinedClusterSize = node1.clusterSize + node2.clusterSize - 2;
    
            dx = node2.x - node1.x;
            dy = node2.y - node1.y;
            distance = Math.sqrt(dx * dx + dy * dy);
    
            minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification));
            var a = a_base / minimumDistance;
            if (distance < 2 * minimumDistance) {
              if (distance < 0.5 * minimumDistance) {
                repulsingForce = 1.0;
              }
              else {
                repulsingForce = a * distance + b; // linear approx of  1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness))
              }
    
              // amplify the repulsion for clusters.
              repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification;
              repulsingForce = repulsingForce / distance;
    
              fx = dx * repulsingForce;
              fy = dy * repulsingForce;
    
              node1.fx -= fx;
              node1.fy -= fy;
              node2.fx += fx;
              node2.fy += fy;
            }
          }
        }
      }
    };

      这里面有个distance变量,已标红,就是这个变量在我们介绍的情况下会为0,然后它又作为一个被除数,所以程序崩溃。

      修改后的代码如下:

      

    /**
       * Calculate the forces the nodes apply on eachother based on a repulsion field.
       * This field is linearly approximated.
       *
       * @private
       */
      _calculateNodeForces: function () {
        var dx, dy, angle, distance, fx, fy, combinedClusterSize,
          repulsingForce, node1, node2, i, j;
    
        var nodes = this.calculationNodes;
        var nodeIndices = this.calculationNodeIndices;
    
        // approximation constants
        var a_base = -2 / 3;
        var b = 4 / 3;
    
        // repulsing forces between nodes
        var nodeDistance = this.constants.physics.repulsion.nodeDistance;
        var minimumDistance = nodeDistance;
    
        // we loop from i over all but the last entree in the array
        // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j
        for (i = 0; i < nodeIndices.length - 1; i++) {
          node1 = nodes[nodeIndices[i]];
          for (j = i + 1; j < nodeIndices.length; j++) {
            node2 = nodes[nodeIndices[j]];
            combinedClusterSize = node1.clusterSize + node2.clusterSize - 2;
    
            dx = node2.x - node1.x;
            dy = node2.y - node1.y;
            distance = Math.sqrt(dx * dx + dy * dy);
            if(distance == 0){
              distance = 0.001;
            }
    
            minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification));
            var a = a_base / minimumDistance;
            if (distance < 2 * minimumDistance) {
              if (distance < 0.5 * minimumDistance) {
                repulsingForce = 1.0;
              }
              else {
                repulsingForce = a * distance + b; // linear approx of  1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness))
              }
    
              // amplify the repulsion for clusters.
              repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification;
              repulsingForce = repulsingForce / distance;
    
              fx = dx * repulsingForce;
              fy = dy * repulsingForce;
    
              node1.fx -= fx;
              node1.fy -= fy;
              node2.fx += fx;
              node2.fy += fy;
            }
          }
        }
      }
    };

      加上一个判断让它不要为0即可。

  • 相关阅读:
    php 高级 提高PHP代码的性能10条建议
    CSRF预防手段
    如何在PHP中防止SQL注入
    网络安全 如何防范 XSS 攻击
    php 算法知识 冒泡排序
    php 基础知识 常见面试题
    MySQL高级 InnoDB 和 MyISAM 的区别
    php 算法知识 猴子选大王
    MySQL高级-索引1
    [POI2007]POW-The Flood(并查集)
  • 原文地址:https://www.cnblogs.com/14061216chen/p/8338128.html
Copyright © 2020-2023  润新知