我正试图为我的绘画应用程序创建一个存储桶工具,该应用程序是用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
现在发生的是,如果我尝试填充某些东西,它会画一条直线直到达到另一种颜色,然后达到最大堆栈大小,
例:
这可能是因为在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
中,我先依次用floodFill
和y - 1
来调用它,这没有意义,因为它先升后降意味着它停留在同一像素上,所以我将其编辑为:
y + 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>