P5.js电子涂料应用中的铲斗工具的洪水填充算法

时间:2018-06-30 13:25:19

标签: javascript p5.js flood-fill

我正试图为我的绘画应用程序创建一个存储桶工具,该应用程序是用electron.js和P5.js制作的画布材料。每当用户单击画布并且他的mode变量等于“ fill”时,它就会调用FloodFill函数。我尝试从此处实现算法:https://en.wikipedia.org/wiki/Flood_fill#Pseudocode(基于堆栈的递归实现(四向)),但是我有一个问题-我正在达到最大调用堆栈。 我将所有内容渲染到屏幕上的方式是,我有一个名为drawing的数组,并且具有可用于绘制的事物类型(圆形,正方形)的构造函数,因此我创建了Pixel构造函数,以便可以对其进行渲染每次我都无法调用updatePixels(),因为我使用draw()函数在每一帧渲染所有内容。 这是我的FloodFill函数:

function floodFill(x, y, target_col, replace_col) {
   if (x >= 0 && y >= 0 && x <= width && y <= height) {
        let index = (x + y * width) * 4;
        if (target_col === replace_col) {
            return;
        } else if (
            pixels[index] !== target_col.levels[0] &&
            pixels[index + 1] !== target_col.levels[1] &&
            pixels[index + 2] !== target_col.levels[2] &&
            pixels[index + 3] !== target_col.levels[3]
        ) {
            return;
        } else {
            drawing.push(new Pixel(x, y, replace_col));
            floodFill(x, y - 1, target_col, replace_col);
            floodFill(x, y + 1, target_col, replace_col);
            floodFill(x - 1, y, target_col, replace_col);
            floodFill(x + 1, y, target_col, replace_col);
        }
    }
}

这就是我调用该函数的方式:

if (mode === "fill") {
    loadPixels();
    let index = (mouseX + mouseY * width) * 4;

    // target_color = what the user wants to replace
    let target_color = color(
        pixels[index],
        pixels[index + 1],
        pixels[index + 2],
        pixels[index + 3]
    );
   // replacement_color = the color that will be instead of the target_color
   let replacement_color = color(current_color);
    floodFill(mouseX, mouseY, target_color, replacement_color);
}

floodFill的{​​{1}}函数中,我之所以使用此技术,是因为简单地执行else if无效

编辑: 我意识到我的FloodFill函数出错,我的color(pixels[index], pixels[index + 1], pixels[index + 2], pixels[index + 3]) !== target_col几乎永远不会成立,因为它还会尝试检查Alpha是否不相等,并且它们几乎总是255。所以我在其他情况下添加了以下内容:< / p>

else if

现在发生的是,如果我尝试填充某些东西,它会画一条直线直到达到另一种颜色,然后达到最大堆栈大小, 例: paint 这可能是因为在else if ( (pixels[index] !== target_col.levels[0] && pixels[index + 1] !== target_col.levels[1] && pixels[index + 2] !== target_col.levels[2]) || pixels[index + 3] !== target_col.levels[3] ) { 函数中,我进行的第一个递归调用是floodFill(向上)。 我希望听到更多建议

另一个编辑: 我发现在递归部分的y - 1中,我先依次用floodFilly - 1来调用它,这没有意义,因为它先升后降意味着它停留在同一像素上,所以我将其编辑为:

y + 1

现在它显示如下: paint

1 个答案:

答案 0 :(得分:0)

这是p5.js中的填充示例。

在洞中绘制一个封闭的形状,然后在其内部按住Shift键并单击。

var stack = [];
var oldColor;
var fillColor


function setup() {
  createCanvas(windowWidth, windowHeight);
  noSmooth();
  fillColor = color(0, 255, 0);
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

function matches(c, x, y) {
  var i = 4 * (y * width + x);
  return (
    pixels[i + 0] === c[0] &&
    pixels[i + 1] === c[1] &&
    pixels[i + 2] === c[2] &&
    pixels[i + 3] === c[3]
  );
}

function draw() {
  if (!stack.length) return;

  var p = stack.pop();
  var x1 = p.x,
    y = p.y;
  while (x1 > 0 && matches(oldColor, x1 - 1, y)) x1--;

  var spanAbove = false,
    spanBelow = false;

  var x2 = x1 + 1;
  var ip = 4 * (y * width + x2);
  while (x2 < width && matches(oldColor, x2, y)) {
    for (var i = 0; i < 4; i++)
      pixels[ip++] = fillColor.levels[i];

    if (y > 0 && spanAbove !== matches(oldColor, x2, y - 1)) {
      if (!spanAbove) stack.push({
        x: x2,
        y: y - 1
      });
      spanAbove = !spanAbove;
    }
    if (y < height - 1 && spanBelow !== matches(oldColor, x2, y + 1)) {
      if (!spanBelow) stack.push({
        x: x2,
        y: y + 1
      });
      spanBelow = !spanBelow;
    }
    x2++;
  }

  updatePixels();
}

function mouseDragged() {
  if (keyIsDown(SHIFT)) return;
  stroke(255);
  strokeWeight(2);
  line(pmouseX, pmouseY, mouseX, mouseY);
}

function mouseClicked() {
  if (keyIsDown(SHIFT)) {
    oldColor = get(mouseX, mouseY);
    loadPixels();
    stack = [];
    stack.push({
      x: mouseX,
      y: mouseY
    });
  }
}
html,
body {
  margin: 0;
  padding: 0;
  border: none;
  background: black;
}

canvas {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>