首页 > 解决方案 > 如何在气泡之间均匀分布?

问题描述

我在 D3 中有一个气泡图。我让它可以显示大约 1,000 个气泡,但我想让它显示 2 甚至 3000 个气泡。我在这里从Shirley Wu与 Guardian 的惊人可视化中获得灵感。

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

在此处输入图像描述

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

在此处输入图像描述

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

// 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;
      }

我在这里上传了代码。部分问题在于中心的气泡不断移动。就好像他们想找个地方安顿下来,但空间的宽度不够大(除非我把它弄大,整个blob就变得太大了)。

我不知道如何停止运动。当我设置为时它看起来更好alpha_decay.1但气泡在可视化上的位置从unawareaware并且它们不会移动如果alpha_decay > 0

如何使气泡均匀分布而不聚集在中心,如示例所示?

标签: javascriptd3.js

解决方案


您可以使用本机 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>


推荐阅读