p5.js 用于鼠标碰撞检测输出覆盖文本标签的循环

p5.js for loop used for mouse collission detection outputs overlaying text tags

Overlaying text tags

我试图在将鼠标悬停在多个气泡上时显示它们的文本,但是当多个气泡重叠时,显示的文本也会重叠。当我有一个以上的文本命令显示信息时,我试图找出一种“检测”的方法,这样我就可以在屏幕上排列和排序它们而不会发生碰撞。 Bellow 是我根据我的实际项目制作的一些代码,它说明了这个问题。我已经尝试将所有检测到的索引发送到一个数组中,但结果是一个永无止境的数组……我希望我的解释很清楚。我对编码比较陌生,目前很迷茫。任何方向或想法将不胜感激!

let bubbles = {
  pos: [],
  diam: [],
  color: [],
  id: []
}

let initialR = 0;

function setup() {
  createCanvas(600, 400);

  for (var i = 0; i < 60; i++) {
    bubbles.pos.push(createVector(random(0, width), random(0, height)));

    bubbles.color.push(color(random(0, 255), random(0, 255), random(0, 255), 120));

    bubbles.diam.push(random(0, 80));

    bubbles.id.push(round(random(0, 1000)));

  }
  console.log(bubbles)
}

function draw() {
  background(10);

  mX = mouseX;
  mY = mouseY;

  for (var i = 0; i < 60; i++) {

    initialR += 0.01;
    consR = constrain(initialR, 0, bubbles.diam[i]);

    stroke(0);
    fill(bubbles.color[i]);
    ellipse(bubbles.pos[i].x, bubbles.pos[i].y, consR);


    dBubbles = dist(mX, mY, bubbles.pos[i].x, bubbles.pos[i].y);

    if (dBubbles < bubbles.diam[i] / 2) {

      textX = bubbles.pos[i].x;
      textY = bubbles.pos[i].y;

      textSize(20);
      fill(255);
      text(bubbles.id[i], textX, textY);
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

这是一种可能的解决方案:

let bubbles = {
  pos: [],
  diam: [],
  color: [],
  id: []
}

let initialR = 0;

function setup() {
  createCanvas(600, 400);
  textAlign(CENTER, CENTER);

  for (var i = 0; i < 60; i++) {
    bubbles.pos.push(createVector(random(0, width), random(0, height)));

    bubbles.color.push(color(random(0, 255), random(0, 255), random(0, 255), 120));

    bubbles.diam.push(random(0, 80));

    bubbles.id.push(round(random(0, 1000)));
  }
}

function draw() {
  background(10);

  let mX = mouseX;
  let mY = mouseY;
  
  // Use a local variable to tack the hovered bubbles for each frame
  let hovered = [];

  for (var i = 0; i < 60; i++) {
    initialR += 0.01;
    consR = constrain(initialR, 0, bubbles.diam[i]);

    stroke(0);
    fill(bubbles.color[i]);
    ellipse(bubbles.pos[i].x, bubbles.pos[i].y, consR);

    dBubbles = dist(mX, mY, bubbles.pos[i].x, bubbles.pos[i].y);

    if (dBubbles < bubbles.diam[i] / 2) {
      // Don't draw the labels yet, just add the bubble index to the hovered list.
      hovered.push(i);
    }
  }

  textSize(20);
  fill(255);

  // Find the center of the hovered spheres
  let center =
    hovered.map(ix => bubbles.pos[ix])
    // This reduce function computes an running average of all the
    // hovered bubble centers. It is basically using a weighted
    // average formula (avgₙ = (avgₙ₋₁ * count + valueₙ) / (count + 1))
    // with the terms rearranged to prevent a loss of precision for very large lists.
    .reduce(
      (acc, pos) => ({
        center:
          acc.center.mult(acc.count / (acc.count + 1))
             .add(p5.Vector.mult(pos, 1 / (acc.count + 1))),
        count: acc.count + 1
      }), {
        center: createVector(0, 0),
        count: 0
      }
    ).center;

  // Find the width and displacement direction for each label
  let labels = hovered.map(ix => ({
    pos: bubbles.pos[ix].copy(),
    width: textWidth(bubbles.id[ix]),
    text: bubbles.id[ix],
    offsetDirection: p5.Vector.sub(bubbles.pos[ix], center).normalize()
  }));

  // There are probably better ways to displace the labels so that they don't overlap
  for (let i = 0; i < labels.length; i++) {
    // offset the current label until it doesn't collide with any other labels
    while (true) {
      let overlapping = false;
      for (let j = 0; j < labels.length; j++) {
        if (i !== j) {
          if (abs(labels[j].pos.x - labels[i].pos.x) < (labels[j].width + labels[i].width) / 2 &&
            abs(labels[j].pos.y - labels[i].pos.y) < 20) {

            overlapping = true;
            break;
          }
        }
      }

      if (overlapping) {
        // At the very least we could jump by exactly the required offset to prevent
        // the currently detected overlap, but this just increases the offset
        // by ~1 pixel at a time
        labels[i].pos.add(labels[i].offsetDirection);
      } else {
        break;
      }
    }
  }

  for (const lbl of labels) {
    text(lbl.text, lbl.pos.x, lbl.pos.y);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>