【问题标题】:Paint bucket getting "Maximum Call Stack Size Exceeded" error油漆桶出现“超出最大调用堆栈大小”错误
【发布时间】:2022-08-22 01:18:08
【问题描述】:

这是我的绘图应用程序中使用 p5.js 库的油漆桶工具的代码。功能self.floodFill由于递归,总是得到“超出最大调用堆栈大小”,我想知道修复它的方法。我在想将函数更改为无递归函数是否有帮助。任何帮助,将不胜感激。

function BucketTool(){
    var self = this;
    //set an icon and a name for the object
    self.icon = \"assets/bucket.jpg\";
    self.name = \"Bucket\";
    var d = pixelDensity();
    
    var oldColor;
    var searchDirections = [[1,0],[-1,0],[0,1],[0,-1]];
    
    var pixelsToFill = [];
    var positionArray = new Array(2);
    
    self.checkBoundary = function(currentX, currentY, localOldColor) {
        if (self.getPixelAtXYPosition(currentX,currentY).toString() != localOldColor.toString() || currentX < 0 || currentY < 0 || currentX > width || currentY > height || pixelsToFill.indexOf(currentX+\" \"+currentY) != -1) {
            return false;
        }
        return true;
    };
    
    self.floodFill = function(currentX, currentY, localOldColor, localSearchDirections) {
        if (self.checkBoundary(currentX, currentY, localOldColor)){
            pixelsToFill.push(currentX+\" \"+currentY);
        } else {
            return;
        }
        for (var i = 0; i < searchDirections.length; i++){
            self.floodFill(currentX + searchDirections[i][0], currentY + searchDirections[i][1], localOldColor, localSearchDirections);
        }
    };
    
    self.getPixelAtXYPosition = function(x, y) {
        var colour = [];
        for (var i = 0; i < d; i++) {
          for (var j = 0; j < d; j++) {
            // loop over
            index = 4 * ((y * d + j) * width * d + (x * d + i));
            colour[0] = pixels[index];
            colour[1] = pixels[index+1];
            colour[2] = pixels[index+2];
            colour[3] = pixels[index+3];
          }
        }
        return colour;
    }
    
    self.drawTheNeededPixels = function(){
        for(var i = 0; i < pixelsToFill.length; i++){
            positionArray = pixelsToFill[i].split(\" \");
            point(positionArray[0],positionArray[1]);
        }
    }
    
    self.draw = function () {
        if(mouseIsPressed){
            pixelsToFill = [];
            
            loadPixels();
            
            oldColor = self.getPixelAtXYPosition(mouseX, mouseY);
            
            self.floodFill(mouseX, mouseY, oldColor, searchDirections);
            
            self.drawTheNeededPixels();
        }
        
    };
    
}
  • 你能提供其余的javascript代码吗?例如,mouseIsPressed 是如何被评估的?
  • 该库的所有代码引用都在p5js.org 中。比如mouseIsPressedp5js.org/reference/#/p5/mouseIsPressed。非常感谢
  • 你填位置(x, y),然后方向[1, 0],你填(x + 1, y),然后方向[-1, 0]你填(x, y),然后方向[1, 0],你\'正在填写(x + 1, y),然后使用方向[-1, 0],您正在填写(x, y),然后使用...您需要跟踪您已填写的那些。
  • 问题是当我画一个小圆圈并在里面点击时,我可以毫无错误地填充。但是,当我单击更大的空间时,它会立即收到错误消息。
  • 您可以尝试制作一个工作示例吗?调试起来会更容易。

标签: javascript recursion drawing p5.js flood-fill


【解决方案1】:

这个问题在wikipedia page 上有很好的记录,以及不同类型的算法在执行洪水填充方面的不足之处。您已经选择了基于堆栈的递归实现。

为了防止堆栈溢出——超过最大调用堆栈——第一步是使用数据结构。使用队列/堆栈而不是让函数自己调用。

下面的代码创建了一个空堆栈,我们在其中放置了一个包含用户选择填充的 x ​​和 y 的新对象。然后将其添加到pixelsToFill 数组中。然后我们循环堆栈直到它完全为空,此时我们准备好显示填充的像素。

在 while 循环中,我们从堆栈中弹出一个元素,然后找到它的子元素——由您创建的 searchDirections 数组表示的上、下、左、右方向。如果我们之前没有见过孩子并且它在边界内,我们将它添加到 pixelsToFill 数组中,并将其添加到堆栈中以重复该过程:

self.floodFill = function (currentX, currentY, localOldColor, localSearchDirections) {
    let stack = [];
    stack.push({ x: currentX, y: currentY });
    pixelsToFill.push(currentX + " " + currentY);
    while (stack.length > 0) {
      let current = stack.pop();
      for (var i = 0; i < searchDirections.length; i++) {
        let child = {
          x: current.x + searchDirections[i][0],
          y: current.y + searchDirections[i][1],
          localOldColor,
        };
        if (self.checkBoundary(child.x, child.y, localOldColor)) {
          pixelsToFill.push(child.x + " " + child.y);    
          stack.push(child);
        }
      }
    }
  };

此代码可能会停止 stackoverflow,但仍然可以进行很多优化。再一次,值得一试the Wikipedia page 并可能看看跨度填充。

let bucketTool;
function setup() {
  createCanvas(400, 400);
  bucketTool = new BucketTool();
}

function draw() {
  background(220);
  strokeWeight(5);
  circle(width / 2, height / 2, 100);
  frameRate(1);
  bucketTool.draw();
}

function BucketTool() {
  var self = this;
  //set an icon and a name for the object
  // self.icon = "assets/bucket.jpg";
  // self.name = "Bucket";
  var d = pixelDensity();

  var oldColor;
  var searchDirections = [
    [1, 0],
    [-1, 0],
    [0, 1],
    [0, -1],
  ];

  var pixelsToFill = [];
  var positionArray = new Array(2);

  self.checkBoundary = function (currentX, currentY, localOldColor) {
    if (
      self.getPixelAtXYPosition(currentX, currentY).toString() !=
        localOldColor.toString() ||
      currentX < 0 ||
      currentY < 0 ||
      currentX > width ||
      currentY > height ||
      pixelsToFill.indexOf(currentX+" "+currentY) != -1
    ) {
      return false;
    }
    return true;
  };

  self.floodFill = function (currentX, currentY, localOldColor, localSearchDirections) {
    let stack = [];
    stack.push({ x: currentX, y: currentY });
    pixelsToFill.push(currentX + " " + currentY);
    while (stack.length > 0) {
      let current = stack.pop();
      for (var i = 0; i < searchDirections.length; i++) {
        let child = {
          x: current.x + searchDirections[i][0],
          y: current.y + searchDirections[i][1],
          localOldColor,
        };
        if (self.checkBoundary(child.x, child.y, localOldColor)) {
          pixelsToFill.push(child.x + " " + child.y);    
          stack.push(child);
        }
      }
    }
  };

  self.getPixelAtXYPosition = function (x, y) {
    var colour = [];
    for (var i = 0; i < d; i++) {
      for (var j = 0; j < d; j++) {
        // loop over
        index = 4 * ((y * d + j) * width * d + (x * d + i));
        colour[0] = pixels[index];
        colour[1] = pixels[index + 1];
        colour[2] = pixels[index + 2];
        colour[3] = pixels[index + 3];
      }
    }
    return colour;
  };

  self.drawTheNeededPixels = function () {
    for (var i = 0; i < pixelsToFill.length; i++) {
      positionArray = pixelsToFill[i].split(" ");
      point(positionArray[0], positionArray[1]);
    }
  };

  self.draw = function () {
    if (mouseIsPressed) {
      pixelsToFill = [];

      loadPixels();

      oldColor = self.getPixelAtXYPosition(mouseX, mouseY);
      self.floodFill(mouseX, mouseY, oldColor, searchDirections);

      console.log(pixelsToFill.length);
      self.drawTheNeededPixels();
    }
  };
}
html, body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
    </main>
    <script src="sketch.js"></script>
  </body>
</html>

无耻的插件,但相关:我创建了一个博客来比较不同的 flood fill algorithms 使用 p5.js。

【讨论】:

  • 非常感谢您的帮助。我知道这是我的问题,但我只想知道有没有办法让它更快,因为当我尝试点击更大的空间时它有点慢。我不再收到错误消息,但我需要等一分钟才能填满。
  • popshift 快。
猜你喜欢
  • 2011-08-31
  • 1970-01-01
  • 2019-07-19
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
  • 2019-11-30
  • 1970-01-01
  • 2021-08-15
相关资源
最近更新 更多