C++ 3D vector "resizing"

Started by
7 comments, last by Zahlman 13 years, 7 months ago
The reason resizing is in quotes is because I'm not using vector.resize(), I'm trying to push_back on a vector but my codes not working.

I have a 3D vector for my tile map. (layer)(width)(height) In my tile map editor I want the ability to add rows and columns to the map by copying the row or column on the outside. I've successfully done this for the second dimension of the vector but I can't get it to work with the final dimension. My two functions are as follows (using C++):

void add(bool f)	{		if(f)//add Column this works		{			for(int i = 0; i < map.size(); i ++)			{map.at(i).push_back(map.at(i).back());}		}		else//add Row this doesn't		{			for(int i = 0; i < map.size(); i ++)			{				for(int j = 0; j <map.at(i).size(); j ++)				{					map.at(i).back().push_back(map.at(i).back().back());				}			}		}	}	void subtract(bool f)	{		if((f) && (map.at(0).size() != 1))//sub Column this works		{			for(int i = 0; i < map.size(); i ++)			{map.at(i).pop_back();}		}		else if((!f) &&(map.at(0).at(0).size() != 1))//sub Row this doesn't		{			for(int i = 0; i < map.size(); i ++)			{					for(int j = 0; j < map.at(i).size(); j--)				{					map.at(i).back().pop_back();				}			}		}	}


What am I doing wrong? Is the code correct and code else where is the reason it's not working?
Advertisement
just noticed the j-- in the for loop, already fixed that. Still having problems.

EDIT: Fixed it. Sorry for the wasted post :(

For anyone who happens upon this similar issue, I fixed it with :

void add(bool f)	{		if(f)//add Column		{			for(int i = 0; i < map.size(); i ++)			{map.at(i).push_back(map.at(i).back());}		}		else//add Row		{			for(int i = 0; i < map.size(); i ++)			{				for(int j = 0; j <map.at(0).size(); j ++)				{					map.at(i).at(j).push_back(map.at(i).back().back());				}			}		}	}	void subtract(bool f)	{		if((f) && (map.at(0).size() != 1))//sub Column		{			for(int i = 0; i < map.size(); i ++)			{map.at(i).pop_back();}		}		else if((!f) &&(map.at(0).at(0).size() != 1))//sub Row		{			for(int i = 0; i < map.size(); i ++)			{					for(int j = 0; j < map.at(0).size(); j++)				{					map.at(i).at(j).pop_back();				}			}		}	}
Seeing nested std::vector's makes me cringe, so let me just point out boost::multi_array. You may have reasons for doing it this way, so I'll just stop there.
The term 3D vector is misleading. You're actually trying to implenent a multidimensional array, here, a 3-dimensional array. The notions of row and column are only meaningful in 2-dimensional arrays. Unless, this is what you mean by row i, column j:

std::vector< std::vector< std::vector< Tile > > > map;//map initialisationint i = 0; //row indexint j = 0; //colum indexint k = 0; //tile index in vector of tiles//we access the kth element (tile) of the jth colum of the ith rowmap[j][k] = Tile(...); 


You probably want to be able to add tiles to your map at run time. Now, I wonder why would you want to be able to do that?

Nevertheless, here is how it could be done:
//10 is the number of std::vector< std::vector< Tile > >sstd::vector< std::vector< std::vector< Tile > > > map( 10 );//we want the fourth std::vector< std::vector< Tile > > to contain 3 elements//that is to say 3 std::vector< Tile >smap[3].resize( 3 );//we want to add a tile to the second element of map[3]map[3][1].push_back( Tile(...) );


After reading what you're trying to achieve:
-instead of using a boolean to either add what you call a column or a "row", write two functions, one for adding a "column", the other, for adding a "row"
-What you do when you add a "row", could be done this way:

std::vector< std::vector< std::vector< Tile > > > map;//map initializationfor( int unsigned i = 0; i < map.size(); ++i ){   map.push_back( std::vector< Tile >() );}

-What you do when you add a "column", could be done this way

    for( size_t i = 0; i < map.size(); ++i )    {         for( size_t j = 0; j < map.size(); ++j )         {              map[j].push_back( Tile() );         }    }


[Edited by - johnstanp on August 28, 2010 5:41:41 AM]
Quote:Original post by incin
Seeing nested std::vector's makes me cringe, so let me just point out boost::multi_array. You may have reasons for doing it this way, so I'll just stop there.


I'm using vectors because when I was using 2d arrays and 3d arrays, the size of the array had to be hard coded to pass it to a function. ex:

int tile[][];function(tile); 


produced errors because in the function definition, I would have to say

void function(int tile[][size]);


to pass it. I want the size to be dynamic. I could be completely wrong on how multi dimensional arrays work but that's what I saw in the googles so I switched to vectors.

Quote:Original post by incin The term 3D vector is misleading. You're actually trying to implenent a multidimensional array, here, a 3-dimensional array. The notions of row and column are only meaningful in 2-dimensional arrays. Unless, this is what you mean by row i, column j


i = layer, j = row.

I have a 3d vector for my tile map because I wanted a layered map. (Layer 0 is for grass and stuff, layer 1 is for buildings and sign posts, etc.) So it's a vector of 2d vectors. The 2d vectors are rows and columns, holding the index of the tile to be placed (or -1 for no tile). I figured loading up the tile set into memory and holding a few ints is better than holding copies of the same tile over and over again. (Bitmaps are bigger than ints I'm almost certain.)

Also the code you provided does add rows and columns, however it doesn't copy the outer row or column and make it the new one. I could fill it with -1 for my set up but I thought if the ground is covered in grass, the user of the tile editor probably wants the rest of the map to be as such and if not, they can delete it.

Thank you for your responses, if you still believe I'm going about this the wrong way, please inform me. I want my code to be as slim as possible. I'm at the point in programming where I can do anything, it will just probably look scary as code.
Quote:Original post by slynk
What am I doing wrong?


What you are doing wrong is that you are not using boost::multi_array for storage.
Do you have a link or a list to what the benefits are to using boost::multi_array over vectors? I'm willing to try them if I'm given a concrete reason as to why they are better. If they are just more user friendly, I think I'd prefer not to convert all my functions and classes to use them.
Quote:Original post by slynk
Do you have a link or a list to what the benefits are to using boost::multi_array over vectors? I'm willing to try them if I'm given a concrete reason as to why they are better. If they are just more user friendly, I think I'd prefer not to convert all my functions and classes to use them.
Typically, using multi_array (or a similar array wrapper) rather than nested vectors for non-jagged multi-dimensional arrays will make your code more clear and more expressive of its intent, which, IMO, is reason enough to do so.

Although this may or may not matter in your case, using a wrapper such as multi_array that stores the data in a contiguous block can be better for performance, due to increased cache coherency and (possibly) decreased fragmentation.

Also, nested vectors are just messy and inelegant :|

If you're going to be inserting and removing rows and columns frequently, that will require a little work regardless of what method you're using for storage. Granted, using nested vectors does make the special case of adding or removing rows or columns (one or the other, depending) relatively straightforward to handle, but it's still not going to be particularly efficient. IMO, using multi_array would still be preferable in this case.
Quote:Original post by slynk
Do you have a link or a list to what the benefits are to using boost::multi_array over vectors? I'm willing to try them if I'm given a concrete reason as to why they are better. If they are just more user friendly, I think I'd prefer not to convert all my functions and classes to use them.


Here's why. (Not tested, but should be fairly close.)

typedef boost::multi_array<Element, 3> Map; // indexed as [layer][column][row]typedef Map::array_view<2>::type Slice;typedef boost::gen_type<3, 2>::type Slicer;const boost::index_range all;using boost::indices;using boost::extents;void adjust_size(Map& m, size_type x, size_type y, size_type z) {  m.resize(extents[m.shape()[0] + x][m.shape()[1] + y][m.shape()[2] + z]);}void copy_slice(Map& m, const Slicer& src, const Slicer& dest) {  Slice src_slice = m[src];  Slice dest_slice = m[dest];  std::copy(src_slice.begin(), src_slice.end(), dest_slice.begin());}void duplicate_last_layer(Map& m) {  adjust_size(m, 1, 0, 0);  int size = m.shape()[0];  copy_slice(indices[size - 2][all][all], indices[size - 1][all][all]);}void duplicate_last_column(Map& m) {  adjust_size(m, 0, 1, 0);  int size = m.shape()[1];  copy_slice(indices[all][size - 2][all], indices[all][size - 1][all]);}void duplicate_last_row(Map& m) {  adjust_size(m, 0, 0, 1);  int size = m.shape()[2];  copy_slice(indices[all][all][size - 2], indices[all][all][size - 1]);}void remove_last_layer(Map& m) {  adjust_size(m, -1, 0, 0);}void remove_last_column(Map& m) {  adjust_size(m, 0, -1, 0);}void remove_last_row(Map& m) {  adjust_size(m, 0, 0, -1);}

This topic is closed to new replies.

Advertisement