如何使气泡之间均匀分布?

时间:2019-05-05 04:36:05

标签: javascript d3.js

我在D3中有一个气泡图。我可以显示1000个气泡,但我想显示2个甚至3000个气泡。我从Shirley Wu借助Guardian here的出色可视化中汲取了灵感。

我的气泡分布不均匀,其中更多的气泡向中心堆积。

enter image description here

我想要得到它,以便它们像这样均匀地分布在整个空间中。

enter image description here

这些是我一直试图操纵的组件。

// Forces
const radius = 1.5
const padding1 = 15;
const padding2 = 5;
const strength = .3
const alpha = .1
const alpha_decay = 0
const alpha_min = 0.001
const alpha_Collision = .03;
const charge_strength = -.5
const charge_theta = .9
const veloc_decay = .5

这是定义我的力量的代码。

  // Circle for each node.
    const circle = svg.append("g")
        .selectAll("circle")
        .data(nodes)
        .join("circle")
          .attr("cx", d => d.x)
          .attr("cy", d => d.y)
          .attr("fill", d => d.color)
          .attr("r", d => d.r);

     // Forces
     const simulation = d3.forceSimulation(nodes)
         .force("x", d => d3.forceX(d.x))
         .force("y", d => d3.forceY(d.y))
         .force("cluster", forceCluster())
         .force("collide", forceCollide())
         .force("charge", d3.forceManyBody().strength(charge_strength).theta(charge_theta))
         .alpha(alpha)
         .alphaDecay(alpha_decay)
         .alphaMin(alpha_min)
         .velocityDecay(veloc_decay);

     // Adjust position of circles.
      simulation.on("tick", () => {
          circle
              .attr("cx", d => d.x)
              .attr("cy", d => d.y)
              .attr("fill", d => groups[d.group].color);
          });

      // Force to increment nodes to groups.
      function forceCluster() {
        let nodes;

        function force(alpha) {
          const l = alpha * strength;
          for (const d of nodes) {
            d.vx -= (d.x - groups[d.group].x) * l;
            d.vy -= (d.y - groups[d.group].y) * l;
          }
        }
        force.initialize = _ => nodes = _;

        return force;
      }

      // Force for collision detection.
      function forceCollide() {
        let nodes;
        let maxRadius;

        function force() {
          const quadtree = d3.quadtree(nodes, d => d.x, d => d.y);
          for (const d of nodes) {
            const r = d.r + maxRadius;
            const nx1 = d.x - r, ny1 = d.y - r;
            const nx2 = d.x + r, ny2 = d.y + r;
            quadtree.visit((q, x1, y1, x2, y2) => {

              if (!q.length) do {
                if (q.data !== d) {
                  const r = d.r + q.data.r + (d.group === q.data.group ? padding1 : padding2);
                  let x = d.x - q.data.x, y = d.y - q.data.y, l = Math.hypot(x, y);
                  if (l < r) {
                    l = (l - r) / l * alpha_Collision;
                    d.x -= x *= l, d.y -= y *= l;
                    q.data.x += x, q.data.y += y;
                  }
                }
              } while (q = q.next);
              return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
            });
          }
        }

        force.initialize = _ => maxRadius = d3.max(nodes = _, d => d.r) + Math.max(padding1, padding2);

        return force;
      }

我已将代码上载here。问题的一部分是中心的气泡不断移动。就像他们试图找到一个住所,但空间的宽度不够大(除非如果加大它,整个斑点就会变得太大)。

我不知道如何停止运动。当我将alpha_decay设置为.1时看起来更好,但是气泡将可视化中的位置从unaware更改为aware,如果alpha_decay > 0,气泡将不会移动。

如何像示例中所示那样使气泡均匀地散布而不会在中心聚集?

1 个答案:

答案 0 :(得分:1)

您可以使用本机d3 force方法来实现它。只需发挥不同的电荷,中心和碰撞力即可。

height = 300;
width = 300;

const nodes = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, ];
  const simulation = d3.forceSimulation(nodes)
    .force('charge', d3.forceManyBody().strength(0.4))
    .force('center', d3.forceCenter(width / 2, height / 2))
    .force('collision', d3.forceCollide().radius(6))
    .on('tick', ticked);

  function ticked() {
    var u = d3.select('svg')
    .selectAll('circle')
    .data(nodes)

    u.enter()
      .append('circle')
      .attr('r', 5)
      .merge(u)
      .attr('cx', function(d) {
        return d.x
      })
      .attr('cy', function(d) {
        return d.y
      });

    u.exit().remove()
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="300" height="300"></svg>