If i try to Fill a 100x100 rectangle then i get an overflow.
A 50x50 works fine.
Is there a way to fix the overflow?
I also print out the Stack Number and sometimes the working rectangle Stack is higher then the big one (it crashes around 7000).
void draw(int x, int y)
{
if ((x >= 0 && x < 100) && (y >= 0 && y < 100))
{
canvas.set_pixel(x, y);
if (!canvas.get_pixel(x, y + 1))draw(x, y + 1);
if (!canvas.get_pixel(x, y-1))draw(x, y - 1);
if (!canvas.get_pixel(x - 1, y))draw(x - 1, y);
if (!canvas.get_pixel(x+1, y))draw(x + 1, y);
}
return;
}
The reason you get a stack overflow is that the recursion goes too deep.
How deep will it go? Well, with the algorithm the way you designed it - it will actually go to depth 100*100=10,000!
Let's look at in what order will the canvas be filled - assuming the canvas is empty and we start filling from the middle:
set the middle pixel
go to x,y+1
do that until you get to the edge
at the edge - move to x-1,0 (remember, we're at the top)
go down until the bottom
etc. etc.
The point is - you're going deeper and deeper until you fill the canvas, and then have a "chain" of recursion calls going all around the canvas and it's a waste :)
Benjamin is right that you can use a stack, but a stack basically does the exact same thing (just without the recursion), so the stack will get to depth 10,000 as well. Still a waste, and you run out of memory in some cases (for a bitmap canvas, each pixel takes 1 bit, but the stack will have 2 integers per pixel for x,y, and thus could take 64 times more memory than the canvas)
Instead - use a queue! Almost the same code:
void draw(int x, int y)
{
struct coordinate { int x, y; };
std::queue<coordinate> to_draw; // <- changed from stack to queue
to_draw.push({x, y});
while (!to_draw.empty())
{
auto top = to_draw.front(); // <- changed from top to front
to_draw.pop();
if ( (top.x >= 0 && top.x < 100)
&& (top.y >= 0 && top.y < 100)
&& !canvas.get_pixel(top.x, top.y))
{
canvas.set_pixel(top.x, top.y);
to_draw.push({top.x, top.y + 1});
to_draw.push({top.x, top.y - 1});
to_draw.push({top.x + 1, top.y});
to_draw.push({top.x - 1, top.y});
}
}
}
And now the memory needed will be <=4*100! In other words - by changing from stack to queue we changed the memory needed from N*N to 4*N.
Don't use recursion. Instead, use a stack to store the coordinates you want to draw. And iterate until the stack is empty.
void draw(int x, int y)
{
struct coordinate { int x, y; };
std::stack<coordinate> to_draw;
to_draw.push({x, y});
while (!to_draw.empty())
{
auto top = to_draw.top();
to_draw.pop();
if ( (top.x >= 0 && top.x < 100)
&& (top.y >= 0 && top.y < 100)
&& !canvas.get_pixel(top.x, top.y))
{
canvas.set_pixel(top.x, top.y);
to_draw.push({top.x, top.y + 1});
to_draw.push({top.x, top.y - 1});
to_draw.push({top.x + 1, top.y});
to_draw.push({top.x - 1, top.y});
}
}
}
Related
recently moved from C# to C++ so I'm new to pointers and references and so on.
I've a pointer-to-pointer array declared like this
enum Type
{
Void,
DeepWater,
Water,
... etc }
Tile::Type** tiles;
TileManager::TileManager(int width, int height)
{
this->tiles = new Tile::Type*[width];
for (int w = 0; w < width; w++)
{
tiles[w] = new Tile::Type[height];
for (int h = 0; h < height; h++)
{
tiles[w][h] = Tile::Type::Dirt;
}
}
}
Now I'm putting together a method that returns the neighbours of a cell in the tiles array and checking if each neighbour is not-equal to NULL.
However even when checking whether it's null or not seems to throw an error, so I'm stumped.
Tile::Type * TileManager::GetNeighbours(int x, int y)
{
Tile::Type neighbours[8];
if(tiles[x][y+1] != NULL)
neighbours[0] = tiles[x ][y + 1];
...etc
if (tiles[x - 1][y - 1] != NULL) //<-- Error fires here
neighbours[5] = tiles[x - 1][y - 1];
return neighbours;
}
I know why it's throwing the error but shy of checking X and Y to see if they go over the limit or below 0... I figure there's a more practical way to prevent this so thought I'd best ask.
Edit:
Thank you, user4581301. I found most of this code elsewhere and adapted it to reflect the changes you suggested.
std::array<Tile::Type, 8> TileManager::GetNeighbours(int c, int r)
{
std::array<Tile::Type, 8> neighbours;
const int y[] = { -1, -1, -1, 1, 1, 1, 0, 0 };// 8 shifts to neighbors
const int x[] = { -1, 0, 1, -1, 0, 1, -1, 1 };// used in functions
for (int i = 0; i < 8; ++i)// visit the 8 spaces around it
if (inField(r + y[i], c + x[i]))
neighbours[i] = tiles[r + y[i]][c + x[i]];
else
neighbours[i] = Tile::Type::Void;
return neighbours;
}
bool TileManager::inField(int r, int c)
{
if (r < 0 || r >= 25) return false;
if (c < 0 || c >= 25) return false;
return true;
}
tiles[x][y+1], if y is the maximum valid value, will not be NULL except by the grace of . This goes out of bounds and as soon as you go out of bounds all bets are off. You've invoked Undefined Behaviour and pretty much anything can happen. Even what you expected to happen.
The same applies to the reported crash site, tiles[x - 1][y - 1].
Edit: Left out solution. Not helpful.
The only way, short of taking off and nuking the entire site from orbit, is to test the index to make sure it does not puncture the array bounds before using the index on the array. You'll probably want a function to handle this.
void assign_if(Type & neighbour, int x, int y)
{
if(x >= 0 && x < width && y >= 0 && y < height)
neighbour = tiles[x][y];
}
and call it
assign_if(neighbours[0], x, y+1);
and later
assign_if(neighbours[0], x-1, y-1);
Edit: Stealing this from Bob__ for completeness
It is impossible to return a raw array from a function. The array goes out of scope and the pointer to it becomes invalid. Either pass in the array as another parameter or use a std::array or std::vector, both of which can be returned. Thanks to Copy Elision, a smart compiler will likely eliminate the copying costs.
Example:
std::array<Tile::Type, 8> TileManager::GetNeighbours(int x, int y)
{
std::array<Tile::Type, 8> neighbours;
...
return neighbours;
}
Edit by original poster. Here is my solution:
std::array<Tile::Type, 8> TileManager::GetNeighbours(int c, int r)
{
std::array<Tile::Type, 8> neighbours;
const int y[] = { -1, -1, -1, 1, 1, 1, 0, 0 };// 8 shifts to neighbors
const int x[] = { -1, 0, 1, -1, 0, 1, -1, 1 };// used in functions
for (int i = 0; i < 8; ++i)// visit the 8 spaces around it
if (inField(r + y[i], c + x[i]))
neighbours[i] = tiles[r + y[i]][c + x[i]];
else
neighbours[i] = Tile::Type::Void;
return neighbours;
}
bool TileManager::inField(int r, int c)
{
if (r < 0 || r >= 25) return false;
if (c < 0 || c >= 25) return false;
return true;
}
Edit: Caveat
This answer deals directly with solving the problem as asked. See the answer by Kaz for a description of a more practical solution that trades a bit of memory to completely eliminate the need for testing and generating the neighbours array.
The more "practical" way (shorter code that avoids conditional checks) is to create the tile array so that it's it contains an additional "border" of tiles around the valid area. If any tile position is in the valid area, then is valid and so is .
You can have a special type for the border tiles which only they have, and simply include those tiles in the "neighbors" list. If your world has walls, then the border can consist of wall material.
Needless to say, you must never ask for the list of neighbors of a border tile. This is ensured by logic such as not allowing a border tile to be the valid position for anything.
This tile is in the valid area within the border" is a condition that is easier to check, in fewer places, and your program can be structured so that this check is actually just a removable assertion (a check for a situation that should not happen if the program is correct, rather than a check for an expected situation).
In C and C++, we can displace the pointers so that position [0][0] is still the corner of the valid area, yet the out-of-bounds coordinates [-1][-1] are valid indices, as are [w][h].
Firstly, the column array is allocated two elements larger than necessary, and the pointer is the incremented by one. Then the columns are allocated two elements larger, and each pointer is incremented by one before being assigned into the main array.
When freeing the arrays with delete [], you have to remember to decrement each pointer by one.
I'm trying out some sample code for a bigger project, and I'm having trouble getting my rectangle to bounce between two lines.
function draw() {
print(frameCount)
background(255)
var x = 150 + frameCount;
rect(x,200,15,15);
line(150,0,150,400);
line(250,0,250,400);
if (x >= 250) {
background(255)
x = 350-frameCount;
rect(x,200,15,15);
line(250,0,250,400);
line(150,0,150,400);
} if (x <= 145) {
background(255)
x = 145 + (frameCount % 100);
rect(x,200,15,15);
line(250,0,250,400);
line(150,0,150,400);
}
}
I'm getting the feeling that after the first instance, it's disregarding the original if statement, which dictates a bounce to the left. I'm really not sure what's going wrong, and any help would be appreciated.
You probably just want to store the current position and speed in a set of variables, and then move the rectangle based on those. Here's an example:
var x = 0;
var speed = 1;
function draw(){
x += speed;
if(x < 0 || x > width){
speed *= -1;
}
background(64);
line(x, 0, x, height);
}
I've written a tutorial on this available here. That's for regular Processing, but the ideas are the same in P5.js.
I'm using a for loop to iterate through some arrays I've created representing regions that the mouse can hover over. Then when the loop confirms the mouse is in a region it saves the iteration variable to a public variable that is used later in the main function to highlight the region the mouse is over. The problem is that the for loop is not giving the right value for the first iteration through.
{
//mouse offsets
int x = 0, y = 0;
//if mouse moves
if (event.type == SDL_MOUSEMOTION)
{
//get the mouse co-ords
x = event.motion.x;
y = event.motion.y;
for (int grid = 0; grid <= sizeof(grid_region); grid++)
{
if ((x > grid_region[grid].x) && (x < grid_region[grid].x + GRID_WIDTH) && (y > grid_region[grid].y) && (y < grid_region[grid].y + GRID_HEIGHT))
{
//set highlight region
highlight = grid;
}
}
}
}
grid_region is is made via "int grid_region[9];" and the strange part is that when I later do a print statement to see what "highlight" is when it's in grid_region[0] is prints 72. How is it possible that the iteration variable becomes 72 at any point in the loop??? Any help here? I later use highlight to apply a sprite in the grid_region and it's being applied incorrectly so this is a problem.
sizeof(grid_region) is the size in multiples of char, not the number of elements.
That is, it is sizeof(int) * 9, not nine, and apparently your int is 8 chars wide since you ended up at 72.
You can loop to < sizeof(grid_region) / sizeof(grid_region[0]) or, better, step into the 21st century and use std::vector, or std::array if your compiler is hip enough.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Determine if two rectangles overlap each other?
Considering I have 2 squares for which I know the x and y positions and I also know the size, what would be the formula to use if I wanted to see if the objects collide with eachother.
if( ((shapeA->getX() - shapeA->getSize()) > (player->getX() - player->getSize())
&& (shapeA->getX() + shapeA->getSize()) < (player->getX() + player->getSize()))
&& (shapeA->getY() - shapeA->getSize() > player->getY() - player->getSize()
&& (shapeA->getY() + shapeA->getSize()) < (player->getY() + player->getSize()))
)
This works, but it works strange (not all the time). I must be missing something
It's very easy to check whether a rectangle intersects or touches another rectangle. Have a look at the following picture:
As you can see, two rectangles intersect if the intersections between ([x,x+a] and [X,X+A]) and ([y,y+b] and [Y,Y+B]) both aren't empty.
struct Rectangle{
bool intersects(const Rectangle&);
unsigned int a; //!< width of the rectangle
unsigned int b; //!< height of the rectangle
unsigned int x; //!< x position
unsigned int y; //!< y position
};
bool Rectangle::intersects(const Rectangle& oRectangle){
return (x < oRectangle.x + oRectangle.a) && // [x,x+a], [X,X+A] intersection
(oRectangle.x < x + a) && // [x,x+a], [X,X+A] intersection
(y < oRectangle.y + oRectangle.b) && // [y,y+b], [Y,Y+B] intersection
(oRectangle.y < y + b); // [y,y+b], [Y,Y+B] intersection
}
So your code should be
if(((shapeA->getX() + shapeA->getSize()) > (player->getX()) // x intersection
&& (shapeA->getX() < (player->getX() + player->getSize())) // x intersection
&& (shapeA->getY() < player->getY() + player->getSize() // y intersection
&& (shapeA->getY() + shapeA->getSize()) > player->getY()) // y intersection
)
You do wrong tests, try this:
int left_bound_A= shapeA->getX()-shapeA->getSize();
int right_bound_A= shapeA->getX()+shapeA->getSize();
int top_bound_A= shapeA->getY()-shapeA->getSize();
int bottom_bound_A= shapeA->getY()+shapeA->getSize();
int left_bound_B= shapeB->getX()-shapeB->getSize();
int right_bound_B= shapeB->getX()+shapeB->getSize();
int top_bound_B= shapeB->getY()-shapeB->getSize();
int bottom_bound_B= shapeB->getY()+shapeB->getSize();
if( left_bound_A < right_bound_B &&
right_bound_A > left_bound_B &&
top_bound_A > bottom_bound_B &&
bottom_bound_A < top_bound_B ) colide(shapeA,shapeB);
The general way is to test for shape intersection. If you implement a Box or Rectangle class, the code simplify to:
Box colision= intersect( shapeA->getBoundBox(), shapeB->getBoundBox() );
if( colision.have_positive_area() )
colide(shapeA,shapeB,colision);
Assuming that getX/Y gives the bottom left corner of the square,
shapeMinX = shapeA; shapeMaxX = shapeB;
if (shapeA()->getX() > shapeB()->getX())
swap (shapeMinX, shapeMaxX);
shapeMinY = shapeA; shapeMaxY = shapeB;
if (shapeA()->getY() > shapeB()->getY())
swap (shapeMinY, shapeMaxY);
collision = (shapeMinX->getX()+shapeMinX->size() >= shapeMaxX()->getX) || (shapeMinX->getY()+shapeMinY->size() >= shapeMaxY()->getY);
Yes, the way to check if two rectangles is simple.
Just as a suggestion, if you then want to compute all the possible intersection between the rectangles in a list of rectangles, preorder them by increasing x of their border and then start the comparison exploiting this relation. This question may be of help
Fast hiding of intersecting rectangles can be of interest
I have a problem with implementation of flood filling.
The task is to ask user to click on the white part of the image (indicating seed point), he want to fill with black. The operation should be done on the binary images. I'm using CImg library. I can't use recursive algorithm. I've came up with something but it is not working properly (the gap becomes black only in the seed point). I am not familiar with the queues at all, so maybe the problem is in their implementaion.
void floodfill(int x, int y, int c, int b, CImg <unsigned char>image)
{
//c-black
//b-white
CImg<unsigned char> kopia(image.width(),image.height());
for (int p=1; p<image.height()-1; p++)
{
for (int q=1; q<image.width()-1; q++)
{
kopia(p,q)=255; //setting kopia2 all white
}
}
queue <pair<int,int> > a;
int p;
if(image(x, y) == c)
{
cout<<"Already black"<<endl;
return;
}
else
{
a.push(make_pair(x, y));
while(!a.empty())
{
a.pop();
p=image(x+1, y);
if((p == b) && (x < image.width()))
{
a.push(make_pair(x+1, y));
kopia(x+1, y)=c;
image(x+1, y)=c;
}
p = image(x-1, y);
if((p == c) && (x > 0))
{
a.push(make_pair(x-1, y));
kopia(x-1, y)=c;
image(x-1, y)=c;
}
p=image(x, y+1);
if((p == b) && (y < image.height()))
{
a.push(make_pair(x, y+1));
kopia(x, y+1)=c;
image(x, y+1)=c;
}
p=image(x, y-1);
if((p == b) && (y > 0))
{
a.push(make_pair(x, y-1));
kopia(x, y-1)=c;
image(x, y-1)=c;
}
}
saving(kopia);
}
}
void hole (CImg <unsigned char>image)
{
CImgDisplay image_disp(image,"Click a point");
int c_x=0; //coordinates
int c_y=0;
while (!image_disp.is_closed())
{
image_disp.wait();
if (image_disp.button())
{
c_x=image_disp.mouse_x(); //reads coordinates indicated by user
c_y=image_disp.mouse_y();
}
}
floodfill(c_x, c_y,0,255,image);
}
1)
while(!a.empty())
{
x = a.front().first; //fixed as per ChristianRau's code
y = a.front().second; //fixed as per ChristianRau's code
a.pop();
You just popped the current x,y coordinates off the stack without looking at what they were.
2)
p = image(x-1, y);
if((p == c) && (x > 0))
Did you mean to check if it was white, like you did with the other directions?
3) The caller passes in black and white, what happens if part of the image is blue? Better would be to pass in the filling color (black), and wherever you have white, replace that with not-black.
Don't you realize that you are working with the same x and y all the time and that a.pop() doesn't return anything? std::queue::pop only pops the front of the queue, but doesn't return it. You have to query it beforehand using std::queue::front. So just add
x = a.front().first;
y = a.front().second;
right before a.pop() inside the while loop.
And by the way, you might also want to set image(x, y) (and maybe kopia(x, y)) to c at the beginning of the else block before pushing the initial pair, although it might also get set by its neighbours' iterations.
Also, there is a built-in function in CImg that does what you want : CImg::draw_fill().