0

I want to fill a polygon using 8-connected boundary fill. The code for 4-connected boundary fill works. However, when I add the four additional statements to test diagonal positions for 8-connected boundary fill, I get ArrayIndexOutOfBoundsException: Coordinate out of bounds! error. What is the problem and how to solve it?

private void bfill(int x, int y, Color fillColour, Color borderColour){
    Stack<Point> points = new Stack<>();
    points.add(new Point(x, y));
    while(!points.isEmpty()) {
        Point currentPoint = points.pop();
        x = currentPoint.x;
        y = currentPoint.y;
        Color interiorColour = new Color(bi.getRGB(x, y));
        if (!interiorColour.equals(borderColour) && !interiorColour.equals(fillColour)){
            setPixel(x, y); //draw pixel
            points.push(new Point(x+1, y));
            points.push(new Point(x-1, y));
            points.push(new Point(x, y+1));
            points.push(new Point(x, y-1));
            //Error occurs when the next four lines are uncommented for 8-connected boundary fill
            /*points.push(new Point(x+1, y+1));
            points.push(new Point(x+1, y-1));
            points.push(new Point(x-1, y-1));
            points.push(new Point(x-1, y+1));*/
        }
    }
}

Edit: Following gpasch's answer, I included bounds checking. However, the program runs endlessly. What is wrong with the bounds checking?

if (!interiorColour.equals(borderColour) && !interiorColour.equals(fillColour)){
    if (x > -1 && y > -1 && x < getWidth() && y < getHeight()){
        setPixel(x, y); //draw pixel
        if (x+1 < getWidth())   points.push(new Point(x+1, y));
        if (x-1 > -1)           points.push(new Point(x-1, y));
        if (y+1 < getHeight())  points.push(new Point(x, y+1));
        if (y-1 > -1)           points.push(new Point(x, y-1));

        if (x+1 < getWidth() && y+1 < getHeight())  points.push(new Point(x+1, y+1));
        if (x+1 < getWidth() && y-1 > -1)           points.push(new Point(x+1, y-1));
        if (x-1 > -1 && y-1 > -1)                   points.push(new Point(x-1, y-1));
        if (x-1 > -1 && y+1 > getHeight())          points.push(new Point(x-1, y+1));
        }
    }
}
Saiyan
  • 197
  • 7
  • 22
  • Can you please add the stacktrace of the exception you get, a push call can't throw an ArrayIndexOutOfBoundsException. – Bax Apr 01 '16 at 16:22

2 Answers2

1

(x, y) is going out of bounds.

You need to check if

(X>-1 && x<width) &&  (y>-1 && y<height)

The same for when adding the points (if you want to avoid later trouble).

If any (x+1, y), .... etc falls out of bounds, don't add it.

--

You have to restrain the bounds checking as follows:

Color interiorColour = null;
if (x > -1 && y > -1 && x < getWidth() && y < getHeight()) { 
  interiorColour=new Color(bi.getRGB(x, y));
  if (!interiorColour.equals(borderColour) && !interiorColour.equals(fillColour)){
        setPixel(x, y); //draw pixel
        if (x+1 < getWidth())   points.push(new Point(x+1, y));
        if (x-1 > -1)           points.push(new Point(x-1, y));
        if (y+1 < getHeight())  points.push(new Point(x, y+1));
        if (y-1 > -1)           points.push(new Point(x, y-1));

        if (x+1 < getWidth() && y+1 < getHeight())  points.push(new Point(x+1, y+1));
        if (x+1 < getWidth() && y-1 > -1)           points.push(new Point(x+1, y-1));
        if (x-1 > -1 && y-1 > -1)                   points.push(new Point(x-1, y-1));
        if (x-1 > -1 && y+1 < getHeight())          points.push(new Point(x-1, y+1));
  }
}

--

On further consideration your solution suffers from the following problem: points that are visited are not excluded so they can be visited over and over again leading to potentially a never-ending program. I dont understand the full implications of your algorithm but I suggest the following:

a) define an array:

boolean[] visited=new boolean[width*height];
for(i=0; i<visited.length; i++) visited[i]=false;

b) when you enter the loop and have a point (x, y) - after popping:

    if(visited[x+y*width]) continue;        
    visited[x+y*width]=true;

c) adjust the checking as follows:

    if (x+1 < width)  if(!visited[x+1+y*width]) points.push(new Point(x+1, y));
    if (x-1 > -1)     if(!visited[x-1+y*width])      points.push(new Point(x-1, y));
    if (y+1 < height) if(!visited[x+(y+1)*width]) points.push(new Point(x, y+1));
    if (y-1 > -1)    if(!visited[x+(y-1)*width])       points.push(new Point(x, y-1));

Similar for the other four checks.

The stack can at most reach a size of width*height then it will decrease to 0.

Notice also the error y+1>getHeight(), should be y+1 < getHeight().

gpasch
  • 2,672
  • 3
  • 10
  • 12
  • I included bounds checking. However, the program runs endlessly. See the edit in the question. – Saiyan Apr 01 '16 at 17:54
  • The program still runs endlessly. – Saiyan Apr 01 '16 at 19:11
  • well its a debugging process: check the size of the stack; check that the colors are ever equal; we need to close this question; open up another one – gpasch Apr 01 '16 at 19:28
  • I included the visited array in the bounds checking. The program terminates. However, it fills the whole window. I found the problem and a solution. See my answer. – Saiyan Apr 02 '16 at 08:45
0

Following gpasch's updated answer, I included the visited array in the bounds checking. The program terminates. However, it fills the whole window.

I tested the x and y values as each point is popped. They go beyond the boundaries of the polygon. Points outside the polygon are of the same colour as points inside the polygon. Both are neither of the border colour nor the fill colour. Therefore they are pushed onto the stack and, when popped, coloured with the fill colour.

Bounds checking is required but it should be limited to the boundaries of the polygon and not to the boundaries of the window. So, once I have a point (x, y) after popping, I check if it is in the polygon using the contains method of the Polygon object.

Saiyan
  • 197
  • 7
  • 22