Javascript 使用dagre-d3或colajs的d3js流程图

Javascript 使用dagre-d3或colajs的d3js流程图,javascript,d3.js,dagre-d3,dagre,Javascript,D3.js,Dagre D3,Dagre,在看到dagre-d3相当复杂的部分后,我认为它能够解析类似复杂度的图表。 在下图中,情况显然不是这样。如果交换了两个标记的节点,则所有交叉都将得到解决。 另一件有趣的事情是,图形的求解效果似乎取决于我设置边的顺序 下面的代码 g.setEdge("148570019_1100", "148570020_1100", { label: "" }); g.setEdge("148570019_1100", "148570021_1100", { label: "" }); g.setEdge("

在看到dagre-d3相当复杂的部分后,我认为它能够解析类似复杂度的图表。 在下图中,情况显然不是这样。如果交换了两个标记的节点,则所有交叉都将得到解决。

另一件有趣的事情是,图形的求解效果似乎取决于我设置边的顺序

下面的代码

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: "" });
请参见以下两个示例:

正如建议的那样,我尝试改用,这是同一个图的样子:

正如您所看到的,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);

是否可以以类似于dagre的方式配置colajs,使其看起来更结构化?dagre是不能解决这样的问题,还是有可能按照现有的方式配置它?

这里有一个解决方案:

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

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

应首先将边保存到列表中

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

  • 洗牌
  • 渲染节点
  • 根据输出计算边的边界框
  • 计算有多少边界框重叠
  • 如果碰撞计数为零,则渲染输出,否则继续,直到运行了max\u 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++;
                 }
             })
          })
    
    var-collisionCnt=0;
    框。forEach(函数(a){
    //-->测试与其他节点的冲突。。。
    框。forEach(功能(b){
    如果(a==b)返回;
    //测试是否在室外
    如果((a.右右)|
    (a.顶部>b.底部)|
    (a.底部如果(a.left>=b.left&&a.left=b.left&&a.right)你能举例说明(也许用一张图片),理想的渲染是什么?@adarren抱歉,我在文本中写了它,但后来忘记编辑图片:如果两个标记的节点被交换,所有交叉点都会被解决。感谢更新您的图像。我认为dagre应该能够处理您的场景,并且认为它可能与您的数据有关。您能够创建jsbin/jsfid吗dle/etc?听起来更适合您的需要。@Larskothoff,但dagre没有宣传类似的功能吗?cola.js更适合一般布局,还是只是更适合减少边缘交叉?谢谢您的努力!dagre不能自己做这件事真是太遗憾了。是的,如果您能仔细阅读源代码的话这里可能是一个在渲染阶段之前添加此类检查并为其创建选项的地方。当返回到该答案并意识到如果边界框(左,上)-(右,下)存在问题时坐标的定义应确保左>右或底部<顶部,以便检查
    左<右
    顶部<底部
    并在必要时交换值。至少应再次检查算法的该部分。
    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;
        }
     }