Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v5]comboCombined(内外侧均使用antv-dagre)布局不理想 #5980

Closed
liseri opened this issue Jul 4, 2024 · 6 comments
Closed

[v5]comboCombined(内外侧均使用antv-dagre)布局不理想 #5980

liseri opened this issue Jul 4, 2024 · 6 comments

Comments

@liseri
Copy link

liseri commented Jul 4, 2024

Describe the bug / 问题描述

V5

  1. 示例链接:https://g6-next.antv.antgroup.com/zh/examples/layout/dagre#antv-dagre-combo
  2. 问题:comboCombined(内外侧均使用antv-dagre)布局不理想,有重叠;如下图:

image

  1. 述求:1)麻烦帮忙看看,是bug 还是我的用法不对配置不对;2)我是使用的单独布局,麻烦看看是不是这么用的;数据转换用的代码是从g6或layout源码中扒出来后改了改;

4.代码:

import { Graph, AntVDagreLayout, ComboCombinedLayout } from '@antv/g6';
import { Graph as GraphLib } from '@antv/graphlib';

const customLayoutData = {
    "nodes": [
        {
            "id": "node-f6080309-1ef1-4cac-a097-932dbc993f25",
            "data": {
                
            }
        },
        {
            "id": "node-27641e0a-f158-498f-84ca-d8914d3bdd51",
            "data": {
                
            }
        },
        {
            "id": "node-82cbdfa3-b666-4495-baa2-32132053370a",
            "data": {
                
            }
        },
        {
            "id": "node-9c854399-cd39-4dc2-9520-39d603c61ea2",
            "data": {
                
            }
        },
        {
            "id": "node-f5f54df8-0436-41f0-b883-5e86bf6eac52",
            "data": {
                "_isCombo": true,
                
            }
        },
        {
            "id": "node-de645c23-0e7a-4175-bd62-c455a2fe4913",
            "data": {
                "parentId": "node-f5f54df8-0436-41f0-b883-5e86bf6eac52",
                
            }
        },
        {
            "id": "node-da5ee6dc-1fdf-420d-abc5-71bdd5cd96cc",
            "data": {
                "parentId": "node-f5f54df8-0436-41f0-b883-5e86bf6eac52",
                
            }
        },
        {
            "id": "node-79394712-37de-4353-8307-81a4ce63dacd",
            "data": {
                "parentId": "node-f5f54df8-0436-41f0-b883-5e86bf6eac52",
                
            }
        },
        {
            "id": "node-12f351ba-b265-46a9-b0cc-0a773fac5ca8",
            "data": {
                
            }
        },
        {
            "id": "node-2bdbebbc-58c4-47d0-8264-ed343b8506fa",
            "data": {
                
            }
        }
    ],
    "edges": [
        {
            "id": "edge-18c062a5-2677-4b14-92b1-050a49e26f86",
            "source": "node-f6080309-1ef1-4cac-a097-932dbc993f25",
            "target": "node-27641e0a-f158-498f-84ca-d8914d3bdd51",
            "data": {}
        },
        {
            "id": "edge-0ba36fda-c523-4715-868e-da6e6f8ef7c2",
            "source": "node-f6080309-1ef1-4cac-a097-932dbc993f25",
            "target": "node-2bdbebbc-58c4-47d0-8264-ed343b8506fa",
            "data": {}
        },
        {
            "id": "edge-d4e5d739-ff90-4e5d-971d-c334302acc42",
            "source": "node-27641e0a-f158-498f-84ca-d8914d3bdd51",
            "target": "node-82cbdfa3-b666-4495-baa2-32132053370a",
            "data": {}
        },
        {
            "id": "edge-07f91b13-0415-4d3f-8346-6381c827a7a1",
            "source": "node-27641e0a-f158-498f-84ca-d8914d3bdd51",
            "target": "node-9c854399-cd39-4dc2-9520-39d603c61ea2",
            "data": {}
        },
        {
            "id": "edge-7b4ea309-d018-4b37-b46f-9a158b733fd6",
            "source": "node-82cbdfa3-b666-4495-baa2-32132053370a",
            "target": "node-12f351ba-b265-46a9-b0cc-0a773fac5ca8",
            "data": {}
        },
        {
            "id": "edge-4fb7755c-ff4e-44dc-87ea-4770b97b97fc",
            "source": "node-9c854399-cd39-4dc2-9520-39d603c61ea2",
            "target": "node-de645c23-0e7a-4175-bd62-c455a2fe4913",
            "data": {}
        },
        {
            "id": "edge-7b3ef5e2-d6e6-4e90-805a-4e6f7d6f9b12",
            "source": "node-de645c23-0e7a-4175-bd62-c455a2fe4913",
            "target": "node-da5ee6dc-1fdf-420d-abc5-71bdd5cd96cc",
            "data": {}
        },
        {
            "id": "edge-03564922-64c9-4312-b26c-717e28d78eab",
            "source": "node-de645c23-0e7a-4175-bd62-c455a2fe4913",
            "target": "node-79394712-37de-4353-8307-81a4ce63dacd",
            "data": {}
        }
    ]
}

/**
 * 图布局数据转为GraphLibData
 */
function layoutData2GraphLib(layoutData, isTree) {
    const graphLibData = new GraphLib(layoutData);
    if (isTree) {
      graphLibData.attachTreeStructure('combo')
      graphLibData.getAllNodes().forEach((node) => {
            const { parentId } = node.data;
            if (parentId === undefined) return;
            if (graphLibData.hasNode(parentId)) {
              graphLibData.setParent(node.id, parentId, 'combo');
            }
          });
    }
    return graphLibData;
    
  }

/**
 * 将图布局数据转换为 G6 数据
 */
function layoutData2G6Data(layoutData) {
  const { nodes, edges } = layoutData;
  const data = { nodes: [], edges: [], combos: [] };

  nodes.forEach((nodeLike) => {
    const target = nodeLike.data._isCombo ? data.combos : data.nodes;
    const { x, y, z = 0, parentId } = nodeLike.data;
    target?.push({
      id: nodeLike.id,
      style: { x, y, z },
      ...(parentId ? { combo: parentId } : {})
    });
  });

  edges.forEach((edge) => {
    const {
      id,
      source,
      target,
      data: { points = [], controlPoints = points.slice(1, points.length - 1) },
    } = edge;

    data.edges.push({
      id: id,
      source: source,
      target: target,
      style: {
        /**
         * antv-dagre 返回 controlPoints,dagre 返回 points
         * antv-dagre returns controlPoints, dagre returns points
         */
        ...(controlPoints?.length ? { controlPoints: controlPoints.map(parsePoint) } : {}),
      },
    });
  });

  return data;
}

// 使用layout只布局; 布局后的数据使用g6渲染
async function comboCombinedLayoutAndRenderWithG6(layoutData) {
  console.log('//==comboCombined布局,数据使用layoutData -> 单独使用布局 -> 使用G6渲染')

  console.log('布局前的layoutData=', layoutData)
  // 实例化布局
  const layout = new ComboCombinedLayout({
    spacing: 20,
    innerLayout: new AntVDagreLayout({
      // begin: [0, 0],
      // align: 'UR',
      nodeSize: [150, 80],
      ranksep: 20,
      nodesep: 10,
      sortByCombo: true,
    }),
    outerLayout: new AntVDagreLayout({
      // begin: [0, 0],
      // align: 'UR',
      nodeSize: [150, 80],
      ranksep: 20,
      nodesep: 10,
      sortByCombo: true,
    })
  });
  // 将layoutData包到graphLib
  const graphLibData = layoutData2GraphLib(layoutData, true);
  console.log('布局前的 graphLibData=', graphLibData)
  // 执行布局
  await layout.assign(graphLibData);
  console.log('布局后的layoutData=', layoutData)

  // 布局后的数据转换为G6数据
  const afterLayoutG6Data = layoutData2G6Data(layoutData);
  console.log('布局后的g6Data=', afterLayoutG6Data)
  
  // 使用G6渲染
  const graph = new Graph({
    container: 'container',
    autoFit: 'view',
    data: afterLayoutG6Data,
    node: {
      type: 'rect',
      style: {
        size: [150, 80],
        radius: 8,
        labelText: (d) => d.id,
        labelPlacement: 'center',
        // ports: [{ placement: 'top' }, { placement: 'bottom' }],
      },
      palette: {
        field: (d) => d.combo,
      },
    },
    edge: {
      type: 'cubic-vertical',
      style: {
        endArrow: true,
      },
    },
    combo: {
      type: 'rect',
      style: {
        radius: 8,
        labelText: (d) => d.id,
        lineDash: 0,
        collapsedLineDash: [5, 5],
      },
    },
    behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas', 'collapse-expand'],
  });

  graph.render();
}

comboCombinedLayoutAndRenderWithG6(customLayoutData)

Reproduction link / 重现链接

https://g6-next.antv.antgroup.com/zh/examples/layout/dagre#antv-dagre-combo

Steps to Reproduce the Bug or Issue / 重现步骤

  1. 打开示例:https://g6-next.antv.antgroup.com/zh/examples/layout/dagre#antv-dagre-combo
  2. 将上面的代码 粘贴到 javascript中

G6 Version / G6 版本

🆕 5.x

Operating System / 操作系统

Windows

Browser / 浏览器

Chrome

Additional context / 补充说明

No response

@Aarebecca
Copy link
Contributor

combo-combine + dagre 结合使用还没有经过充分实践,可能并不能正常工作

@liseri
Copy link
Author

liseri commented Jul 4, 2024

combo-combine + dagre 结合使用还没有经过充分实践,可能并不能正常工作

你好,那想请教下 我这单独使用布局,用法对么,文档中找了半天也没找到个完整的示例,数据需要先包到graphLib中才能用,而且还有一个特殊处理(从源码中扒出来的),不知道对不对,如下代码:

/**
 * 图布局数据转为GraphLibData
 */
function layoutData2GraphLib(layoutData, isTree) {
    const graphLibData = new GraphLib(layoutData);
   // 下面这段是从g6源码里扒出来,改的; 如果使用AntVDagreLayout不需要,因为AntVDagreLayout中就有这段代码(g6中有,且layout也有);但如果使用ComboCombinedLayout就需要(g6中有,但comboCombinedlayout中没有,单独使用布局时就会出问题)
    if (isTree) {
      graphLibData.attachTreeStructure('combo')
      graphLibData.getAllNodes().forEach((node) => {
            const { parentId } = node.data;
            if (parentId === undefined) return;
            if (graphLibData.hasNode(parentId)) {
              graphLibData.setParent(node.id, parentId, 'combo');
            }
          });
    }
    return graphLibData;
  }

@liseri
Copy link
Author

liseri commented Jul 4, 2024

combo-combine + dagre 结合使用还没有经过充分实践,可能并不能正常工作

你好,那想请教下 我这单独使用布局,用法对么,文档中找了半天也没找到个完整的示例,数据需要先包到graphLib中才能用,而且还有一个特殊处理(从源码中扒出来的),不知道对不对,如下代码:

/**
 * 图布局数据转为GraphLibData
 */
function layoutData2GraphLib(layoutData, isTree) {
    const graphLibData = new GraphLib(layoutData);
   // 下面这段是从g6源码里扒出来,改的; 如果使用AntVDagreLayout不需要,因为AntVDagreLayout中就有这段代码(g6中有,且layout也有);但如果使用ComboCombinedLayout就需要(g6中有,但comboCombinedlayout中没有,单独使用布局时就会出问题)
    if (isTree) {
      graphLibData.attachTreeStructure('combo')
      graphLibData.getAllNodes().forEach((node) => {
            const { parentId } = node.data;
            if (parentId === undefined) return;
            if (graphLibData.hasNode(parentId)) {
              graphLibData.setParent(node.id, parentId, 'combo');
            }
          });
    }
    return graphLibData;
  }

我具体化一下吧,给您个具体的例子,这个例子(代码如下)里如下图这么改一下连接数据,就直接报错了,因此我怀疑就是上述扒的代码扒的有问题; 而不单独布局,直接使用g6进行布局且渲染,就没有错误(不报错,但布局有问题,与本报错无关 已单独提issue #5988)
image
不出错的代码,如上图改一下连接(从4连6改为 4连5)就报错了

import { Graph, AntVDagreLayout, ComboCombinedLayout } from '@antv/g6';
import { Graph as GraphLib } from '@antv/graphlib';

const customLayoutData = {
    "nodes": [
        {
            "id": "node-1",
            "data": {
                
            }
        },
        {
            "id": "node-2",
            "data": {
                
            }
        },
        {
            "id": "node-3",
            "data": {
                
            }
        },
        {
            "id": "node-4",
            "data": {
                
            }
        },
        {
            "id": "node-5",
            "data": {
                "_isCombo": true,
                
            }
        },
        {
            "id": "node-6",
            "data": {
                "parentId": "node-5",
                
            }
        },
        {
            "id": "node-7",
            "data": {
                "parentId": "node-5",
                
            }
        },
        {
            "id": "node-8",
            "data": {
                "parentId": "node-5",
                
            }
        },
        {
            "id": "node-9",
            "data": {
                
            }
        },
        {
            "id": "node-10",
            "data": {
                
            }
        }
    ],
    "edges": [
        {
            "id": "edge-18c062a5-2677-4b14-92b1-050a49e26f86",
            "source": "node-1",
            "target": "node-2",
            "data": {}
        },
        {
            "id": "edge-0ba36fda-c523-4715-868e-da6e6f8ef7c2",
            "source": "node-1",
            "target": "node-10",
            "data": {}
        },
        {
            "id": "edge-d4e5d739-ff90-4e5d-971d-c334302acc42",
            "source": "node-2",
            "target": "node-3",
            "data": {}
        },
        {
            "id": "edge-07f91b13-0415-4d3f-8346-6381c827a7a1",
            "source": "node-2",
            "target": "node-4",
            "data": {}
        },
        {
            "id": "edge-7b4ea309-d018-4b37-b46f-9a158b733fd6",
            "source": "node-3",
            "target": "node-9",
            "data": {}
        },
        {
            "id": "edge-4fb7755c-ff4e-44dc-87ea-4770b97b97fc",
            "source": "node-4",
            "target": "node-6",
            "data": {}
        },
        {
            "id": "edge-7b3ef5e2-d6e6-4e90-805a-4e6f7d6f9b12",
            "source": "node-6",
            "target": "node-7",
            "data": {}
        },
        {
            "id": "edge-03564922-64c9-4312-b26c-717e28d78eab",
            "source": "node-6",
            "target": "node-8",
            "data": {}
        }
    ]
}

/**
 * 图布局数据转为GraphLibData
 */
function layoutData2GraphLib(layoutData, isTree) {
    const graphLibData = new GraphLib(layoutData);
    if (isTree) {
      graphLibData.attachTreeStructure('combo')
      graphLibData.getAllNodes().forEach((node) => {
            const { parentId } = node.data;
            if (parentId === undefined) return;
            if (graphLibData.hasNode(parentId)) {
              graphLibData.setParent(node.id, parentId, 'combo');
            }
          });
    }
    return graphLibData;
    
  }

/**
 * 将图布局数据转换为 G6 数据
 */
function layoutData2G6Data(layoutData) {
  const { nodes, edges } = layoutData;
  const data = { nodes: [], edges: [], combos: [] };

  nodes.forEach((nodeLike) => {
    const target = nodeLike.data._isCombo ? data.combos : data.nodes;
    const { x, y, z = 0, parentId } = nodeLike.data;
    target?.push({
      id: nodeLike.id,
      style: { x, y, z },
      ...(parentId ? { combo: parentId } : {})
    });
  });

  edges.forEach((edge) => {
    const {
      id,
      source,
      target,
      data: { points = [], controlPoints = points.slice(1, points.length - 1) },
    } = edge;

    data.edges.push({
      id: id,
      source: source,
      target: target,
      style: {
        /**
         * antv-dagre 返回 controlPoints,dagre 返回 points
         * antv-dagre returns controlPoints, dagre returns points
         */
        ...(controlPoints?.length ? { controlPoints: controlPoints.map(parsePoint) } : {}),
      },
    });
  });

  return data;
}

// 使用layout只布局; 布局后的数据使用g6渲染
async function comboCombinedLayoutAndRenderWithG6(layoutData) {
  console.log('//==comboCombined布局,数据使用layoutData -> 单独使用布局 -> 使用G6渲染')

  console.log('布局前的layoutData=', layoutData)
  // 实例化布局
  const layout = new AntVDagreLayout({
    // begin: [0, 0],
    // align: 'UR',
    nodeSize: [150, 80],
    ranksep: 10,
    nodesep: 10,
    sortByCombo: true,
  });
  // 将layoutData包到graphLib
  const graphLibData = layoutData2GraphLib(layoutData, true);
  console.log('布局前的 graphLibData=', graphLibData)
  // 执行布局
  await layout.assign(graphLibData);
  console.log('布局后的layoutData=', layoutData)

  // 布局后的数据转换为G6数据
  const afterLayoutG6Data = layoutData2G6Data(layoutData);
  console.log('布局后的g6Data=', afterLayoutG6Data)
  
  // 使用G6渲染
  const graph = new Graph({
    container: 'container',
    autoFit: 'view',
    data: afterLayoutG6Data,
    node: {
      type: 'rect',
      style: {
        size: [150, 80],
        radius: 8,
        labelText: (d) => d.id,
        labelPlacement: 'center',
        // ports: [{ placement: 'top' }, { placement: 'bottom' }],
      },
      palette: {
        field: (d) => d.combo,
      },
    },
    edge: {
      type: 'cubic-vertical',
      style: {
        endArrow: true,
      },
    },
    combo: {
      type: 'rect',
      style: {
        radius: 8,
        labelText: (d) => d.id,
        lineDash: 0,
        collapsedLineDash: [5, 5],
      },
    },
    behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas', 'collapse-expand'],
  });

  graph.render();
}

comboCombinedLayoutAndRenderWithG6(customLayoutData)

@Aarebecca
Copy link
Contributor

node-5 是一个 id,它应该位于 data.combos 中,通过 _isCombo 标识是底层库的隐式逻辑,G6 并不基于该字段

@liseri
Copy link
Author

liseri commented Jul 5, 2024

node-5 是一个 id,它应该位于 data.combos 中,通过 _isCombo 标识是底层库的隐式逻辑,G6 并不基于该字段

您是说 AntVDagreLayout.execute(data), 这里的data应该是 {nodes:[], combos:[], edges:[]} 这样的数据结构么? 哪里能找到一个示例么,这样就不用一遍遍麻烦您了;

@Aarebecca
Copy link
Contributor

Aarebecca commented Jul 31, 2024

node-5 是一个 id,它应该位于 data.combos 中,通过 _isCombo 标识是底层库的隐式逻辑,G6 并不基于该字段

您是说 AntVDagreLayout.execute(data), 这里的data应该是 {nodes:[], combos:[], edges:[]} 这样的数据结构么? 哪里能找到一个示例么,这样就不用一遍遍麻烦您了;

是的,你只需要把 combo 数据放到 data.combos 下即可,与 nodes\edges 是一样的,可以参考下面示例中的数据:

https://g6-next.antv.antgroup.com/examples/element/combo/#circle

@liseri liseri closed this as completed Aug 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants