I want to store a list of tiles (pointers to objects of class "Sprite") in a two dimensional vector.
Currently I'm storing all the sprites of my tilemap in a vector.
std::vector<Sprite*> _tiles;
Now I need to get all the neighbored tiles and I thought it be easier, to store my sprites in a 2d matrix (vector in vector) to do those caluclations.
But I can't figure out how to do that.
Header:
private:
std::vector<std::vector<Sprite*>> matrix;
C++ File:
vectorMatrix::vectorMatrix(int columns, int rows) { }
vectorMatrix::~vectorMatrix() { }
void vectorMatrix::addCellAt(int x, int y, Sprite* sprite) {
std::vector< std::vector<Sprite*> > matrix;
matrix[x][y].push_back(sprite);
}
But I get an error message if I use two index operators.
std::vector< std::vector<Sprite*> > matrix;
This is an empty std::vector of of std::vector, and as others have pointed out, it's no longer the data member matrix.
Since it's empty, it's not possible to do matrix[0] - get the first row, matrix[1] - get the second row... You can however allocate the size of the matrix in your constructor first -- e.g. This will give you a rows*columns matrix
vectorMatrix::vectorMatrix(int columns, int rows)
: matrix(rows, std::vector<Sprite*>(columns))
{
}
And then you can set the xth and yth element to the corresponding Sprite*, and of course x and y should be smaller than rows and columns
matrix[x][y] = sprite;
And don't declare matrix again inside the addCellAt, You can use the data members directly anywhere inside the class.
Why do you think it's a good idea to make your matrix a vector-of-vectors? True, this will "work" in the sense that vec_of_vec_matrix[i][j] will return the right thing, but it's somewhat cumbersome and inefficient.
Instead, consider basing your class on a single vector - of all data, as done in this answer (and probably in many libraries). Element access will be something like (for column-major data):
Sprite*& Matrix::operator()(size_t i, size_t j)
{
return mData[i * num_columns + j];
}
You could even arrange with map[x][y] to work, using a row proxy class.
Three problems:
You declare a local variable matrix inside the function, which shadows the member variable.
If the size of the vectors is not set to include the x and y indexes then you will go out of bounds.
matrix[x][y] is not itself a vector, it's an element that you can assign directly:
matrix[x][y] = sprite;
Remember to consider problem 2 before doing this.
Related
I am writing a program where I need to use the following data structure:
struct shape
{
std::vector<float> verts; // contains the x & y values for each vertex
char type; // the type of shape being stored.
float shapeCol[3]; // stores the color of the shape being stored.
float shapeSize; // stores the size of the shape if it is a line or point
};
In my main program I need a vector of type shape. How would I store values into the vector inside of the struct shapes using the the vector of struct shapes.
For instance, vector<shape> myshapes;
If I wanted to store a value into the first index of my verts vector, inside of my first index of my myshapes vector how would I do this?
in pseudo code it would look something like this, with i being the index:
myshapes[i].vector[i] = 4; // but I know this is incorrect
Would this be easier to implement using a STL list instead and if so what would that syntax look like?
Thanks for the help I am new to vectors so any advice would be appreciated.
vector supports the use of the [] operator. The syntax and semantics are very similar to using the [] operator with arrays. See: http://en.cppreference.com/w/cpp/container/vector/operator_at.
As with any struct member, you need to access it by name. myshapes[i].verts[j] = 4;.
The general advice given is to use std::vector as your default container of choice. Naturally if you have specific needs (like adding/removing items in the middle of the container) other containers may have better performance characteristics.
If your vector(s) start out empty, you'll have to add elements to them before you can index into them with operator[]. This is usually done with push_back (to add an existing shape object) or emplace_back (to construct a new shape object directly in the vector).
Given vector<shape> myshapes, you could add some shapes like this:
// add 10 shapes
for (size_t n = 0; n < 10; n++) {
shape s; // creates a new, blank shape object
// initialize the shape's data
s.type = ...;
s.shapeSize = ...;
// etc.
// add verts
s.verts.push_back(1.0f);
s.verts.push_back(2.0f);
s.verts.push_back(3.0f);
// etc.
// add the shape to the vector
myshapes.push_back(std::move(s));
}
(Since we're done with s on that last line, we can use std::move. This allows push_back to move the shape's data into the vector instead of copying it. Look up move semantics for more info.)
Once you have stuff in the vector, you can access elements by index like this:
myshapes[index of shape].verts[index of vertex within that shape]
Using [] with an invalid index or when the vector is empty invokes undefined behavior (don't do it or your program will crash/malfunction).
I'm programming a level editor for a new game. The problem is, I am not sure what structure to use for storing my data.
It is a tile-based map engine, using x- and y coordinates and an id for the tile at that position.
I've got multiple layers, the map is resizeable, so an array may cause me some troubles, that's why I chose a std::vector for that case.
To prevent a lot of overload I only add a tile, when somebody placed it, so the vector size is zero if there are no tiles and increases the more tiles are placed.
struct tile {
unsigned short tile_id;
unsigned short tile_x;
unsigned short tile_y;
};
And my vector:
std::vector<tile> tiles;
The thing is, before adding a new tile I need to check if there's already a tile at that x- and y-position.
// Returns true/false if there is a tile at given position or not
bool Layer::has_tile_at(unsigned short x, unsigned short y) {
unsigned int i;
for (i = 0; i < tiles.size(); i++) {
if (tiles[i].tile_x == x && tiles[i].tile_y == y)
return true;
}
return false;
}
My problem is, that for every placed tile, I must loop through the whole vector, which is fast at the beginning, but really gets a pain in the ass after some tiles have been placed.
Do you think my approach is okay so far, or is there something smarter and more performant?
Data structure to use should depend mostly on the use cases: if you're doing mostly (x,y) reads, then perhaps you need a matrix (be it via a vector of vectors, or just array of arrays).
If you need indexed access AND easy iteration over tiles, perhaps keep the data within two data structures. You should be able to easily implement a 2d map with pointers to tiles within vector - initially empty, lazily loaded upon (x,y) access (remember about data safety!).
Consider for example:
class Map
{
public:
void addTile(std::shared_ptr<Tile> tile)
{
m_tiles.push_back(tile); // should probably remove old elements at (x,y) from the vector
m_map[{tile->x, tile->y}] = tile; // overwrites whatever was stored in there!
}
std::shared_ptr<Tile> getTile(int x, int y)
{
return m_tilesMap[{x, y}]; // if no tile there, returns default ctor-ed shared_ptr: nullptr
}
private:
std::vector<std::shared_ptr<Tile>> m_tiles;
using Index2D = std::pair<int, int>;
std::map<Index2D, std::shared_ptr<Tile>> m_tilesMap;
};
(extended comment with a brief code example: data is kept on the heap, while both vector and map keep copies of it - perhaps could be improved for easier removal)
Why not use multiple vectors? You could essentially create a growable 2-D vector by having a vector of vectors, then overload the [] operator to first check if the vector size could contain that element (returning false if not), and if it can, check if that element isn't a constructed value (whatever your default "tile" may be). This would allow nearly O(1) lookup like in regular vectors. Otherwise you can use a formula for your max col/row distance and do a O(1) look-up that way with some 2-D to 1-D conversions such as in 2-D arrays.
This is what I'm thinking:
vector< vector<tile> > tiles;
bool::Layer has_tile_at(unsigned short x, unsigned short y) {
if (tiles.size() <= x) {
return false;
} else if (tiles[x].size() > y) {
if (tiles[x][y] != tile()) {
return true;
}
}
return false;
}
Edit:
As another user pointed out, you could also use pointers and check if tiles[x][y] == nullptr; instead!
I have a 2 dimensional structure of objects initialized as thus:
std::vector<std::shared_ptr<tile> > appearance;
for (int x = 0; x < building_data.x_width; x++)
{
appearance.push_back
(std::shared_ptr<tile>(new tile[building_data.y_length]));
}
now, as far as I can figure out, the only way to access a member function of a tile in this is to use
appearance.at(x).get()[y].member_function()
which is confusing and cumbersome, and I feel like I'm missing something.
Previously, I had used tile** for the same structure, and the syntax of
tile[x][y] was nice but the raw pointers were a headache.
So, is there a better way access functions of an object held in an array, where the first element in the array is pointed to by a smart pointer held in a vector? Wordy but its the best I have.
You can use the -> operator to access members of the object managed by the shared_ptr. It's the same syntax you use with raw pointers.
However, you're going to run into problems with delete as mentioned in Dantez's answer.
Also, it looks like you're building some sort of board of tiles, perhaps for a game? Have you considered replacing the multidimensional array with with a 1D vector and some accessor functions?
// board_width and height should be integers
std::vector<Tile> board;
board.reserve(board_width * board_height);
for (unsigned y_axis = 0; y_axis < board_height; ++y_axis)
{
for (unsigned x_axis = 0; x_axis < board_width; ++x_axis)
{
board.push_back(Tile());
}
}
...
vec2 index_to_coords(unsigned index)
{
return vec2(index % board_width, index / board_width);
}
...
unsigned coords_to_index(const vec2& coords)
{
return (static_cast<unsigned>(coords.y) * board_width) + static_cast<unsigned>(coords.x);
}
First of all, shared_ptr is not designed to work with arrays. When there is no more references, it calls delete instead of delete[] which results in undefined behaviour if managed object is an array. You can read about it here.
As for accessing shared_ptr object, you can use operator* to dereference it.
Also, if you know vector's final size, you may want to reserve some space to avoid reallocation.
I agree with Fibbles, but have an alternate idea to provide. Fibble's approach is actually quite common even in C, because multidimensional structures (matrices) are just much easier that way.
However, if you do insist on the two dimensional concept, you can nest vectors. Consider:
typedef std::vector< tile > TileRow;
typedef std::vector< TileRow > Tiles;
At first, this may be a bit confusing, so to be clear that creates:
std::vector< std::vector< tile > > t;
However, with the typedef, thats
Tiles t;
Now, that's empty. To use it you'd need to push in some rows, and for each row push in some columns. You might not like that, so...you can use the assign function to set some rows. If, for example, you needed a matrix of 10 rows by 10 columns, you might
t.assign( 10, TileRow( 10, tile() ) );
This assume tile has a default constructor, pushing 10 rows of TileRow, each with 10 columns of default constructed tiles.
Now, t[ 1 ] returns a reference to the row 1. As such, t[ 1 ][ 1 ] is a reference to the tile at location 1,1, much like an array.
Yet, now you have no allocation/deallocation issues.
Something similar can be done with std::array, even better.
typedef std::array< tile, 10 > TileRow;
typedef std::array< TileRow, 10 > Tiles;
Tiles t;
At which point, t is ready with default initialized tiles.
I have a struct and I am finding the beginning and end of the lines in an image and in those lines beginning and end of the word, letter etc. (Implementing a basic OCR.)
typedef struct _IMAGE {
int row;
int col;
int max_value;
int **line;
int **space;
int **word;
int **letter;
int **matrix;//holds the image pixels
} IMAGE;
I want to alter my code with dynamically allocated matrices. But since I don't know how many lines and words that will be I don't know the size at the beginning so cannot do simple dynamic allocation. Do you recommend for me to use vectors in this situation? If so how should I use it?
You clarified in a comment that the number of columns in the 2D arrays will be constant, whereas the number of rows can change.
Therefore, you can use 1D vectors and address the elements by mapping the (i, j) indexes to a single index with the formula: k = i * number_of_columns + j.
However, you said that the line array will have only 2 columns, one for the beginning o the line and one for the end. This makes me think that it is more convenient (especially for semantics) to have a Line struct/class with two fields (start, end) and put instances of this struct/class into a simple 1D vector.
When you design an array in which each element has a different meaning depending on its position, and the elements are few, I think it is better to devise an appropriate struct.
Last, since this is C++, you can declare structs in this way:
struct X
{
int a;
// ...
}
No professional image library out there holds image data in a matrix. You should simply do:
struct Image
{
int col;
std::vector<int> matrix;
};
And access the pixels like matrix[x+ col* y]. That will be the quickest, most memory-friendly layout for pixel data. Or use an already existing image struct to avoid the concept of NIH.
I have defined a struct
struct path{
char type;
bool visit;
bool inPath;
int level;
}
I have also defined a vector of vectors of this type struct
vector < vector<path> > spaceStation(numLevels*levelSize,vector<path> (levelSize));
I have two questions.
1) Have i defined the vector so that the number of rows pertain to (numLevels*levelSize) and columns pertain to levelSize
2) When accessing the individual elements of the vector, how can i set the elements of the struct inside it. I have tried using the .at() member function to little success
Re: 1
Yes. But I can't help feeling like you wanted to do this instead:
vector < vector<path> > spaceStation(numLevels,vector<path> (levelSize))
Note that using the term "rows" and "columns" is entirely in your imagination, concerning vectors. You just have a vector inside another vector. It's like an array of arrays - no special geometry implied.
Re: 2
Because you have a vector of vector, you need to use two indices, not just one:
spaceStation[level][pathindex].visit = true;
Where spaceStation[level] returns the vector at index level, which you then take the element at position pathindex (which is an instance of your struct), and finally modify a value in that struct.
For Q1, you are correct.
For example: a 4x4 dimension vector.
vector< vector< int > > vec(4, vector(4));
For Q2, to access the path, can't you do the following:
spaceStation[2][3] to access row 2 column 3 data, for example.
Then you can do:
spaceStation[2][3].visit to access elements inside your struct.