15

在看到 dagre-d3 相当复杂的TCP 状态图示例后,我认为它能够解析类似复杂度的图。在下图中,显然不是这种情况。如果两个标记的节点被交换,所有的交叉都将被解决。 使用 dagre 和 d3.js 创建的图表

另一个有趣的事情是,解决图形的好坏似乎取决于我设置边的顺序。

以下代码

g.setEdge("148570019_1100", "148570020_1100", { label: "" });
g.setEdge("148570019_1100", "148570021_1100", { label: "" });
g.setEdge("148570019_1100", "148570010_1100", { label: "" });

没有给出与此相同的结果:

g.setEdge("148570019_1100", "148570010_1100", { label: "" });
g.setEdge("148570019_1100", "148570020_1100", { label: "" });
g.setEdge("148570019_1100", "148570021_1100", { label: "" });

看这两个例子:

正如建议的那样,我尝试使用cola.js,这就是同一张图的样子: 使用 cola.js 和 d3.js 创建的图表

如您所见,colajs 在减少交叉点方面做得更好,但布局不像 dagre 那样结构化和清晰。我对 colajs 使用以下设置:

cola.d3adaptor()
      .avoidOverlaps(true)
      .convergenceThreshold(1e-3)
      .symmetricDiffLinkLengths(20)
      .flowLayout('x', 150)
      .size([width, height])
      .nodes(nodes)
      .links(edges)
      .constraints(constraints)
      .jaccardLinkLengths(300);

是否可以配置 colajs 使其看起来更有条理,类似于 dagre?dagre 是否根本无法解决这样的问题,或者是否有可能以某种方式对其进行配置?

4

1 回答 1

9

这是解决问题的一种方法:http: //jsfiddle.net/5u9mzfse/

或多或少,我只是对确定最佳渲染的实际问题感兴趣,如何通过算法实现这一点。

这个想法是利用渲染节点的顺序很重要的事实,因此您可以打乱顺序并找到产生最佳结果的顺序。最简单的方法是测试边缘形成的线条的边界框是否发生碰撞。在这里,我假设边缘开始和结束对于边界框来说是足够好的估计。

边缘应首先保存到列表中

var edgeList = [["10007154_1100","148570017_1100",{"label":""}, ...]

然后

  1. 随机播放列表
  2. 渲染节点
  3. 从输出中计算边缘的边界框
  4. 计算有多少边界框重叠
  5. 如果碰撞计数为零,则渲染输出,否则继续直到运行max_cnt迭代并选择迄今为止最好的

渲染输出中边缘的边界框可以通过以下方式找到:

  var nn = svg.select(".edgePaths");
  var paths = nn[0][0];
  var fc = paths.firstChild;
  var boxes = [];
  while(fc) {
     var path = fc.firstChild.getAttribute("d");
     var coords = path.split(/,|L/).map(function(c) {
         var n = c;
         if((c[0]=="M" || c[0]=="L")) n = c.substring(1);
         return parseFloat(n);
     })
     boxes.push({ left : coords[0], top : coords[1], 
            right : coords[coords.length-2], 
            bottom : coords[coords.length-1]});
     fc = fc.nextSibling;
  }

您只需计算盒子是否发生碰撞,我认为这样的结果会给出大致正确的结果:

  var collisionCnt = 0;
  boxes.forEach( function(a) {
         // --> test for collisions against other nodes...
         boxes.forEach(function(b) {
             if(a==b) return;
             // test if outside
             if ( (a.right  < b.left) || 
                  (a.left   > b.right) || 
                  (a.top    > b.bottom) || 
                  (a.bottom < b.top) ) {

                  // test if inside
                  if(a.left >= b.left  && a.left <=b.right 
                  || a.right >= b.left  && a.right <=b.right) {
                     if(a.top <= b.top && a.top >= b.bottom) {
                        collisionCnt++;
                     }
                     if(a.bottom <= b.top && a.bottom >= b.bottom) {
                        collisionCnt++;
                     }                 
                  }
             } else {
                collisionCnt++;
             }
         })
      })

然后你知道有多少条边与这组边相交。

然后在每一轮之后检查这是否是我们迄今为止最好的数组,或者是否没有冲突立即退出;

if(collisionCnt==0) {
     optimalArray = list.slice();
     console.log("Iteration cnt ", iter_cnt);
     break; 
 } 
 if(typeof(best_result) == "undefined") {
    best_result = collisionCnt;
 } else {
    if(collisionCnt < best_result) {
        optimalArray = list.slice();
        best_result = collisionCnt;
    }
 }

在至少使用简单图进行测试期间,该算法需要 1-5 轮来计算边的最佳顺序,因此看起来至少在图不太大的情况下它可能工作得很好。

于 2016-03-20T11:32:53.410 回答