I'm making a game with C++ and SFML and was wondering if there's a way to iterate through specific elements in a vector. I have a vector of tiles which makes up the game world, but depending on the game map's size, (1000 x 1000 tiles) iterating through all of them seems very inefficient. I was wondering if there was a way to say "for each tile in vector of tiles that (fits a condition)". Right now, my code for drawing these tiles looks like this:
void Tile::draw()
{
for (const auto& TILE : tiles)
{
if (TILE.sprite.getGlobalBounds().intersects(Game::drawCuller.getGlobalBounds()))
{
Game::window.draw(TILE.sprite);
}
}
}
As you can see, I'm only drawing the tiles in the view (or drawculler). If the vector is too large, it will take a really long time to iterate through it. This greatly impacts my fps. When I have a 100 x 100 tile map, I get around 800 fps, but when I use a 1000 x 1000 tile map, I get roughly 25 fps due to the lengthy iteration. I know that I could separate my tiles into chunks and only iterate through the ones in the current chunk, but I wanted something a little easier to implement. Any help would be appreciated :)
Given the following assumptions:
Your tiles are likely arranged on a regular grid with a (column, row) index.
Your tiles are likely inserted into your vector in row-major order, and is also likely fully-populated. So the index of a tile in your vector is likely (row * numColumns + column).
Your view is likely axis-aligned to the grid (where you can't rotate your view - as is the case with many 2d tile-based games)
If those assumptions hold true, then you can easily iterate through the appropriate range of tiles with a nested loop.
for (int row = minRow; row <= maxRow; ++row) {
for( int column = numColumn; column <= maxColumn; ++column) {
int index = row * numColumns + column;
// Here you can...
doSomethingWith(tiles[index]);
}
}
This just requires that you can compute the minRow, maxRow, minColumn, and maxColumn from your Game::drawCuller.getGlobalBounds(). You haven't disclosed the details, but it's likely something like a rectangle in world coordinates (which might be in some units like meters). It's likely either a left, top, width, height style rectangle or a min, max style bounds rectangle. Assuming the latter:
minViewColumn = floor((bounds.minInMeters.x - originOfGridInMeters.x) / gridTileSizeInMeters);
maxViewColumn = ceil((bounds.maxInMeters.x - originOfGridInMeters.x) / gridTileSizeInMeters);
// similarly for rows
minViewRow = floor((bounds.minInMeters.y - originOfGridInMeters.y) / gridTileSizeInMeters);
maxViewRow = ceil((bounds.maxInMeters.y - originOfGridInMeters.y) / gridTileSizeInMeters);
The originOfGridInMeters is the global coordinates of top-left corner of the tile at (row=0, column=0), which may very well be (0, 0), conveniently, if you set up your world like that. And gridTileSizeInMeters is, well, just that; presumably your tiles have a square aspect ratio in world space.
If the view is permitted to go outside the extents of the tile array, minViewColumn, (and the other iterator ranges) may now be less than 0 or greater than or equal to the number of columns in your tile array. So, it would then be necessary to compute minColumn from minViewColumn by clipping it to the range of tiles stored in your grid. (Same goes for the other iteration extents.)
// Clip to the range of valid rows and columns.
minColumn = min(max(minViewColumn, 0), numColumns - 1);
maxColumn = min(max(maxViewColumn, 0), numColumns - 1);
minRow = min(max(minViewRow, 0), numRows - 1);
maxRow = min(max(maxViewRow, 0), numRows - 1);
Now do that loop I showed you above, and you're good to go!
I was wondering if there was a way to say "for each tile in vector of tiles that (fits a condition)
In general, no. The only way to know if an element fits a condition is to look at it and see if it fits the condition. You can't do that without iterating over all the elements and checking the condition for each.
The way to avoid this is to build some sort of index structure. For instance, if you have tiles with attributes that change rarely, you could pre-build vectors of pointers to all of your tiles with some attribute. That way you can check the condition only once (or rarely) instead of on each frame. For instance you could build separate vectors of all of your blue tiles, all of your red tiles, and all of your green tiles. Then if you want to iterate over all of the tiles of a certain color you could do "for each blue tile" directly instead of "for each tile, if it's blue". This generally trades storage/memory usage for execution speed.
The same concept applies to your specific situation, as you mentioned. You can pre-build caches of chunks, and quickly filter out whole chunks that aren't near your camera. This will prevent you from having to check every tile to see if it's in view.
Related
I have a 2d array of depth values and need a fast way to find the maximum value within a given rectangular region. Many rectangles will be tested against a given depth buffer so a reasonable preprocess step is acceptable.
The naive approach would be to scan over each pixel in the rectangle keeping track of the max, requiring width * height iterations.
By first creating a quadtree of the depth buffer where each parent node contains the maximum value of its children the complexity can be reduced to approximately width + height iterations. This method is good but i would like to know if it can be done even faster.
I have given an example of a method for finding the average value, rather than the max value in constant time by using a linear time preprocess here.
Does anyone know of a similar technique for finding the maximum value?
Yes, you can generalize the trick of the average, but only for small color depths, for instance 8 Bit depth (0-255). Assume you have k colors (or different depth values).
For your reference, here is a good explanation for the mean calculation of a rectangle through integral images Viola/Jones CVPR 2001, see Section 2.1.
My generalized algorithm is to precompute the integral of a vector with dimension k how often do color/depth values occur. From this vector, you can take the same differences as in the trick to compute the mean. This gives you not only the maximum value within a rectangle region, but really a vector of the histogram within that rectangle within constant time. Of course, the histogram allows you to extract the maximum (or minimum, or other quantile).
Time and memory requirements of course grow with the number of colors, I think the complexity class is O(k) for lookup and O(k * width * height) for pre-computation.
(I would be interested if my idea has previously been used.)
Pad the array out to a square power of 2, by adding zeroes. Then pyramidize it into a stack, shrinking by a factor of 2 in each dimension each time. At each level, the value of each element is equal to the max of the 4 corresponding elements in the next largest array in the pyramid. This will take an extra 1/3 storage space on top of the padded array size, like mip-maps do.
Write a recursive function that takes a single element of a given array level and checks if the query region covers the bounds covered by it. If not then it checks each region in the next largest array that overlaps the query region, returns the recursively-computed max of each.
Pseudo-code:
int getMax(Rect query_rect, int level, int level_x, int level_y)
{
int level_factor = 1 << level;
// compute the area covered by this array element:
Rect level_rect(level_x * level_factor, level_y * level_factor,
(level_x + 1) * level_factor, (level_y + 1) * level_factor);
// if the regions don't overlap then ignore:
if(!query_rect.intersects(level_rect))
return -1;
// query rect entirely contains this region, return precomputed max:
if(query_rect.contains(level_rect))
return pyramid[level][level_x][level_y];
if(level == 0)
return -1; // ran out of levels
int max = getMax(query_rect, level - 1, level_x * 2, level_y * 2);
max = maxValue(max, getMax(query_rect, level - 1, (level_x * 2) + 1, level_y * 2);
max = maxValue(max, getMax(query_rect, level - 1, level_x * 2, (level_y * 2) + 1);
max = maxValue(max, getMax(query_rect, level - 1, (level_x * 2) + 1, (level_y * 2) + 1);
return max;
}
Call it at the top level (the capstone 1x1 array in the pyramid) initially:
int max = getMax(query_rect, 10, 0, 0);
There will be a minimum size of query rectangle, below which it will be cheaper just to iterate over all the elements. You could adapt this to work with non-square arrays, it will just require some extra tests against the array size at each level when recursing.
I have a set of objects(each object contains a rectangle and a value assigned to it) which is kept in a vector container.
See picture below:
I need to create a matrix by drawing horizontal and vertical lines at each y/x lower left (LL) / upper right(UR) coordinate like below:
And I need to assign value = 0 to each new empty rectangle, and to other rectangles which are inside of initial rectangles, I need to assign their old values.
I've implemented this with some naive algorithm but it works too slow when I have huge number of rectangles. My algorithm basically does the following:
- Stores all rectangles in a map container. Each element of the map contains set of rectangles with the same LL Y coordinate and they are sorted by LL X coordinate, i.e. key is LL Y coordinate.
- Stores all X/Y coordinates in set containers.
- Iterates over Y/X coordinate containers, and for each new rectangle finds out if it exists in map or not, if exists-assigns existing value to it, otherwise-assigns 0 value. I.e, for each new rectangle it looks for its LL Y coordinate in map, if such Y exists, then searches through the corresponding value(set of rectangles), otherwise-it searches in a whole map.
Is there an effective algorithm to get needed results?
For n rectangles this can be solved easily in O(n^3) time (or just O(n^2) time if at most a bounded number of rectangles intersect) by looking at the problem a different way. This should be adequate for handling up to thousands of rectangles in a few seconds.
Also, unless some other constraints are added to the problem, the latter time bound is optimal: that is, there exist inputs consisting of n non-intersecting rectangles for which O(n^2) smaller grid rectangles will need to be output (which of course requires O(n^2) time). An example such input is n width-1 rectangles, all having equal bottommost y co-ord and having heights 1, 2, ..., n.
Grid size bounds
First of all, notice that there can be at most 2n vertical lines, and at most 2n horizontal lines, since each input rectangle introduces at most 2 of each kind (it may introduce less if one or both vertical lines are also the edge(s) for some already-considered rectangle, and likewise for horizontal lines). So there can be at most (2*n - 1)^2 = O(n^2) cells in the grid defined by these lines.
The grid cell co-ordinate system
We can invent a co-ordinate system for grid cells in which each cell is identified by its lower-left corner, and the co-ordinates of an intersection of two grid lines is given simply by the number of horizontal grid lines below it and the number of vertical grid lines to its left (so that the bottommost, leftmost grid cell has co-ords (0, 0), the cell to its right has co-ords (1, 0), the cell two cells above that cell has co-ords (1, 2), etc.)
The algorithm
For each input rectangle having LL co-ords (x1, y1) and UR co-ords (x2, y2), we determine the horizontal and vertical intervals that it occupies within the new grid co-ordinate system, and then simply iterate through every cell (i, j) belonging to this rectangular region (i.e., every grid cell (i, j) such that toGridX(x1) <= i < toGridX(x2) and toGridY(y1) <= j < toGridY(y2)) with a nested for loop, recording in a hashtable that the ID (colour?) for the cell at (i, j) should be the colour of the current input rectangle. Input rectangles should be processed in decreasing z-order (implicitly at least there seems to be such an order, from your example) so that for any cell covered by more than one input rectangle, the hashtable will wind up recording whatever the "nearest" rectangle's colour is. Finally, iterate through the hash table, converting each grid co-ord pair (i, j) back to the LL and UR co-ords of the input-space rectangle that corresponds to this grid cell, and output this rectangle with the ID given by the value for this hash key.
Preprocessing
In order to accomplish the above, we need two things: a way to map input-space co-ordinates to grid co-ordinates (to determine the horizontal and vertical grid intervals for a given input rectangle), and a way to map grid co-ordinates back to input-space co-ordinates (to generate the output rectangles in the final step). Both operations are easy to do via that old workhorse, sorting.
Given any corner (x, y) of some input rectangle, the grid x co-ordinate corresponding to x, toGridX(x), is simply the rank position of x within the sorted list of all distinct x positions of vertical edges that are present among the input rectangles. Similarly, toGridY(y) is just the rank position of y within the sorted list of all distinct y positions of horizontal edges that are present among the input rectangles. In the other direction, for any grid co-ordinate (i, j), the corresponding input-space x co-ordinate, fromGridX(i), is simply the i-th smallest x co-ord (ignoring duplicates) of any vertical edge among the input rectangles, and similarly for fromGridY(j). These can all be computed as follows (all array indices start at 0, and I show only how to do it for x co-ords; y co-ords are similar):
For each rectangle i in the input having LL co-ords (x1, y1) and (x2, y2):
Append the two-element array [x1, i] to the list-of-arrays VERT.
Append the two-element array [x2, i] to the list-of-arrays VERT.
Sort the list VERT in increasing order by its first item.
Combine elements in VERT having identical x co-ords. Specifically:
Set j = 0.
For i from 1 to n-1:
If VERT[i][0] == VERT[j][0] then append VERT[i][1] to VERT[j] (thereby forming an array of length 3 or more at position j), otherwise set j = j + 1 and overwrite VERT[j] with the two-element array VERT[i].
Delete VERT[j+1] and all later elements from VERT.
By this time, for any i, VERT[i] is an array that contains (in its second and subsequent positions) the IDs of every input rectangle that uses, as either its left or right edge, the ith-leftmost distinct vertical line used by any input rectangle -- or in other words, the rank-i vertical line. We now "invert" this:
For i from 0 to n-1:
For j from 1 to length(VERT[i])-1:
Set toGridX[VERT[i][j]] = i.
For i from 0 to length(VERT)-1:
Set fromGridX[i] = VERT[i][0].
Running time
As previously established, there are at most O(n^2) grid cells. Each of the n input rectangles can occupy at most all of these cells, each of which is visited once per input rectangle, for a time bound of O(n^3). Note that this is an extremely pessimistic time bound, and for example if none (or none but a bounded number) of your rectangles overlap, then it drops to O(n^2) since no grid cell will ever be visited more than once.
I suspect the lookups and iterations are not fast enough. Things like 'otherwise it searches the whole map' point out that you do very heavy computations.
What I think you need is to use a 2d datastructure. A k-d tree or a BSP would work but the easiest to understand and implement would be a quad tree.
In a quad tree each node represents a rectangle in your space. Each node can be split into 4 children by selecting the mid point along the 2 dimensions and having the children represent the 4 resulting rectangles. Each node also holds the value that you want to assign to the area and an extra flag if the value is uniform.
To mark a rectangle with some value, you start from the root and recursively:
If the input rectangle covers the node rectangle you set the value to that node, mark it as uniform and return.
If the input rectangle and the node rectangle don't touch just return.
If the node is marked as uniform, copy the value to it's children and mark the node not uniform.
Recursively call for the 4 children (you might have to create them).
On the way back, check if the 4 children have the same value and are all marked as uniform and if so mark the node as uniform and set the same value as the children.
The main advantage of this approach is that you get to mark large areas of your map quickly. You can also prove that marking a area is O(logN) where N is the size of your map (with a larger constant than the usual tree).
You can find a more detailed explanation and some helpful images on wikipedia.
Assuming you know the top- and bottom-most y and the left- and right-most x, extend the four vectors belonging to each rectangle to the respective max and min x and y points. Keep a set of extended vertical vectors and a set of extended horizontal ones. Whenever an extended vector is added, it will necessarily intersect with each vector in the perpendicular list - the intersections are the cell coordinates of the matrix.
Once the list of cell coordinates is made, iterate over them and assign values appropriately, looking up if they are in or out of an original rectangle. I'm not too versed in data structures for rectangles, but it seems to me that two interval trees, one for horizontal, the other for vertical could find that answer in O(log n) time per query, where n is the number of intervals in the tree.
All together, this method seems to be O(n * log m) time, where n is the number of cell coordinates in the resultant matrix and m is the number of original rectangles.
Some sample code about image processing using OpenCV give somethings like this:
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
if(pointPolygonTest(Point(i,j),myPolygon))
{
// do some processing
}
}
}
In the iteration, why we need to start from height and width? and also why the Point is store (height, width) so that is -> (y,x) ?
Ranges between [0..Height] and [0..Width] are maximum boundaries of your working area.
This code is testing which pixels of whole image are inside the polygon myPolygon.
The word "whole" means you should check all pixels of your image so you should iterate from 0 to height for Y, and iterate from 0 to width for X.
Actually here, the row/column convention is used to iterate over the whole image.
height = Number of Rows
width = Number of Columns
The image is being accessed row wise.The outer loop is iterating over rows of the image and the inner loop is iterating on columns. So basically i is the current row and j is the current column of the image.
The inner loop processes a complete row of the image.
I am writing a dots and boxes program for a class and have most everything ready to go except I am having trouble printing the grid.
Note: The grid can be any size from 2x2 to 9x9. A two by two grid must print like this to standard out:
a + + + //after some moves: a +-+ +
|P|
b + + + b +-+ +
c + + + c + + +
1 2 3 1 2 3
I have a data structure for the dots, edges, and boxes. and the grid object has a one dimensional vector for each class.
ie dots is a vector of all of the points on the grid,
edges is a vector of all the edges on the grid (Each edge has two dots)
boxes is a vector of all the boxes on the grid (each box has four edges)
the boxes have a enum for who owns the box Player or Computer
and the edges have an bool for if they are taken or not, and also a bool for if they are vertical or not.
I am getting confused when I am trying to print the grid since the grid can be multiple sizes.
Since (size) edges print on the even(horizontal) rows, and (size+1) on the odd(vertical).
I hope I am explaining this clearly.
Thanks!
Take your edges and lay it out in a sequence (a vector).
gridsize = 2 : hh vvv hh vvv hh
This is what you noticed with horizontal = 2 and vertical = size + 1.
Now figure out the algorithm to get the different values for the different edges. Here is how I stored the edges into a vector based on your example:
Your first (empty) 2x2 grid: 00 000 00 000 00
Your second 2x2 grid: 10 110 10 000 00
When I printed out the grid, I used rows and columns in for loops.
// I consider the (row, col) to be the box.
// Each row loop prints one horizontal line and (possibly) one vertical line.
// Access to the edge vector is based on this (row, col) pair.
for (int row = 0; row < size + 1; row++) /* +1 to draw the bottom line. */
{
if (row < size) /* Bottom most horizontal row not followed by vertical. */
for (int col = 0; col < size + 1; col++) /* +1 to draw rightmost line. */
}
The edge going from b1 to b2 is stored in position 5 (counting from 0, count the possible edges). How do you get this position?
You may want to stop here and figure the rest out yourself. I use pen and paper!
The edge from b1 to b2 (according to my algorithm) is part of the second row, first column (1, 0). When printing the horizontal edges, here's how to calculate the edge's position in the vector.
Horizontal edge: pos = (row * (size + size + 1)) + col
The edge from a2 to b2 is part of the first row, second column (0, 2). When printing the vertical edges you need to adjust it
Vertical edge: pos = (row * (size + size + 1)) + size + col
I implemented an HTML+JS version of this game using a 2D model of just cells (each with a list of edges) plus a separate 2D view model of all four things (h,v,c,d). See here for code snippets and a link to the full code and playable game: https://stackoverflow.com/a/30387118/1593924 .
That has the benefit of separating UI state/concerns, which have to do with rendering, from the 'real data' in the underlying model, which needs to understand quite different things:
that each cell 'touches' four edges and shares up to two of them with other cells,
how a cell becomes 'filled', and
how to handle players and extra turns when a player fills one (or two!) cells in a given turn.
In fact, the UI can render all four kinds of elements--vertices, v-edges, h-edges, and cells--as rectangles in a single grid, though the behavior attached to them is different between cells and edges (and omitted for vertices).
I have an array that represents a grid
For the sake of this example we will start the array at 1 rather that 0 because I realized after doing the picture, and can't be bothered to edit it
In this example blue would have an index of 5, green an index of 23 and red 38
Each color represents an object and the array index represents where the object is. I have implemented very simple gravity, whereby if the grid underneath is empty x + (WIDTH * (y + 1)) then the grid below is occupied by this object, and the grid that the object was in becomes empty.
This all works well in its current form, but what I want to do is make it so that red is the gravity point, so that in this example, blue will move to array index 16 and then 27.
This is not too bad, but how would the object be able to work out dynamically where to move, as in the example of the green grid? How can I get it to move to the correct index?
Also, what would be the best way to iterate through the array to 'find' the location of red? I should also note that red won't always be at 38
Any questions please ask, also thank you for your help.
This sounds very similar to line rasterization. Just imagine the grid to be a grid of pixels. Now when you draw a line from the green point to the red point, the pixels/cells that the line will pass are the cells that the green point should travel along, which should indeed be the shortest path from the green point to the red point along the discrete grid cells. You then just stop once you encounter a non-empty grid cell.
Look for Bresenham's algorithm as THE school book algorithm for line rasterization.
And for searching the red point, just iterate over the array linearly until you have it and then keep track of its grid position, like William already suggested in his answer.
x = x position
y = y position
cols = number of columns across in your grid
(y * cols) + x = index in array absolute value for any x, y
you could generalize this in a function:
int get_index(int x, int y, int gridcols)
{
return (gridcols * y) + x;
}
It should be noted that this works for ZERO BASED INDICES.
This is assuming I am understanding what you're talking about at all...
As for the second question, for any colored element you have, you should keep a value in memory (possibly stored in a structure) that keeps track of its position so you don't have to search for it at all.
struct _THING {
int xpos;
int ypos;
};
Using the get_index() function, you could find the index of the grid cell below it by calling like this:
index_below = get_index(thing.x, thing.y + 1, gridcols);
thing.y++; // increment the thing's y now since it has moved down
simple...
IF YOU WANT TO DO IT IN REVERSE, as in finding the x,y position by the array index, you can use the modulus operator and division.
ypos = array_index / total_cols; // division without remainder
xpos = array_index % total_cols; // gives the remainder
You could generalize this in a function like this:
// x and y parameters are references, and return values using these references
void get_positions_from_index(int array_index, int total_columns, int& x, int& y)
{
y = array_index / total_columns;
x = array_index % total_columns;
}
Whenever you're referring to an array index, it must be zero-based. However, when you are referring to the number of columns, that value will be 1-based for the calculations. x and y positions will also be zero based.
Probably easiest would be to work entirely in a system of (x,y) coordinates to calculate gravity and switch to the array coordinates when you finally need to lookup and store objects.
In your example, consider (2, 4) (red) to be the center of gravity; (5, 1) (blue) needs to move in the direction (2-5, 4-1) == (-3, 3) by the distance _n_. You get decide how simple you want n to be -- it could be that you move your objects to an adjoining element, including diagonals, so move (blue) to (5-1, 1+1) == (4, 2). Or perhaps you could move objects by some scalar multiple of the unit vector that describes the direction you need to move. (Say, heavier objects move further because the attraction of gravity is stronger. Or, lighter objects move further because they have less inertia to overcome. Or objects move further the closer they are to the gravity well, because gravity is an inverse square law).
Once you've sorted out the virtual coordinates of your universe, then convert your numbers (4, 2) via some simple linear formulas: 4*columns + 2 -- or just use multidimensional arrays and truncate your floating-point results to get your array indexes.