Search code examples
c++vectorflood-fill

How should I implement a flood fill function to my c++ program?


Im currently looking to turn something like this:

.............
.............
..XXX.....X..
..XXX.....X..
..XXX........
..XXX........
..XXXXXXX....
..XXXXXXX....
..XXXXXXX....
.............
.............

into this:

.............
.............
..XXX.....O..
..XXX.....O..
..XXX........
..XXX........
..XXXXXXX....
..XXXXXXX....
..XXXXXXX....
.............
.............

with the user entering ./a.exe input4.txt floodfill 2 10 o

I believe im going to need to implement some recursion into the program to be able to only look at indexes that match that of the user pointer (including positions of up, down, left, and right) instead of reading the entire vector (which i wouldn't mind doing but dont see how i would begin doing so).

here is the code I have so far for the flood fill function:

void floodfilll(vector<vector<char>> &vec, int x, int y, char r, char dum)
{
 int ii; 
 ii = 0;
 int jj; 
 jj = 0;
for (int i = 0; i < vec.size(); i ++) {
        for (int j = 0; j < vec[i].size(); j++) {
            if (vec[i][j] == r) {
                vec[i][j] == r;
                if ((i + ii) > 0) {
                    if (vec[i-1][j] == r)
                        vec[i-1][j] = dum;
                        vec[i][j] == r;
                    ii--;
                    floodfilll(vec, x + ii, y, r, dum);
                }
                if ((j + jj) > 0) {
                    if(vec[i][j-1] != r)
                        vec[i][j-1] = dum;
                        vec[i][j] == r;
                    jj--;
                    floodfilll(vec, x, y + jj, r, dum);
                }
                if ((i + ii)<vec.size()) {
                    if (vec[i+1][j] != r)
                        vec[i+1][j] = dum;
                        vec[i][j] == r;
                    ii++;
                    floodfilll(vec, x + ii, y, r, dum);
                }
                if ((j + jj)<vec[i].size()) {
                    if (vec[i][j+1] != r)
                        vec[i][j+1] = dum;
                        vec[i][j] == r;
                    jj++;
                    floodfilll(vec, x, y + jj, r, dum);
                }
            }
        }
        replacee(vec, dum, r);
    }
}

NOTE: Im using a function called replacee to replace Var dum, with Var R. Var dum is assigned 'i' and r is 'X'.

Also, the text file is parsed as a 2d vector of char's (char)**

Its just the way the rest of my program is based. Here is the replace function:

    void replacee(vector<vector<char>> &vec, char oldd, char neww)
    {
        for (vector<char> &v : vec) // reference to innver vector
        {
            replace(v.begin(), v.end(), oldd, neww); // standard library algorithm
        }
    }

This is the int main file im using:

int main(int argc, char* argv[]) {

fstream fin; char ch;
string name (argv[1]); //File Name.
vector<vector<char>> data;
// 2D Vector.
vector<char> temp;
// Temporary vector to be pushed 
// into vec, since its a vector of vectors.
fin.open(name.c_str(),ios::in);
// Assume name as an arbitary file.
string argument2 (argv[2]);
while(fin)
{
    ch = fin.get();
    if(ch!='\n') {
        temp.push_back(ch);
    }
    else 
    { 
        data.push_back(temp); 
        temp.clear(); 
    }
}
if (argument2 == "floodfill") {
    string argument3 (argv[3]);
    string argument4 (argv[4]);
    string argument5 (argv[5]);
    int x = 0;
    int y = 0;
    stringstream xx(argument3);
    stringstream yy(argument4);
    xx >> x;
    yy >> y;
    floodfilll(data, x, y, argument5[0], 'i');
    for (int m = 0; m < data.size(); m ++) {
        for (int n = 0; n < data[m].size(); n++) {
            cout << data[m][n];
        }
        cout << endl;
    }
}

fin.close();
} 

Sorry if it looks like im just pasting code for grabs, this is incase anyone has a solution outside my mode of thought. The int main and replacee functions work as intended. I just need help coming up with a way to make floodfilll work correctly.

This is the output i get with my code:

$ ./a.exe input4.txt floodfill 2 10 o
.............
.............
..XXX.....X..
..XXX.....X..
..XXX........
..XXX........
..XXXXXXX....
..XXXXXXX....
..XXXXXXX....
.............

Solution

  • Why do you iterate over the whole field in each recursion?

    Normally, flood filling works as follows:

    1. You have a specific starting point.
    2. You fill this starting point with the colour intended
    3. You check for each of the four (or 8, if you consider diagonals as well) neighbours, if they have the same colour as the starting point originally had; if so, you continue with recursively.

    So an implementation might look like this:

    void floodfill
    (
            std::vector<std::vector<char>>& v,
            unsigned int x, unsigned int y, char r
    )
    {
        char p = v[x][y];
        v[x][y] = r;
        if(x > 0 && v[x - 1][y] == p)
            floodfill(v, x - 1, y, r);
        if(x + 1 < v.size() && v[x + 1][y] == p)
            floodfill(v, x + 1, y, r);
        if(y > 0 && v[x][y - 1] == p)
            floodfill(v, x, y - 1, r);
        if(y + 1 < v[x].size() && v[x][y + 1] == p)
            floodfill(v, x, y + 1, r);
    }
    

    Note that I did not check for the case of colour to fill being the same as the one of the starting pixel, neither did I initially check the range check of x and y. For efficiency, I wouldn't add these checks in the recursive function, but in a specific entry function starting the recursion, so they are done only once when needed and not needlessly repeated.