0

I am trying to implement a paint bucket that instaead of coloring the shape it creates a new shape and cover it. after some research I decided to implement the flood fill algorithm using a stack. '''

floodFill(pos: Point2D, stack: Point2D[]): void {
    if (this.stroke.includes(pos)) { return; }
    this.setColor(pos);
    this.stroke.push(pos);
    const cardinalPoints: Point2D[] = [{x: pos.x, y: pos.y - 1}, {x: pos.x + 1, y: pos.y},
                                       {x: pos.x, y: pos.y + 1}, {x: pos.x - 1, y: pos.y}];
    cardinalPoints.forEach( (point) => {
      const color = this.getColor(point);
      if (this.toleranceCheck(color, this.targetColor) && !stack.includes(point)) { stack.push(point); }
    });
    console.log(stack.length);
    const nextPoint = stack.pop();
    if (nextPoint) {this.floodFill(nextPoint, stack); }
  }

''' I think it is stuck in the recursion.

  • setColor sets the desired pixels of the ImageData.data uint8ClampedArray
  • tolerance check checks if the pixel's color are within [targetColor - (targetColor * tolerance), targetColor + (targetColor * tolerance)];
  • stroke is a list of the visited points
  • stack is the stack of point to check I tried implementing it without a stack (calling it North/east/west/south) but the same happens.

A fix for that is calling the function in a while loop like shown in the code under:

  async onMouseDown(e: MouseEvent): Promise<void> {
await this.getCanvasData();
this.stack.push(new Point2D(e.offsetX, e.offsetY));
this.targetColor = this.getColor(new Point2D(e.offsetX, e.offsetY));
this.visited = [];
this.control.addShape(this.shape);
this.shape.style.strokeWidth = '1';
this.shape.style.primaryColor = this.colors.getPrimaryString();
this.shape.style.secondaryColor = this.colors.getPrimaryString();
while (this.stack.length !== 0) {
  console.log(this.stack.length);
  let point = this.stack.pop();
  while (point && this.visitedIncludes(point)) { point = this.stack.pop(); }
  console.log(this.stack.length);
  if (point) { this.floodFill(point);}
  this.pathify();
}

}

This code works fine for small areas. But is really slow for bigger areas. can it be optimised?

Farid Fakhry
  • 354
  • 1
  • 10
  • It looks like your code is individually colouring each pixel using a stroke, and each time it runs it checks all the pixels around it to see if they have the correct colour or not, to add the next pixels. The problem is every time you add a pixel it’s causing a new recursion in the same stack. – adamfsk Apr 11 '20 at 19:11
  • Node allows for approx 20k recursive calls before a stack overflow is created. That means this would only work on an area less than approx 200x100px. It’d be better to perform this in two steps; identify the pixels that need colouring first (possibly use a while loop?) and then colour them. – adamfsk Apr 11 '20 at 19:14
  • yes I found this out and instead of making it a recursion a set the stack parameter as attribute of my service and call the function using a while loop! this is working alright for small numbers of pixels but really slow for bigger areas – Farid Fakhry Apr 11 '20 at 20:38
  • Yeah it’s really not an efficient way to fill a path. If you’re just trying to do that you could simply use the fill() method, but I don’t think that allows for a tolerance property. The fastest method to do something like that is likely to use WebGL. Check https://stackoverflow.com/questions/34709128/webgl-draw-pixels-inside-vertices-position for a starting point on how to fill a path and from there add your tolerance in before colouring the pixels. – adamfsk Apr 11 '20 at 20:52

0 Answers0