How should I implement a flood fill function to my c++ program?
up vote
1
down vote
favorite
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....
.............
c++ vector flood-fill
add a comment |
up vote
1
down vote
favorite
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....
.............
c++ vector flood-fill
1
Rather than iterate over the entirevector<vector<char>>
when floodfilling, why not start at x,y and branch out adjacently until no more matches are found? Also, your 5th argumento
is the character to search, not the new desired character.
– Enfyve
Nov 11 at 9:27
1
So let me get this straight, all you want is to floodfill the area of x-s defined by the starting position with o-s?
– Qubit
Nov 11 at 9:28
@Qubit yes thats what im trying to do
– xannax159
Nov 11 at 9:31
'The int main [...] functions work as intended.' - only if user provides appropriate number of command line arguments, otherwise undefined behaviour (crash likely). Get used right from the start to check if they are available. Additionally, check the fail bits of your string streams for the case if user did not provide numbers. You could as well check if there are further characters left (to catch input like7s
as invalid).
– Aconcagua
Nov 11 at 9:47
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
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....
.............
c++ vector flood-fill
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....
.............
c++ vector flood-fill
c++ vector flood-fill
asked Nov 11 at 9:04
xannax159
448
448
1
Rather than iterate over the entirevector<vector<char>>
when floodfilling, why not start at x,y and branch out adjacently until no more matches are found? Also, your 5th argumento
is the character to search, not the new desired character.
– Enfyve
Nov 11 at 9:27
1
So let me get this straight, all you want is to floodfill the area of x-s defined by the starting position with o-s?
– Qubit
Nov 11 at 9:28
@Qubit yes thats what im trying to do
– xannax159
Nov 11 at 9:31
'The int main [...] functions work as intended.' - only if user provides appropriate number of command line arguments, otherwise undefined behaviour (crash likely). Get used right from the start to check if they are available. Additionally, check the fail bits of your string streams for the case if user did not provide numbers. You could as well check if there are further characters left (to catch input like7s
as invalid).
– Aconcagua
Nov 11 at 9:47
add a comment |
1
Rather than iterate over the entirevector<vector<char>>
when floodfilling, why not start at x,y and branch out adjacently until no more matches are found? Also, your 5th argumento
is the character to search, not the new desired character.
– Enfyve
Nov 11 at 9:27
1
So let me get this straight, all you want is to floodfill the area of x-s defined by the starting position with o-s?
– Qubit
Nov 11 at 9:28
@Qubit yes thats what im trying to do
– xannax159
Nov 11 at 9:31
'The int main [...] functions work as intended.' - only if user provides appropriate number of command line arguments, otherwise undefined behaviour (crash likely). Get used right from the start to check if they are available. Additionally, check the fail bits of your string streams for the case if user did not provide numbers. You could as well check if there are further characters left (to catch input like7s
as invalid).
– Aconcagua
Nov 11 at 9:47
1
1
Rather than iterate over the entire
vector<vector<char>>
when floodfilling, why not start at x,y and branch out adjacently until no more matches are found? Also, your 5th argument o
is the character to search, not the new desired character.– Enfyve
Nov 11 at 9:27
Rather than iterate over the entire
vector<vector<char>>
when floodfilling, why not start at x,y and branch out adjacently until no more matches are found? Also, your 5th argument o
is the character to search, not the new desired character.– Enfyve
Nov 11 at 9:27
1
1
So let me get this straight, all you want is to floodfill the area of x-s defined by the starting position with o-s?
– Qubit
Nov 11 at 9:28
So let me get this straight, all you want is to floodfill the area of x-s defined by the starting position with o-s?
– Qubit
Nov 11 at 9:28
@Qubit yes thats what im trying to do
– xannax159
Nov 11 at 9:31
@Qubit yes thats what im trying to do
– xannax159
Nov 11 at 9:31
'The int main [...] functions work as intended.' - only if user provides appropriate number of command line arguments, otherwise undefined behaviour (crash likely). Get used right from the start to check if they are available. Additionally, check the fail bits of your string streams for the case if user did not provide numbers. You could as well check if there are further characters left (to catch input like
7s
as invalid).– Aconcagua
Nov 11 at 9:47
'The int main [...] functions work as intended.' - only if user provides appropriate number of command line arguments, otherwise undefined behaviour (crash likely). Get used right from the start to check if they are available. Additionally, check the fail bits of your string streams for the case if user did not provide numbers. You could as well check if there are further characters left (to catch input like
7s
as invalid).– Aconcagua
Nov 11 at 9:47
add a comment |
2 Answers
2
active
oldest
votes
up vote
2
down vote
accepted
Why do you iterate over the whole field in each recursion?
Normally, flood filling works as follows:
- You have a specific starting point.
- You fill this starting point with the colour intended
- 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.
Thank you, i wasn't using the ++ statement correctly, neither were my if statements, I guess i got carried off with something too deep to even work.
– xannax159
Nov 11 at 9:39
add a comment |
up vote
1
down vote
One option is to use recursion, as suggested by the other answer. However, personally I prefer avoiding recursion where it is not necessary. An alternative, is a queue-based approach.
void floodfill (std::vector<std::vector<char>>& v, unsigned int x, unsigned int y, char r) y >= v[x].size) return; //Index out of bounds.
std::queue<std::pair<unsigned int, unsigned int>> q;
q.push(std::make_pair(x, y)); //Push the initial position into the queue.
v[x][y] = r; //Replace the initial point.
while (!q.empty()) //Keep looking at relevant neighbours so long as there is something in the queue.
auto pt = q.top(); //Collect the first entry.
q.pop(); //Remove it, we don't want to keep processing the same point.
//Now add neighbours if they match our initial point.
if(pt.first > 0 && v[pt.first - 1][pt.second] == init)
q.push(std::make_pair(pt.first - 1, pt.second);
v[pt.first - 1][pt.second] = r; //Replace the value here to avoid pushing the same point twice.
if(pt.first + 1 < v.size() && v[pt.first + 1][pt.second] == init)
q.push(std::make_pair(pt.first + 1, pt.second);
v[pt.first + 1][pt.second] = r;
if(pt.second > 0 && v[pt.first][pt.second - 1] == init)
q.push(std::make_pair(pt.first, pt.second - 1);
v[pt.first][pt.second - 1] = r;
if(pt.second + 1 < v[pt.first].size() && v[pt.first][pt.second + 1] == init)
q.push(std::make_pair(pt.first, pt.second + 1);
v[pt.first][pt.second + 1] = r;
This gives you a BFS-like flood-fill pattern without recursion. Alternatively you could also use a stack
instead of the queue
, then the flood-fill would behave more like a DFS (much more similar to what the recursive pattern will do). It might even perform a little better than the queue, given the data structure is a little simpler.
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
Why do you iterate over the whole field in each recursion?
Normally, flood filling works as follows:
- You have a specific starting point.
- You fill this starting point with the colour intended
- 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.
Thank you, i wasn't using the ++ statement correctly, neither were my if statements, I guess i got carried off with something too deep to even work.
– xannax159
Nov 11 at 9:39
add a comment |
up vote
2
down vote
accepted
Why do you iterate over the whole field in each recursion?
Normally, flood filling works as follows:
- You have a specific starting point.
- You fill this starting point with the colour intended
- 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.
Thank you, i wasn't using the ++ statement correctly, neither were my if statements, I guess i got carried off with something too deep to even work.
– xannax159
Nov 11 at 9:39
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
Why do you iterate over the whole field in each recursion?
Normally, flood filling works as follows:
- You have a specific starting point.
- You fill this starting point with the colour intended
- 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.
Why do you iterate over the whole field in each recursion?
Normally, flood filling works as follows:
- You have a specific starting point.
- You fill this starting point with the colour intended
- 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.
edited Nov 11 at 9:41
answered Nov 11 at 9:28
Aconcagua
11.1k32142
11.1k32142
Thank you, i wasn't using the ++ statement correctly, neither were my if statements, I guess i got carried off with something too deep to even work.
– xannax159
Nov 11 at 9:39
add a comment |
Thank you, i wasn't using the ++ statement correctly, neither were my if statements, I guess i got carried off with something too deep to even work.
– xannax159
Nov 11 at 9:39
Thank you, i wasn't using the ++ statement correctly, neither were my if statements, I guess i got carried off with something too deep to even work.
– xannax159
Nov 11 at 9:39
Thank you, i wasn't using the ++ statement correctly, neither were my if statements, I guess i got carried off with something too deep to even work.
– xannax159
Nov 11 at 9:39
add a comment |
up vote
1
down vote
One option is to use recursion, as suggested by the other answer. However, personally I prefer avoiding recursion where it is not necessary. An alternative, is a queue-based approach.
void floodfill (std::vector<std::vector<char>>& v, unsigned int x, unsigned int y, char r) y >= v[x].size) return; //Index out of bounds.
std::queue<std::pair<unsigned int, unsigned int>> q;
q.push(std::make_pair(x, y)); //Push the initial position into the queue.
v[x][y] = r; //Replace the initial point.
while (!q.empty()) //Keep looking at relevant neighbours so long as there is something in the queue.
auto pt = q.top(); //Collect the first entry.
q.pop(); //Remove it, we don't want to keep processing the same point.
//Now add neighbours if they match our initial point.
if(pt.first > 0 && v[pt.first - 1][pt.second] == init)
q.push(std::make_pair(pt.first - 1, pt.second);
v[pt.first - 1][pt.second] = r; //Replace the value here to avoid pushing the same point twice.
if(pt.first + 1 < v.size() && v[pt.first + 1][pt.second] == init)
q.push(std::make_pair(pt.first + 1, pt.second);
v[pt.first + 1][pt.second] = r;
if(pt.second > 0 && v[pt.first][pt.second - 1] == init)
q.push(std::make_pair(pt.first, pt.second - 1);
v[pt.first][pt.second - 1] = r;
if(pt.second + 1 < v[pt.first].size() && v[pt.first][pt.second + 1] == init)
q.push(std::make_pair(pt.first, pt.second + 1);
v[pt.first][pt.second + 1] = r;
This gives you a BFS-like flood-fill pattern without recursion. Alternatively you could also use a stack
instead of the queue
, then the flood-fill would behave more like a DFS (much more similar to what the recursive pattern will do). It might even perform a little better than the queue, given the data structure is a little simpler.
add a comment |
up vote
1
down vote
One option is to use recursion, as suggested by the other answer. However, personally I prefer avoiding recursion where it is not necessary. An alternative, is a queue-based approach.
void floodfill (std::vector<std::vector<char>>& v, unsigned int x, unsigned int y, char r) y >= v[x].size) return; //Index out of bounds.
std::queue<std::pair<unsigned int, unsigned int>> q;
q.push(std::make_pair(x, y)); //Push the initial position into the queue.
v[x][y] = r; //Replace the initial point.
while (!q.empty()) //Keep looking at relevant neighbours so long as there is something in the queue.
auto pt = q.top(); //Collect the first entry.
q.pop(); //Remove it, we don't want to keep processing the same point.
//Now add neighbours if they match our initial point.
if(pt.first > 0 && v[pt.first - 1][pt.second] == init)
q.push(std::make_pair(pt.first - 1, pt.second);
v[pt.first - 1][pt.second] = r; //Replace the value here to avoid pushing the same point twice.
if(pt.first + 1 < v.size() && v[pt.first + 1][pt.second] == init)
q.push(std::make_pair(pt.first + 1, pt.second);
v[pt.first + 1][pt.second] = r;
if(pt.second > 0 && v[pt.first][pt.second - 1] == init)
q.push(std::make_pair(pt.first, pt.second - 1);
v[pt.first][pt.second - 1] = r;
if(pt.second + 1 < v[pt.first].size() && v[pt.first][pt.second + 1] == init)
q.push(std::make_pair(pt.first, pt.second + 1);
v[pt.first][pt.second + 1] = r;
This gives you a BFS-like flood-fill pattern without recursion. Alternatively you could also use a stack
instead of the queue
, then the flood-fill would behave more like a DFS (much more similar to what the recursive pattern will do). It might even perform a little better than the queue, given the data structure is a little simpler.
add a comment |
up vote
1
down vote
up vote
1
down vote
One option is to use recursion, as suggested by the other answer. However, personally I prefer avoiding recursion where it is not necessary. An alternative, is a queue-based approach.
void floodfill (std::vector<std::vector<char>>& v, unsigned int x, unsigned int y, char r) y >= v[x].size) return; //Index out of bounds.
std::queue<std::pair<unsigned int, unsigned int>> q;
q.push(std::make_pair(x, y)); //Push the initial position into the queue.
v[x][y] = r; //Replace the initial point.
while (!q.empty()) //Keep looking at relevant neighbours so long as there is something in the queue.
auto pt = q.top(); //Collect the first entry.
q.pop(); //Remove it, we don't want to keep processing the same point.
//Now add neighbours if they match our initial point.
if(pt.first > 0 && v[pt.first - 1][pt.second] == init)
q.push(std::make_pair(pt.first - 1, pt.second);
v[pt.first - 1][pt.second] = r; //Replace the value here to avoid pushing the same point twice.
if(pt.first + 1 < v.size() && v[pt.first + 1][pt.second] == init)
q.push(std::make_pair(pt.first + 1, pt.second);
v[pt.first + 1][pt.second] = r;
if(pt.second > 0 && v[pt.first][pt.second - 1] == init)
q.push(std::make_pair(pt.first, pt.second - 1);
v[pt.first][pt.second - 1] = r;
if(pt.second + 1 < v[pt.first].size() && v[pt.first][pt.second + 1] == init)
q.push(std::make_pair(pt.first, pt.second + 1);
v[pt.first][pt.second + 1] = r;
This gives you a BFS-like flood-fill pattern without recursion. Alternatively you could also use a stack
instead of the queue
, then the flood-fill would behave more like a DFS (much more similar to what the recursive pattern will do). It might even perform a little better than the queue, given the data structure is a little simpler.
One option is to use recursion, as suggested by the other answer. However, personally I prefer avoiding recursion where it is not necessary. An alternative, is a queue-based approach.
void floodfill (std::vector<std::vector<char>>& v, unsigned int x, unsigned int y, char r) y >= v[x].size) return; //Index out of bounds.
std::queue<std::pair<unsigned int, unsigned int>> q;
q.push(std::make_pair(x, y)); //Push the initial position into the queue.
v[x][y] = r; //Replace the initial point.
while (!q.empty()) //Keep looking at relevant neighbours so long as there is something in the queue.
auto pt = q.top(); //Collect the first entry.
q.pop(); //Remove it, we don't want to keep processing the same point.
//Now add neighbours if they match our initial point.
if(pt.first > 0 && v[pt.first - 1][pt.second] == init)
q.push(std::make_pair(pt.first - 1, pt.second);
v[pt.first - 1][pt.second] = r; //Replace the value here to avoid pushing the same point twice.
if(pt.first + 1 < v.size() && v[pt.first + 1][pt.second] == init)
q.push(std::make_pair(pt.first + 1, pt.second);
v[pt.first + 1][pt.second] = r;
if(pt.second > 0 && v[pt.first][pt.second - 1] == init)
q.push(std::make_pair(pt.first, pt.second - 1);
v[pt.first][pt.second - 1] = r;
if(pt.second + 1 < v[pt.first].size() && v[pt.first][pt.second + 1] == init)
q.push(std::make_pair(pt.first, pt.second + 1);
v[pt.first][pt.second + 1] = r;
This gives you a BFS-like flood-fill pattern without recursion. Alternatively you could also use a stack
instead of the queue
, then the flood-fill would behave more like a DFS (much more similar to what the recursive pattern will do). It might even perform a little better than the queue, given the data structure is a little simpler.
edited Nov 11 at 9:53
answered Nov 11 at 9:46
Qubit
972215
972215
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53247243%2fhow-should-i-implement-a-flood-fill-function-to-my-c-program%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Rather than iterate over the entire
vector<vector<char>>
when floodfilling, why not start at x,y and branch out adjacently until no more matches are found? Also, your 5th argumento
is the character to search, not the new desired character.– Enfyve
Nov 11 at 9:27
1
So let me get this straight, all you want is to floodfill the area of x-s defined by the starting position with o-s?
– Qubit
Nov 11 at 9:28
@Qubit yes thats what im trying to do
– xannax159
Nov 11 at 9:31
'The int main [...] functions work as intended.' - only if user provides appropriate number of command line arguments, otherwise undefined behaviour (crash likely). Get used right from the start to check if they are available. Additionally, check the fail bits of your string streams for the case if user did not provide numbers. You could as well check if there are further characters left (to catch input like
7s
as invalid).– Aconcagua
Nov 11 at 9:47