在 cytoscape.js 中使用网格布局时复合节点重叠

Compound nodes overlap while using grid layout in cytoscape.js

我正在为我的可视化项目使用 cytoscape.js,我必须在其中显示具有复合节点的层次结构。

所以我最初使用了 Cose-Bilkent 布局,它非常有效,但要求父节点的所有子节点必须在一行中。所以我试着稍微调整一下,但无法得到准确的结果。

然后我尝试通过提供 硬编码行号和列号 来使用网格布局,我得到了准确的结果,但由于我的数据是动态的,我意识到很难分配行号和我自己的列号。

这是我使用的数据,

elements: [ // list of graph elements to start with
            { // node a
                data: { id: 'X1', label: 'X1'}
            },
            {
                data: { id: 'X2', label: 'X2'}
            },
            {
                data: { id: 'X3', label: 'X3'}
            },
            {
                data: { id: 'X4', label: 'X4'}
            },
            {
                data: { id: 'X5', label: 'X5'}
            },
            {
                data: { id: 'X6', label: 'X6'}
            },
            {
                data: { id: 'X7', label: 'X7'}
            },
            {
                data: { id: 'X8', label: 'X8'}
            },
            {
                data: { id: 'X9', label: 'X9'}
            },
            {
                  data: { id: 'X10', label: 'X10'}
            },
            {
                data: { id: 'X1e1',label: 'e1', parent: 'X1', row: '1' ,col: '1'}
            },
            {
                data: { id: 'X1e5',label: 'e5', parent: 'X1', row: '1',col: '2'}
            },
            {
                data: { id: 'X1e6',label: 'e6', parent: 'X1', row: '1',col: '3'}
            },
            {
                data: { id: 'X2e2',label: 'e2', parent: 'X2', row: '3',col: '1'}
            },
            {
                data: { id: 'X2e3',label: 'e3', parent: 'X2', row: '3',col: '2'}
             },
            {
                data: { id: 'X3e4',label: 'e4', parent: 'X3', row: '4',col: '1'}
            },
            {
                data: { id: 'X4e5',label: 'e5', parent: 'X4', row: '2',col: '1'}
            },
            {
                data: { id: 'X4e6',label: 'e6', parent: 'X4', row: '2',col: '2'}
            },
            {
                data: { id: 'X5e7',label: 'e7', parent: 'X5', row: '7',col: '1'}
            },
            {
                data: { id: 'X6e8',label: 'e8', parent: 'X6', row: '5',col: '1'}
            },
            {
                data: { id: 'X6e9',label: 'e9', parent: 'X6', row: '5',col: '2'}
            },
            {
                data: { id: 'X7e10',label: 'e10', parent: 'X7', row: '7',col: '2'}
            },
            {
                data: { id: 'X7e11',label: 'e11', parent: 'X7', row: '7',col: '3'}
            },
            {
                data: { id: 'X7e12',label: 'e12', parent: 'X7', row: '7',col: '4'}
            },
            {
                data: { id: 'X8e13',label: 'e13', parent: 'X8', row: '6',col: '1'}
            },
            {
                data: { id: 'X8e14',label: 'e14', parent: 'X8', row: '6',col: '2'}
            },
            {
                data: { id: 'X8e15',label: 'e15', parent: 'X8', row: '6',col: '3'}
            },
            {
                data: { id: 'X8e16',label: 'e16', parent: 'X8', row: '6',col: '4'}
            },
            {
                data: { id: 'X9e17',label: 'e17', parent: 'X9', row: '8',col: '1'}
            },
            {
                data: { id: 'X10e18',label: 'e18', parent: 'X10', row: '8',col: '2'}
            },
            {
                data: { id: 'X1e5X4e5', source:'X1e5', target:'X4e5'}
            },
            {
                data: { id: 'X1e6X4e6', source:'X1e6', target:'X4e6'}
            },
            {
                data: { id: 'X1e1X2', source:'X1e1', target:'X2'}
            },
            {
                data: { id: 'X2e3X3', source:'X2e3', target:'X3'}
            },
            {
                data: { id: 'X4e5X5', source:'X4e5', target:'X5'}
            },
            {
                data: { id: 'X4e6X6', source:'X4e6', target:'X6'}
            },
            {
                data: { id: 'X6X8e16', source:'X6', target:'X8e16'}
            },
            {
                data: { id: 'X6e9X8', source:'X6e9', target:'X8'}
            },
            {
                data: { id: 'X6e8X7', source:'X6e8', target:'X7'}
            },
            {
                data: { id: 'X6X7e12', source:'X6', target:'X7e12'}
            }
        ]

和布局

layout:{
            name: 'grid',
            fit: true,
            position: function( node ){ return {row:node.data('row'), col:node.data('col') }} 
        }

这是我通过设置手动行和列得到的(也是预期的)结果

如有任何帮助,我们将不胜感激。谢谢

嗯,有两个扩展,可以满足您的需求:

巧合的是,两者都来自同一个人,所以这应该不是问题,您所要做的就是为应用程序应用正确的样式,使其看起来像您的示例:

document.addEventListener("DOMContentLoaded", function() {
  var cy = (window.cy = cytoscape({
    container: document.getElementById("cy"),
    layout: {
      name: "evenParent"
    },
    style: [{
        selector: "node",
        style: {
          "content": "data(id)",
          "background-color": "#ad1a66"
        }
      },
      {
        selector: ":parent",
        style: {
          "background-opacity": 0.333
        }
      },
      {
        selector: "edge",
        style: {
          width: 3,
          "line-color": "#ad1a66"
        }
      },
      {
        selector: "edge.meta",
        style: {
          width: 2,
          "line-color": "red"
        }
      },
      {
        selector: ":selected",
        style: {
          "border-width": 3,
          "border-color": "#DAA520"
        }
      }
    ],
    elements: {
      nodes: [{
          data: {
            id: "Jerry",
            name: "Jerry"
          }
        },
        {
          data: {
            id: "Elaine",
            name: "Elaine"
          }
        },
        {
          data: {
            id: "Kramer",
            name: "Kramer"
          }
        },
        {
          data: {
            id: "George",
            name: "George"
          }
        },
        {
          data: {
            id: "Martin",
            name: "Martin"
          }
        },
        {
          data: {
            id: "Philippe",
            name: "Philippe"
          }
        },
        {
          data: {
            id: "Louis",
            name: "Louis"
          }
        },
        {
          data: {
            id: "Genevieve",
            name: "Genevieve"
          }
        },
        {
          data: {
            id: "Leo",
            name: "Leo"
          }
        },
        {
          data: {
            id: "Larry",
            name: "Larry"
          }
        },
        {
          data: {
            id: "Logaina",
            name: "Logaina"
          }
        }
      ],
      edges: [{
          data: {
            source: "Jerry",
            target: "Elaine"
          }
        },
        {
          data: {
            source: "Jerry",
            target: "Kramer"
          }
        },
        {
          data: {
            source: "Jerry",
            target: "George"
          }
        },
        {
          data: {
            source: "Elaine",
            target: "Martin"
          }
        },
        {
          data: {
            source: "Elaine",
            target: "Philippe"
          }
        },
        {
          data: {
            source: "Elaine",
            target: "Louis"
          }
        },
        {
          data: {
            source: "Elaine",
            target: "Genevieve"
          }
        },
        {
          data: {
            source: "Elaine",
            target: "Leo"
          }
        },
        {
          data: {
            source: "Kramer",
            target: "Larry"
          }
        },
        {
          data: {
            source: "Kramer",
            target: "Logaina"
          }
        }
      ]
    }
  }));

  // demo your collection ext
  cy.nodes().noOverlap({
    padding: 5
  });
});
body {
  font: 14px helvetica neue, helvetica, arial, sans-serif;
}

#cy {
  height: 100%;
  width: 100%;
  position: absolute;
  left: 0;
  top: 0;
}
<html>

<head>
  <script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/cytoscape-even-parent@1.1.1/cytoscape-even-parent.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/cytoscape-no-overlap@1.0.1/cytoscape-no-overlap.min.js"></script>
</head>

<body>
  <div id="cy"></div>
</body>

</html>

以下代码对我有用,

如果这不起作用,请考虑@Stephen 的代码。

var cy = cytoscape({
    container: /* your div within which you want to render */ ,
    elements: [ /*list of graph elements to start with */ ] ,
    style: [ /* the stylesheet for the graph */ ] ,
    layout:{
                name: 'cola',
                fit: false,
                infinite: false,
                avoidOverlap: true
            }
});

//Used to make child nodes stay on the same row
cy.ready(function(){
                setTimeout(function(){
                    cy.zoom(0.5);
                    cy.nodes(':compound').forEach(function(comp,j,comps){
                        var nodePosition={};
                        if(comp.descendants().length>1)
                        {
                            var minX;
                            var maxY;
                            comp.descendants().forEach(function(ele,i,eles){
                                if(i==0)
                                {
                                    minX=ele.renderedPosition('x');
                                    maxY=ele.renderedPosition('y');
                                }
                                else
                                {
                                    var tempX=ele.renderedPosition('x');
                                    var tempY=ele.renderedPosition('y');
                                    if(tempX<minX)
                                    {
                                        minX=tempX;
                                    }
                                    if(tempY>maxY)
                                    {
                                        maxY=tempY;
                                    }
                                }
                            });
                            comp.descendants().forEach(function(ele,i,eles){
                                ele.renderedPosition({x:minX,y:maxY});
                                minX=minX+60;
                            });
                        }
                        cy.resize();
                        cy.fit();
                        cy.minZoom(cy.zoom());
                    });
                },1000);
            });

P.S:不要忘记包含 cytoscape 脚本;)