在javascript / d3中使用强制定向布局的怪异情节

时间:2011-10-31 14:29:33

标签: javascript layout d3.js plot force-layout

这是article on creating beeswarm plots in R

还有R package for beeswarm plot。接下来的两张图片说明了该软件包提供的一些可能性:

enter image description here

enter image description here

但是,我现在正试图使用​​d3.js的强制导向布局。

我的计划是让自定义重力将点拉向垂直线及其正确的y值,并且碰撞检测将点相互隔离。

我有a semi-working prototype

enter image description here

不幸的是,我无法找到解决两个问题的方法 - 我怀疑这个问题确实存在同样的问题:

  1. 我的观点至少有点重叠。

  2. 积分堆积之后,正在进行“洗牌” 布局的中心,作为防撞力和“来了 到中心“部队战斗。

  3. 我希望这些要点可以很快就他们应该居住的地方达成协议,最终不会重叠。

    我正在使用的强制代码(如果你想在这里看到它而不是在bl.ocks.org上):

    force.on("tick", function(e) {
      var q,
        node,
        i = 0,
        n = nodes.length;
    
      var q = d3.geom.quadtree(nodes);
    
      while (++i < n) {
        node = nodes[i];
        q.visit(collide(node));
        xerr = node.x - node.true_x;
        yerr = node.y - node.true_y;
        node.x -= xerr*0.005;
        node.y -= yerr*0.9;
      }
    
      svg.selectAll("circle")
          .attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
    });
    
    function collide(node) {
      var r = node.radius,
        nx1,
        nx2,
        ny1,
        ny2,
        xerr,
        yerr;
    
      nx1 = node.x - r;
      nx2 = node.x + r;
      ny1 = node.y - r;
      ny2 = node.y + r;
    
      return function(quad, x1, y1, x2, y2) {
        if (quad.point && (quad.point !== node)) {
          var x = node.x - quad.point.x,
              y = node.y - quad.point.y,
              l = Math.sqrt(x * x + y * y),
              r = node.radius + quad.point.radius;
          if (l < r) {
            // we're colliding.
            var xnudge, ynudge, nudge_factor;
            nudge_factor = (l - r) / l * .4;
            xnudge = x*nudge_factor;
            ynudge = y*nudge_factor;
            node.x -= xnudge;
            node.y -= ynudge;
            quad.point.x += xnudge;
            quad.point.y += ynudge;
          }
        }
        return x1 > nx2
            || x2 < nx1
            || y1 > ny2
            || y2 < ny1;
      };
    }
    

    这是我第一次涉足强制导向的布局,如果这是非常的话,请道歉......

2 个答案:

答案 0 :(得分:4)

您的结果对我来说非常好。但是,如果你想减少重叠的可能性,可以尝试一些事情。

  1. 在节点之间添加一些填充。特别是,你的圆圈有一个笔划,这个笔划的一半会超出圆的半径。因此,如果您想避免重叠笔划,则在计算r时,通过将两个半径相加,您将需要至少一个填充像素。 (这假设每个圆上有一个像素笔划,每个半径增加0.5个像素。)

  2. 在计算nudge_factor时使用.5而不是.4。这使得重叠分辨率更强,通过将任何重叠的圆圈推得足够分开以使它们不再重叠。如果你使用小于.4的值,解决方案会更稳定一点,但它会收敛得更慢,因为即使在轻推之后圆圈​​仍然会重叠一点。

  3. 每次打勾多次运行碰撞解决方案。您当前正在运行冲突解决方案,然后应用自定义重力(朝向true_x和true_y)。如果每次打勾多次运行碰撞分辨率,则会使碰撞分辨率相对于重力更强。

  4. 此外,如果您只想要静态布局,您还可以考虑让力布局运行固定数量的迭代(或稳定),然后在结束时渲染一次,而不是迭代渲染。这使得布局更快,但如果运行太多迭代,它可能会导致渲染暂时出现打嗝。

答案 1 :(得分:1)

简单实施

这是另一个实现:http://bl.ocks.org/4732279

enter image description here

我最初试图用力布局来实现这一点,但是力布局模拟自然会试图通过沿两个轴推动数据点来达到平衡,这可能会破坏数据的排序(如果这对你很重要的话)这是给我的。)

通过用智能策略替换正态分布的随机抖动,可以改善这种实现。但就我的目的而言,这已经足够了。

  1. 冲突访问者的迭代总数直接影响最终状态中的冲突概率。
  2. 将正态分布的标准偏差提高可以帮助可视化更快地收敛到无碰撞解决方案,但当然最终可能需要更多的垂直空间。
  3. 功能更全面,但更复杂......

    这里有一点冲刷(带轴,缩放等):http://bl.ocks.org/4734864

    enter image description here

    GIF动画:

    enter image description here