D3.js用力沿圆分布方格

时间:2018-11-28 09:08:26

标签: javascript d3.js d3-force-directed

我想用力()和碰撞沿着带有一个圆的内容散布具有内容的正方形,所以这些正方形和主圆不会重叠。 有什么想法怎么做?我应该使用链接来做到这一点吗? 这是一个小提琴http://jsfiddle.net/benderlio/usbq839m/3/ 有时它可以很好地扩展正方形,但是大多数情况下,正方形会像img上那样移动。

enter image description here

    var force = d3.forceSimulation(nodes)
      .force("charge", d3.forceManyBody(1130))
     // .force('link', d3.forceLink().links(links))
      .on("tick", function () {

        var k = this.alpha(),
          kg = k * .02,
          spaceAround = 0.04;

        //console.log('', nodes);

        nodes.forEach(function (a, i) {
                       ...
        });

        svg.selectAll("rect")
          .style("fill", function (d) {
            return "#ccc"
          })
          .attr("x", function (d) {
            return d.x - d.width / 2;
          })
          .attr("y", function (d) {
            return d.y - d.height / 2;
          });

        svg.selectAll("line")
          .attr("x2", function (d) {
            return d.x;
          })
          .attr("y2", function (d) {
            return d.y;
          })

        svg.selectAll("circle.end")
          .attr("cx", function (d) {
            return d.x;
          })
          .attr("cy", function (d) {
            return d.y;
          })

      });

谢谢。

UPD:

添加了一个主要的固定正方形,现在看起来更好。但是看起来我必须对“冲突顺序”做一些事情。正方形翻转。有什么想法如何解决? enter image description here

2 个答案:

答案 0 :(得分:2)

我不知道解决此问题的一般方法,但是以下是一些更好的解决方法:

  1. 为矩形重力算法定义“目标点”时,使其不位于圆的边界上,而是相距一定距离。

    x = ((150 + radius) * Math.cos(angle)) + (width / 2) + offset; // Calculate the x position of the element.
    y = ((150 + radius) * Math.sin(angle)) + (width / 2) + offset; // Calculate the y position of the element.
    
  2. 如果希望矩形不与圆碰撞,则需要自定义力。由于矩形与直线碰撞的确切公式并不简单,因此,将矩形视为圆形就足够了,因此请在nodes.forEach(...)之后添加:

    nodes.forEach(function(a) {
       const diag = Math.sqrt(a.width ** 2 + a.height ** 2) / 2;
       const rad = 150;
       const [cx, cy] = [500, 500];
       const [dx, dy] = [a.x - cx, a.y - cy];
       const dist = Math.sqrt(dx ** 2 + dy ** 2);
       const shift = Math.max(0, rad + diag - dist);
       a.x += shift * dx / dist;
       a.y += shift * dy / dist;
    })
    

screenshot

答案 1 :(得分:1)

您的碰撞力太大。减少一点。

              lx *= 0.3;
              ly *= 0.3;

中心矩形可以添加

nodes.push( { x:500, y:500, width:300, height:300, fx:500, fy:500, center: true });

现在应该过滤矩形的绘画

  var g = svg.selectAll("g")
    .data(nodes.filter(d => !d.center))
    .enter()
    .append("g")

如果还绘制中心矩形,则必须添加一个类

  g.append("rect")
    .attr("opacity", 0.5)
    .attr("class", "node")
    .attr("fill", "#ccc" )
    .attr("width", d => d.width )
    .attr("height", d => d.height )
    .attr("rx", 10)
    .attr("ry", 10);

然后在带有类的rect的tick函数过滤器中

svg.selectAll("rect.node")
  .attr("x", d => d.x - d.width / 2 )
  .attr("y", d => d.y - d.height / 2 );

添加测试以忽略中心rect

          if (a.center) return;

更新的部队代码

        nodes.forEach(function (a, i) {
          if (a.center) return;  // ignore this node
          // Apply gravity forces.
          a.x += (a.gravity.x - a.x) * kg;
          a.y += (a.gravity.y - a.y) * kg;
          nodes.slice(i + 1).forEach(function (b) {
            dx = (a.x - b.x)
            dy = (a.y - b.y)
            adx = Math.abs(dx)
            ady = Math.abs(dy)
            mdx = (1 + spaceAround) * (a.width + b.width) / 2
            mdy = (1 + spaceAround) * (a.height + b.height) / 2
            if (adx < mdx && ady < mdy) {
              l = Math.sqrt(dx * dx + dy * dy)
              lx = (adx - mdx) / l * k
              ly = (ady - mdy) / l * k

              lx *= 0.3;
              ly *= 0.3;

              // choose the direction with less overlap
              if (lx > ly && ly > 0) lx = 0;
              else if (ly > lx && lx > 0) ly = 0;
              dx *= lx
              dy *= ly
              a.x -= dx
              a.y -= dy
              b.x += dx
              b.y += dy
            }
          });
        });

修改

一种修改方式是使移动独立于矩形中心的距离。只能沿另一个中心的相反方向移动一定量。

当前方法将列表中稍后的节点已更改为新位置,并将下一个节点的碰撞基于这些新位置。更好的方法是首先根据时间N的所有节点计算运动,最后应用该运动。在力仿真中,这是通过计算/修改节点的速度(d.vxd.vy)来完成的。模拟会将速度应用在刻度上。