C++: std::vector - std::find & std::distance query - c++

To give context to my question I will describe what it is I am ultimately trying to achieve - I am developing a game, I have a .obj model that I am using as my terrain and I must update the players height as they traverse the terrain (because the terrain is far from flat). I am achieving this currently by doing the following - when I load the mesh (terrain.obj) I store all its vertices (each vertex is a Vector3f object that has an x y and z value) in a std::vector<Vector3f> meshVertices, then every second in the "main game loop" I loop through every one of the Vector3f objects in meshVertices and check its x and z value against the players x and z value, if they are colliding I set the players height as the y value of the matched Vector3f object.
This system actually works and updates my players height as they traverse through the terrain, the only issue is this - my approach of checking every single mesh vertex against player position every second kills my frame rate, I need a much better system.
I will now describe my new optimized approach in the attempt of saving my frame rate - Firstly, when creating the mesh I don't just store each Vector3f vertex in a single std::vector<Vector3f>, I store the x y and z values of each Vector3f vertex of the mesh in three seperate std::vector<Vector3f>'s, named meshVerticesX, mechVerticesY and meshVerticesZ. These std::vector's can be seen in the following code:
for (Vector3f currentVertex : meshVertices) {
meshVerticesX.push_back((int)currentVertex.GetX());
}
for (Vector3f currentVertex : meshVertices) {
meshVerticesZ.push_back((int)currentVertex.GetZ());
}
for (Vector3f currentVertex : meshVertices) {
meshVerticesY.push_back((int)currentVertex.GetY());
}
Now every second I get the x and z value of the players position (casted to an int because I feel like making this system work with int values and not float values will be much easier for comparisons later) and then send them to functions that check to see if they exist in the before mentioned meshVerticesX and mechVerticesZ by returning a bool, the code responsible for this is as follows:
int playerPosX = (int) freeMoveObjects[0]->GetParent()->GetTransform()->GetPos()->GetX();
int playerPosZ = (int) freeMoveObjects[0]->GetParent()->GetTransform()->GetPos()->GetZ();
bool x = meshObjects[0]->checkMeshVerticesX(playerPosX);
bool z = meshObjects[0]->checkMeshVerticesZ(playerPosZ);
The functions checkMeshVerticesX and checkMeshVerticesZ are as follows:
bool Mesh::checkMeshVerticesX(int playerPosX)
{
return std::find(meshVerticesX.begin(), meshVerticesX.end(), playerPosX) != meshVerticesX.end();
}
bool Mesh::checkMeshVerticesZ(int playerPosZ)
{
return std::find(meshVerticesZ.begin(), meshVerticesZ.end(), playerPosZ) != meshVerticesZ.end();
}
Using the returned boolean values (true if the players position was in the respective std::vector or false if it was not) I then call another function (getMeshYHeight) that also gets passed the players x and z position that then checks the index of the respective std::vector's (meshVerticesX & meshVerticesZ) were the match was found, then checks if these indexes are equal and if so returns an int of that index from the meshVerticesY std::vector mentioned earlier, this code can be seen in the following:
if (x == true & z == true) {// boolean values returned by checkMeshVerticesX & checkMeshVerticesZ
int terrainVertexYHeight = meshObjects[0]->getMeshYHeight(playerPosX, playerPosZ);
freeMoveObjects[0]->GetParent()->GetTransform()->GetPos()->SetY(terrainVertexYHeight);
}
The function getMeshYHeight is as follows:
int Mesh::getMeshYHeight(int playerXPos, int playerZPos) {//15/2/20
auto iterX = std::find(meshVerticesX.begin(), meshVerticesX.end(), playerXPos) != meshVerticesX.end();
auto iterZ = std::find(meshVerticesZ.begin(), meshVerticesZ.end(), playerZPos) != meshVerticesZ.end();
int indexX = std::distance(meshVerticesX.begin(), iterX);
int indexZ = std::distance(meshVerticesZ.begin(), iterZ);
if (indexX == indexZ)
{
return meshVerticesY[indexX];
}
}
The idea here is that if the index from the meshVerticesX and meshVerticesZ std::vectors's for the original check match, then they must be the x and z values from an original Vector3f object when I first made the mesh as described earlier, and so that same index in meshVerticesY must contain that same Vector3f's objects y value, therefore return it and use it to set the players height.
The issue is that I cant even test if this works because the line of code int indexX = std::distance(meshVerticesX.begin(), iterX); gives an error saying the arguments supplied to std::distance are wrong (it says iterX is a bool instead of an int which is what I thought it would be).
So my question is - Firstly, if I diden't have this error would my approach even work? and if so, how can I fix the error?

I kind of lost track of your logic somewhere in the middle there, but to address the issue at hand: iterX is a bool!
auto iterX = std::find(...) != meshVerticesX.end();
In this statement, find returns an iterator, which you compare to another iterator, meshVerticesX.end(). The result of that expression (the comparison operator) is a bool, which is then assigned to iterX, so auto deduces that iterX needs to be of type bool.

could you convert your terrain's x,y coordinates to ints, you may need to scale it, then when you load the mesh you could just store the z value for every x,y point (you may have to take some sort of average over the 1x1 square). Now you don't have to look for collisions, instead for each object in the game you can just look up it's z value by it's (scaled) x,y coordinates.

Related

How do you compare elements of a vector within a 3 Dimensional vector in c++

I am trying to loop through a three-dimensional vector. I have one vector containing multiple vectors of x and y coordinates. Lets call the parent vector v0, the vector containing the coordinates v1, and the pair of coordinates v2.
For any v2 in any v1, if the x value of v2 is equal to the x value of another v2 in any v1, I want to add the y values of the v2. if a v2 doesn't have any matching y values, add it to the y value at the point in the next v1 (I already have a function that does this)
In the end I only have one v1 which I will render.
The size of a v1 is unknown, but will always have some v2 with the same x value as a v2 in another v1
The context is essentially I have a set of graphs that I want to "add" to make fractal noise. I am successfully generating the set of graphs, but am unsure how to add them.
Here is my code so far
(also for those unfamiliar with sfml, sf::vector2f is just a vector of two floats.
These are the coordinates. you can access the value of a coordinate with vec.y/vec.x)
void Noise() {
std::vector<sf::Vector2f> finalGraph;
std::vector<sf::Vector2f> singleNoise;
std::vector < std::vector<sf::Vector2f>> allGraphVec;
float persistance = 0.5;
int nOOPM1 = 4;
//generates the graphs to be added
for (int i = 0; i < nOOPM1; i++) {
float frequency = pow(2, i);
float amplitude = pow(persistance, i);
singleNoise =
this->generateNoise(frequency, 300 * amplitude);
allGraphVec.emplace_back(singleNoise);
}
//Do what I described above over here
//This is pseudoCode I have no idea what Im doing here
for(auto &v : allGraphVec){
if(v1 has a v2 == to another v1's v2){
add the v2
take the added value and put it in finalgraph
}
}
//this function just draws a line between each point in the graph, making it a graph not a series of points
for (std::vector<sf::Vector2f>& v : allGraphVec) {
v = this->interpolateNoise(v, 3, 1000);
}
//this will be rendered
this->graphToBeRendered = finalGraph
};
Sorry if this is a bit confusingšŸ˜….
I'm going to answer the question in parts.
So if i understand correctly you have a list of multiple two dimensional points. You currently have this vector: vector<vector<vector<Vector 2f>>> which after translation means you have a list of a list of a list of objects with 2 float values.
What you described in text, from what i understand, is meant to be a vector<vector<vector 2f>> which is a list of lists of objects with two float values.
You can imagine the difference between these lines of code that:
vector<vector<Vector2f>> is a 2D table like in excel where each cell has two values x and y
vector<vector<vector<Vector2f>>> is on the other hand a stacked up version of the previous one on top of each other. So imagine an excel sheet like before, on top of another excel sheet making an excel sheet cube
As you can see this is all getting confusing really quickly...
First things first. Looping...
Easiest way to loop through a vector is to use for(auto &v : vector) and to do it to multiple at the same time:
for(auto &v0 : vector) for(auto &v1 : vector) for(auto &v2 : vector)
The above case would be used to loop through the triple vector. For the rest of the answer i'll assume the thing you want to do is the double vector of double floats.
for(auto &v0 : vector)
{
for(auto &v1 : vector)
{
//do the thing
}
}
To do the task we need to loop through every item in that vector one by one and check if the v2 x exists in another point.
Next is to actually do the thing. We need to find if that number exists in the vector. like going through any list there are many methods some fast some slow. I'll continue using the method i used previously - just plainly going through the vector.
Finding the number x. To find the number x we need to, yes you guessed it go through the vector again. This time to make it less eye straining lets do it in a function:
void findMe(vector 2f &point, vector<vector<vector 2f>> &vec)
{
bool found = false;
for(auto &v0 : vec)
{
for(auto &v1 : vec)
{
if (&v1 != point) //check if we are at the point we are searching
{
if (point.x == v1.x)
{
point.y += v1.y; //i think this is what you wanted. you didnt specify what to add to what
found = true; // check flag
}
}
}
}
if (found == false)
{
//add to next point. you can do this by going through the loop finding the next element and adding it there, by saving the next element or by sending it to the function as parameter. your choice.
}
}
Adding to vector. Removing from vector.
You can add something to a vector by using the emplace_back() function.
eg. adding 5 to vector of ints:
vec.emplace_back(5);
when you have objects you can add them via empty constructor:
vec.emplace_back(sf::vector 2f())
vec.emplace_back({2,5})
vec.emplace_back(sf::vector 2f(5,2)) this should work too i think.
to add vector of vectors vec.emplace_back(vector<vector<vector 2f>>())
Removing:
vec.erase(vec.begin() + offset) //where offset is the index of the item in the vector you want to delete.
IMPORTANT!!!:
remember you need to add item to vector before you use the item or index.
I hope this answer helps. Comment under this if i made any mistakes. I wrote this all from the top of my head and havent teste it in IDE.

Data analysis - memory bug c++

I am a data scientist, currently working on some C++ code to extract triplet particles from a rather large text file containing 2D coordinate data of particles in ~10āµ consecutive frames. I am struggling with a strange memory error that I don't seem to understand.
I have a vector of structs, which can be divided into snippets defined by their frame. For each frame, I build an array with unique ID's for each individual coordinate pair, and if at any point the coordinate pair is repeated, the coordinate pair is given the old coordinate pair. This I then use later to define whether the particle triplet is indeed a trimer.
I loop over all particles and search forward for any corresponding coordinate pair. After I'm done, and no particles were found, I define this triplet to be unique and push the coordinates into a vector that corresponds to particle IDs.
The problem is: after the 18th iteration, at line trimerIDs[i][0] = particleCounter; , the variable trimerCands (my big vector array) suddenly becomes unreadable. Can this be that the vector pointer object is being overwritten? I put this vector fully on the heap, but even if I put it on stack, the error persists.
Do any of you have an idea of what I might be overlooking? Please note that I am rather new at C++, coming from other, less close to the metal, languages. While I think I understand how stack/heap allocations work, especially with respect to vectors/vector structs, I might be very wrong!
The error that Eclipse gives me in the variables tab is:
Failed to execute MI command:
-data-evaluate-expression trimerCands
Error message from debugger back end:
Cannot access memory at address 0x7fff0000000a
The function is as follows.
struct trimerCoords{
float x1,y1,x2,y2,x3,y3;
int frame;
int tLength1, tLength2, tLength3;
};
void removeNonTrimers(std::vector<trimerCoords> trimerCands, int *trCandLUT){
// trimerCands is a vector containing possible trimers, tLengthx is an attribute of the particle;
// trCandLUT is a look up table array with indices;
for (int currentFrame = 1; currentFrame <=framesTBA; currentFrame++){ // for each individual frame
int nTrimers = trCandLUT[currentFrame] - trCandLUT[currentFrame-1]; // get the number of trimers for this specific frame
int trimerIDs[nTrimers][3] = {0}; // preallocate an array for each of the inidivual particle in each triplet;
int firstTrim = trCandLUT[currentFrame-1]; // first index for this particular frame
int lastTrim = trCandLUT[currentFrame] - 1; // last index for this particular frame
bool found;
std::vector<int> traceLengths;
traceLengths.reserve(nTrimers*3);
// Block of code to create a unique ID array for this particular frame
std::vector<Particle> currentFound;
Particle tempEntry;
int particleCounter = 0;
for (int i = firstTrim; i <= lastTrim; i++){
// first triplet particle. In the real code, this is repeated three times, for x2/y2 and x3/y3, corresponding to the
tempEntry.x = trimerCands[i].x1;
tempEntry.y = trimerCands[i].y1;
found = false;
for (long unsigned int j = 0; j < currentFound.size(); j++){
if (fabs(tempEntry.x - currentFound[j].x) + fabs(tempEntry.y - currentFound[j].y) < 0.001){
trimerIDs[i][0] = j; found = true; break;
}
}
if (found == false) {
currentFound.push_back(tempEntry);
traceLengths.push_back(trimerCands[i].tLength1);
trimerIDs[i][0] = particleCounter;
particleCounter++;
}
}
// end block of create unique ID code block
compareTrips(nTrimers, trimerIDs, traceLengths, trimerfile_out);
}
}
If anything's unclear, let me know!

How I can check that an element in a grid of tiles is on my viewport fast?

I have a for loop that I use to draw a grid of tiles with sdl on a game. Since the grid is quite huge with more than 50k elements I want to optimize it.
So there is this function that use to check if I should draw a tile, so if it's outside of the screen I ignore it.
bool Camera::isInViewport(int &x, int &y, int &w, int &h) {
int translatedX = x + offsetX;
int translatedY = y + offsetY;
if (translatedX + w >= 0 && translatedX <= 0 + sdl.windowWidth) {
if (translatedY + h >= 0 && translatedY <= 0 + sdl.windowHeight) {
return true;
}
}
return false;
}
I checked this function it's eating 15% of the CPU alone when the grid is big. Will be possible to make this faster? I can't think of way that will make it eat less resources.
There is not a lot that you can do with this funciton. Do not pass ints as references, it internally passes them as pointers, and it increases costs by dereferencing them. Merge conditions into one if statement and start from those that most probably will be evaluated into false to make early short-circuiting possible.
What I would do instead to solve this performance issue is to organize your tiles in 2D array where index and coordinates could be calculated from each other. In this case you just need to understand index boundaries of tiles covered by your viewport. Instead of checking result of this function on every cell you will be able to just tell left and right X index and top and down Y index. Then just draw them in two nested loops like that:
for (int y = topY; y <= bottomY; ++y)
for (int x = leftX; x <= rightX; ++x)
// do drawing with tile[y][x];
Another approach would be to cache the previous results. If camera is not moving and tiles are not moving - then result of this function is not going to change. Just storing flag that indicates you whether each tile is visible could work here (but not a good practice in big game), update them every time camera moves or recalculate tile if it moves (if it is possible in your app). Still recalculation of all visibility flags on camera movement will be expensive, so try to use first optimization and reduce the task by finding what tile range is affected by camera at all

Trouble writing to 4D vector in C++ (no viable overloaded '=')

The issue I am facing is that via the openCV library I am reading in a series of images as their own "Mat" format: an image matrix.
Basically I need to write any pixel value that's > 0 as "true" to a 4D vector and any that == 0 as "false".
Why 4 dimensions?
vector<vector<vector<bool>>>pointVector;
The 3 vector levels refer to X,Y,Z axes. Bool is just the true/false. The images are Y by Z and are stacked in 3D along axis X.
Basically we have a series of images representing points that are stacked in 3D.
(Poor explanation? Probably)
Anyway, the issue comes in my function to read the points in a single photo then write them out to the 4D vector.
Note: xVal is a global storing the ID number of the photo addressed. It's used for the X dimension (layers of images).
Int lineTo3DVector (Mat image)
{
// Takes in matrix and converts to 4D vector.
// This will be exported and all vectors added together into a point cloud
vector<vector<vector<bool>>>pointVector;
for (int x=0; x<image.rows; x++)
{
for (int y = 0; y<image.cols; y++)
{
if((image.at<int>(x,y)) > 0)
{
pointVector[xVal*image.cols*image.rows + x*image.cols + y] = true;
}
}
}
}
I haven't finished writing all of the function because the if statement intended to write pointVector at address xVal, x, y with bool 'true' throws up an error saying:
No viable overloaded '='
Any idea what's going wrong? I've scoured the web and given myself a headache trying to dig up info (yeah, noob in the deep-end again) so any suggestions would be appreciated.
You're accessing only the first vector (the outer one), without actually accessing any of the vectors inside.
The syntax would be pointVector[x][y][z] = true, where x, y and z are the values you want to use to access the three nested vectors.
What you want is:
pointVector[xVal][x][y] = true
What you were using is a way to access a 3D array that is laid out in memory as a 1D array, but it's not what you want in your case.
Make sure you don't go out of bounds
Make sure the elements you are accessing actually exist! If the code you have shown is the actual code, pointVector will have no elements when you try to use it.
To fix that, you have to resize all the vectors (outer and inner). This can get cumbersome, and you may want to go with the 1D approach, i.e. allocate a large, one-dimensional bool array and access it using the method you were using (largeArray[xVal*image.cols*image.rows + x*image.cols + y]).
1D approach
In the following code numberOfValues is the maximum number of elements you will access.
int lineTo3DVector (Mat image)
{
// Takes in matrix and converts to 4D vector.
// This will be exported and all vectors added together into a point cloud
std::vector<bool> pointVector;
pointVector.resize(numberOfValues);
for (int x=0; x < image.rows; x++)
{
for (int y = 0; y < image.cols; y++)
{
if(image.at<int>(x,y) > 0)
{
pointVector[xVal*image.cols*image.rows + x*image.cols + y] = true;
}
}
}
// Return whatever.
}

Loop to check apple position against snake body positions

I'm trying to figure out how to write a loop to check the position of a circle against a variable number of rectangles so that the apple is not placed on top of the snake, but I'm having a bit of trouble thinking it through. I tried:
do
apple.setPosition(randX()*20+10, randY()*20+10); // apple is a CircleShape
while (apple.getPosition() == snakeBody[i].getPosition());
Although, in this case, if it detects a collision with one rectangle of the snake's body, it could end up just placing the apple at a previous position of the body. How do I make it check all positions at the same time, so it can't correct itself only to have a chance of repeating the same problem again?
There are three ways (I could think of) of generating a random number meeting a requirement:
The first way, and the simpler, is what you're trying to do: retry if it doesn't.
However, you should change the condition so that it checks all the forbidden cells at once:
bool collides_with_snake(const sf::Vector2f& pos, //not sure if it's 2i or 2f
const /*type of snakeBody*/& snakeBody,
std::size_t partsNumber) {
bool noCollision = true;
for( std::size_t i = 0 ; i < partsNumber && noCollision ; ++i )
noCollision = pos != snakeBody[i].getPosition()
return !noCollision;
}
//...
do
apple.setPosition(randX()*20+10, randY()*20+10);
while (collides_with_snake(apple.getCollision(), snakeBody,
/* snakeBody.size() ? */));
The second way is to try to generate less numbers and find a function which will map these numbers to the set you want. For instance, if your grid has N cells, you could generate a number between 0 and N - [number of parts of your Snake] then map this number X to the smallest number Y such that this integer doesn't refer to a cell occupied by a snake part and X = Y + S where S is the number of cells occupied by a snake part referred by a number smaller than Y.
It's more complicated though.
The third way is to "cheat" and choose a stronger requirement which is easier to enforce. For instance, if you know that the cell body is N cells long, then only spawn the apple on a cell which is N + 1 cells away of the snakes head (you can do that by generating the angle).
The question is very broad, but assuming that snakeBody is a vector of Rectangles (or derived from Rectanges), and that you have a checkoverlap() function:
do {
// assuming that randX() and randY() allways return different random variables
apple.setPosition(randX()*20+10, randY()*20+10); // set the apple
} while (any_of(snakeBody.begin(), snakeBody.end(), [&](Rectangle &r)->bool { return checkoverlap(r,apple); } );
This relies on standard algorithm any_of() to check in one simple expression if any of the snake body elements overlaps the apple. If there's an overlap, we just iterate once more and get a new random position until it's fine.
If snakebody is an array and not a standard container, just use snakeBody, snakeBody+snakesize instead of snakeBody.begin(), snakeBody.end() in the code above.
If the overlap check is as simple as to compare the postition you can replace return checkoverlap(r,apple); in the code above with return r.getPosition()==apple.getPosition();
The "naive" approach would be generating apples and testing their positions against the whole snake until we find a free spot:
bool applePlaced = false;
while(!applePlaced) { //As long as we haven't found a valid place for the apple
apple.setPosition(randX()*20+10, randY()*20+10);
applePlaced = true; //We assume, that we can place the apple
for(int i=0; i<snakeBody.length; i++) { //Check the apple position with all snake body parts
if(apple.getPosition() == snakeBody[i].getPosition()) {
applePlaced=false; //Our prediction was wrong, we could not place the apple
break; //No further testing necessary
}
}
}
The better way would be storing all free positions in an array and then pick a Position out of this array(and delete it from the array), so that no random testing is necessary. It requires also updating the array if the snakes moves.