【问题标题】:D3 force simulation graph, edges fail to render for larger datasetsD3 力模拟图,边缘无法渲染更大的数据集
【发布时间】:2019-08-27 11:00:53
【问题描述】:

编辑:对于那些对大型数据集的效率感兴趣的人,接受的答案很有用,无论如何我都需要实现它。但是在 cmets 中指出的问题的更直接原因是数据库没有为大型查询发送所有边缘。所以这是一个 X Y 问题。

我正在使用 d3.forceSimulation 在浏览器中创建网络图。该图对少量边按预期工作(参见图 1。)对于大量边(大约 > 500),大多数边开始无法渲染(参见图 2。)这当然是不受欢迎的行为.

到目前为止,我已经尝试增加画布的大小,并将更新调整为仅在每 20 次滴答时运行。这些更改中的任何一项都没有改善边缘渲染。

如果需要,我愿意牺牲性能(例如,降低帧率)。重要的是我能够在图表上显示至少 1000 个节点。我不知道我可以更改哪些参数来实现这一点,因为我不确定到底是什么导致了问题。

模拟设置代码复制到下面。我还在下面包含了 drawEdge 函数,因为我使用了一个非常手动的过程来使图形定向(绘制三角形),特别是 arctan 函数在过去给我带来了问题。所以也许那里有问题。 4 谢谢。

模拟设置:

   simulation = d3.forceSimulation()
        .force("x", d3.forceX(canvasWidth/2))
        .force("y", d3.forceY(canvasHeight/2))
        .force("collide", d3.forceCollide(nodeRadius+1))
        .force("charge", d3.forceManyBody()
                .strength(-90))
        .force("link", d3.forceLink()
                .id(function (d) { return d.id; }))
        .on("tick", queue_update);

    simulation.nodes(graph.nodes);
    simulation.force("link")
        .links(graph.edges);

drawLink 函数:

function drawLink(l) {
    //Setup for line
    ctx.beginPath();
    ctx.strokeStyle=colors[l.source.court];

    //Draw a line between the nodes
    ctx.moveTo(l.source.x, l.source.y);
    ctx.lineTo(l.target.x, l.target.y);
    ctx.stroke();

    ctx.beginPath();
    ctx.strokeStyle = "#000";

    //Setup for arrow
    var line_angle = Math.atan2(l.source.y - l.target.y, l.source.x - l.target.x);
    var x_move = Math.cos(line_angle);
    var y_move = Math.sin(line_angle);
    var on_line_x = l.target.x + x_move*11;
    var on_line_y = l.target.y + y_move*11;

    var on_line_x_2 = l.target.x + x_move*6;
    var on_line_y_2 = l.target.y + y_move*6;


    ctx.moveTo(on_line_x, on_line_y);
    ctx.lineTo(on_line_x - y_move, on_line_y + x_move);
    ctx.lineTo(on_line_x_2, on_line_y_2);
    ctx.lineTo(on_line_x + y_move, on_line_y - x_move);
    ctx.lineTo(on_line_x, on_line_y);
    ctx.stroke();
}

编辑:可以在此处找到最小示例:https://drive.google.com/file/d/19efUYOaB6D04jVg4FfjxEQbw8Mcwa1pm/view?usp=sharing

【问题讨论】:

  • 这里可能不仅仅是渲染问题 - 尝试评论 chargecollide 函数,看看是否有改进。可能需要实现更高性能的自定义布局功能
  • 好的,谢谢,我现在就试试。
  • 尝试设置minimal reproducible example 来演示该问题。把它剥离到最低限度,去掉所有花哨的颜色、不同的半径、标记、标签……让它足以重现你的问题。这会让其他人更容易帮助你。
  • @CornelStefanache 注释掉充电和碰撞似乎确实解决了边缘问题。您对如何实现更高性能的自定义布局功能有任何指示吗?
  • @Neil 这个修改版的演示将所有未连接的节点涂成红色,这清楚地表明了您的问题:plnkr.co/edit/AKo6NVqMAyPuEaLUlUoc?p=preview

标签: javascript d3.js graph


【解决方案1】:

有一种方法可以优化渲染功能(节点和链接):仅当它们在视口中可见时才渲染,并让 D3 在所有节点上进行充电/碰撞计算

首先,您必须定义视口和代表矩形的宽度/高度:

const WIDTH = 600;
const HEIGHT = 600;
const viewport = {left: 0, top: 0, right: WIDTH, bottom: HEIGHT}

检查链接是否穿过视口的最基本的事情是检查源和目标定义的矩形是否与视口相交:

function intersectRect(r1, r2) {
  return !(r2.left > r1.right || 
           r2.right < r1.left || 
           r2.top > r1.bottom ||
           r2.bottom < r1.top);
}

function drawLink(l) {

    const lineRect = {
      left: Math.min(l.source.x, l.target.x),
      right: Math.max(l.source.x, l.target.x),
      top: Math.min(l.source.y, l.target.y),
      bottom: Math.max(l.source.y, l.target.y),
    }

    // draw only if they intersect         
    if (intersectRect(lineRect, viewport)) {

      //Setup for line
      ctx.beginPath();
      ctx.strokeStyle = "#000";

      //Draw a line between the nodes
      ctx.moveTo(l.source.x, l.source.y);
      ctx.lineTo(l.target.x, l.target.y);
      ctx.stroke();

      ctx.beginPath();
      ctx.strokeStyle = "#000";

      //Setup for arrow
      var line_angle = Math.atan2(l.source.y - l.target.y, l.source.x - l.target.x);
      var x_move = Math.cos(line_angle);
      var y_move = Math.sin(line_angle);
      var on_line_x = l.target.x + x_move*11;
      var on_line_y = l.target.y + y_move*11;

      var on_line_x_2 = l.target.x + x_move*6;
      var on_line_y_2 = l.target.y + y_move*6;

      ctx.moveTo(on_line_x, on_line_y);
      ctx.lineTo(on_line_x - y_move, on_line_y + x_move);
      ctx.lineTo(on_line_x_2, on_line_y_2);
      ctx.lineTo(on_line_x + y_move, on_line_y - x_move);
      ctx.lineTo(on_line_x, on_line_y);
      ctx.stroke();
   }
}

渲染时可以对节点做同样的事情

function drawNode(d) {
    if (d.x > 0 && d.x< WIDTH && d.y> 0 && d.y< HEIGHT){ 
       ctx.beginPath();
       ctx.fillStyle = "#666";
       fill_node(d)
    }
}

function fill_node(d) {
    if (d.x > 0 && d.x < WIDTH && d.y > 0 && d.y < HEIGHT){ 
      ctx.moveTo(d.x, d.y);
      ctx.arc(d.x, d.y, nodeRadius, 0, 2*Math.PI);
      ctx.fill();
    }
}

【讨论】:

    猜你喜欢
    • 2021-07-28
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 2022-10-04
    • 2014-02-27
    • 2019-12-08
    • 1970-01-01
    • 2014-01-21
    相关资源
    最近更新 更多