C++ double iterator loop for collision detection - c++

First things first, I'm very new to C++! So I've got a standard list of objects representing elements in 2d space, and I want to do some basic collision detection. My first idea (coming from a mostly Java background...) is to compare each object with each other object, calling an function to check for intersection on each pair or objects. This would have been simple in Java, taking the first item of the ArrayList, comparing it with the second, third, etc. then taking the second item, comparing it with the third, fourth, etc. This is the approach I took with the problem in C++, but I'm using an iterator (rather than direct element access like I'd do in Java), but iterators are meant to be used linearly, right? So direct access isn't suitable.
So my question is how do I perform this algorithm? I'm also reasonably sure this isn't the best way to do (very basic) collision detection, so any advice on that would be welcomed too. Here's my (non-working) code.
for (list<Box>::iterator p = mBoxes.begin(); p != mBoxes.end(); p++) {
for (list<Box>::iterator q = mBoxes.begin() + p); q != mBoxes.end(); q++) {
if (p->isIntersecting(q)) {
p->changeDirection();
q->changeDirection();
}
}
}
This should illustrate the method I'm attempting, but of course my try of mBoxes.begin() + p doesn't work!

EDIT: Responding to multiple comments.
for (list<Box>::iterator p = mBoxes.begin(); p != mBoxes.end(); ++p) {
for (list<Box>::iterator q = p); q != mBoxes.end(); ++q) {
if (p==q) continue;
if (p->isIntersecting(*q)) {
p->changeDirection();
q->changeDirection();
}
}
}

The list class is a linked list, you cannot directly index into it. Also, iterators are not indices, + just doesn't makes any sense.
However, in theory, you should be able to copy an iterator, if you change q = mboxes.begin() + p into q = p it should set q to be an iterator pointing to the same location of p, and that may just solve your problem.
for (list<Box>::iterator p = mBoxes.begin(); p != mBoxes.end(); p++) {
for (list<Box>::iterator q = p, q++; q != mBoxes.end(); q++) {
if (p->isIntersecting(q)) {
p->changeDirection();
q->changeDirection();
}
}
}
the q++ should simply skip the current element so you don't compare an item against itself.

Something like this should work:
for (int p = 0; p < mBoxes.size(); ++p)
{
for (int q = 0; q < mBoxes.size(); ++q)
{
if (p == q)
{
// don't compare for collision against itself
continue;
}
if (mBoxes[p]->isIntersecting(mBoxes[q]))
{
mBoxes[p]->changeDirection();
mBoxes[q]->changeDirection();
}
}
}
However, one problem with this is that as you iterate through you'll compare p intersect q and also q intersect p meaning that if they do intersect they'll change direction twice putting them back in the same direction each time. So some extra logic would be needed to avoid this.

Related

Prevent adding multiple same items to vector in recursion

I got a method that adds adjacent Voxels to a vector. This method uses an vector with all the remaining points (means, they still need to be looked at as they are possible adjacents) and looks on every one of them if it is near enough to be added. If an element is a neighbor it also checks all adjacents of this element to add them too. This happens in a recursive manner.
void remove(std::vector<pcl::PointXYZ> &vec, pcl::PointXYZ p) {
for (int i = 0; i < vec.size(); i++) {
if (vec[i].x == p.x && vec[i].y == p.y && vec[i].z == p.z) {
vec.erase(vec.begin() + i);
break; // as all points should be unique
}
}
}
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
for (pcl::PointXYZ p : remainingPoints) {
if (distance(p, start) < 0.015) {
newCluster.push_back(p);
remove(remainingPoints, p);
if (remainingPoints.size() > 0)
addAdjacents(p, newCluster, remainingPoints);
}
}
}
The problem is, that many points from the remainingPoints-vector are added to the newCluster multiple times. I thought this wouldn't happen but it seems like internally it makes copies of the vector in the recursion? If a point is removed in a deeper layer the for-loop in the outer execution is somehow still iterating over this (removed) element.
I am fairly new to c++ so I am not sure how to prevent this. Can anyone help me? Thanks!
For sure I can just write a method addToCluster which just checks if the vector has this element before adding it but I thought that maybe there is a more elegant way to prevent this happening in the first place.
Edit:
As I understand I am breaking my iterator in the loop. So I would need to somehow update my iterator after calling addAdjacents. Is this right? Can I do something like that?
You need to separate identifying the points you wish to migrate with erasing them from the input.
template<typename BidirIt>
BidirIt addAdjacentsImpl(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, BidirIt first, BidirIt last) {
auto part = std::stable_partition(first, last, [&](auto p){ return distance(p, start) >= 0.015; });
for (auto it = part; it != last; ++it) {
newCluster.push_back(*it);
part = addAdjacentsImpl(*it, newCluster, first, part);
}
return part;
}
This only re-orders the elements, such that those we wish to remove are after those we wish to keep. I've written it as a template because I don't care to name the particular iterator types.
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
auto last = addAdjacentsImpl(start, newCluster, remainingPoints.begin(), remainingPoints.end());
remainingPoints.erase(last, remainingPoints.end());
}
Here
list.erase(list.begin() + i);
You erase an element from the std::vector called list (really not the best name for a vector) which invalidates iterators at and after the erased position. It isnt that obvious, but this erasing happens while addAdjacents iteratates over the same container. Leaving only the broken part in we have
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
for (pcl::PointXYZ p : remainingPoints) {
newCluster.push_back(p);
//remove(remainingPoints, p); // <- calls erase
remainingPoints.erase( remainingPoints.begin() + some_index);
}
}
}
Erasing an element from remainingPoints does break the range-based loop, because under the hood it uses iterators that potentially got invalidated in remove.

How to get int position of vector loop

How to get int position of this loop? Thank you.
auto a = vect.begin();
auto b = vect2.begin();
auto c = vect3.begin();
for (; a != vect.end() && b != vect2.end() && c != vect3.end(); a++, b++, c++) {
}
I need to print values of other variable, but I need to get actual unsigned int position of this vector loop.
I need to print double vector using this position of this vector.
And how to get the last index of vector.
My problem is for for loop with multiple vectors and getting index from it next to use only last of indexes.
As Angew shows, a simple indexed loop may be preferable when you need indices.
However, it is possible to get the index from an iterator as well:
auto a = vect.begin();
auto b = vect2.begin();
auto c = vect3.begin();
for (/*the loop conditions*/) {
auto index = a - vect.begin();
}
It is also possible to get the index of a forward iterator using std::distance, but it would be unwise to use it in a loop, since the complexity will be linear for non-random-access iterators.
In the case of forward iterators (and generic code that must support forward iterators), you can write a loop which has both the index variable, and the iterators.
P.S. it is potentially preferable to use pre-increment with iterators. Probably only matters in debug build.
It's simple: if you need indices, don't use iterators:
for (
size_t idx = 0, idxEnd = std::min({vect.size(), vect2.size(), vect3.size()});
idx < idxEnd;
++idx
)
{
auto& obj1 = vect[idx];
auto& obj2 = vect2[idx];
auto& obj3 = vect3[idx];
}
(The above code initialises idxEnd once at the start of the loop, so that it's not needlessly recomputed at each iteration. It's just an optimisation).

Vector Collision

I am quite green regarding vectors, and this is my first time actually using them for collision checking. This is for my project, and I am stumped on how to implement the collision. The current Collision check and response codes I have seem to be ... bad design.
This is my code:
for(auto it = ArrayofEntities.begin(); it != ArrayofEntities.end(); it++)
{
CEntity * go = (*it);
for(auto i = ArrayofEntities.begin(); i != ArrayofEntities.end();)
{
//Collision for entities. Collision Event returns the iterator after an element is erased.
CEntity * other = (*i);
if (go != other)
{
if (!theCollision.CheckCollision(go, other, false, false, false, false)) //Checks if it has collided go with other
{
i = go->CollisionEvent(*other, ArrayofEntities); //Run collision code, setting i to the iterator which is returned.
//break;
}
else
{
i++;
}
}
else
{
i++;
}
}
}
CEntity is the base class for all the entities.
My CheckCollision just returns a true or false on the collision, and my collision event runs the collision and returns an iterator (because I might have to destroy things in the vector).
My collision event is below
vector<CEntity*>::iterator bullet::CollisionEvent(CEntity &other, vector<CEntity*> & theArray)
{
case ZOMBIE:
{
other.hp -= power * 0.01;//Effect
int Counter, index, bulletindex;
auto it = theArray.begin();
//Find the bullet and the other in the array.
for (it = theArray.begin(), Counter = 0; it != theArray.end();it++, Counter++)
{
CEntity *go = NULL;
go = (*it);
if (go == &other)
{
index = Counter;
}
if(go->ID == BULLET && go->GetX() == GetX() && go->GetY() == GetY())
{
bulletindex = Counter;
}
}
this->~bullet();//Delete the bullet
theArray.erase(theArray.begin() + bulletindex);
if(other.hp <= 0)
{
other.~CEntity();
it = theArray.erase(theArray.begin() + index); //delete from array.
return it;
}
it = theArray.begin() + index;
return it;
}
}
I have basically done this like how I would do an array. Just check it against itself. The error it gives is "Vector Iterator not Incrementable", on the first for loop after the collision event has been run.
So my question: 1) What am I doing wrong?
2) Is my thinking to do this like checking arrays wrong?
This is my school project, so I have full control of the codes.
I would prefer to have a quick fix over a complete rewriting of all the collision codes, but if it really comes down to it, I will rewrite my codes.
If you look at the implementation of std::remove_if, you'll see that they've solved the issue of iterator invalidation in another way. instead of erasing elements, they move them to the end of the array.
This may be the easiest solution for you as well. Keep an iterator which points after the last "live" entirty. It starts out at .end but as bullets hit things, you swap the entities to the back of your range and decrement that last-live iterator.
Then, when you're done looping over your array, you clean up with a single call to .erase.
And yes, you should use either std::unique_ptr<CEntity> or std::shared_ptr<CEntity> in the collection. In that way, .erase won't just erase the pointer but also the object pointed to.

c++ refer to consecutive values with iterator

I want to calculate values of w, being the distance between two points, iteratively. In my Aclass i have members like x, y and this w. So I use Pitagoras Theorem, but how to do it smoothly using iterators? This is my try, but I don't know how using iterator reference to the "previous than pointed" value.
Please help :)
Aclass temp;
for (ars::cClassC::iterator it = New_Diff.begin(); it != New_Diff.end(); it++)
{
temp.w = sqrt(((it->getX()-(it->getX()-1))^2+((it->getY()-(it->getY()-1)));
New_Diff.push_back(temp);
}
Just save the previous element:
ars::cClassC::iterator it_prev = New_Diff.end();
for (ars::cClassC::iterator it = New_Diff.begin(); it != New_Diff.end(); it++) {
if (it_prev != New_Diff.end()) {
//do the stuff here, now you have the two elements
}
it_prev = it;
}

Delete and add elements to vector inside main loop

I searched before but couldn't find any answers. I am somewhat new to c++, so hopefully this question won't be too stupid.
I am trying to add and remove elements in a vector, in my case populated with particles during a big update or drawing loop over all particles. For example remove some particles because they died, but also add a few other ones because one particle collided with an object and I want to show a small particle burst at the point of collision. I made this simple test code in a demo file to get to the bottom of the problem.
I think the problem is since I delete and add particles the iterator pointer becomes invalid. Deletion works, but when I add a few random ones I get a null pointer. the code below is somewhat verbose, I know I should use iterators with begin() and end() but I had the same problem with those, and played with the code a bit, trying more javascript array style looping because I am more familiar with that.
void testApp::drawParticles()
{
int i=0;
int max = particles.size();
vector<Particle*>::iterator it = particles.begin();
while ( i < max ) {
Particle * p = particles[i];
if ( ofRandom(1) > .9 ) {
it = particles.erase(it);
max = particles.size();
} else {
ofSetColor(255, 255, 0);
ofCircle( p->x, p->y, p->z, 10);
if ( ofRandom(1) < .1 ) addSomeNewOnes();
i++;
it++;
}
}
}
void testApp::addSomeNewOnes()
{
int max = ofRandom(4);
for ( int i=0; i<max; i++ ) {
Particle * p = new Particle();
p->x = ofRandom( -ofGetWidth()/2, ofGetWidth()/2 );
p->y = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
p->z = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
particles.push_back( p );
}
}
Every time you insert in to a vector, the iterators to it are potentially invalidated. You cannot do this:
if ( ofRandom(1) < .1 ) addSomeNewOnes();
it++
Because after the call to addSomeNewOnes(), it is invalid.
You can use the iterator returned by a call to vector::insert, but in your case that would mean re-engineering your code.
This is something you might want to do, anyway, as your code is a bit kludgy.
You could loop at it from the end, which should allow you to delete your current (since you're only deleting off of the end) and add new ones which get added to the end:
Vector<Particle*>::iterator it = particles.end();
while (iter != particles.begin()) {
Particle * p = *iter;
if ( ofRandom(1) > .9 ) {
particles.erase(iter);
} else {
ofSetColor(255, 255, 0);
ofCircle( p->x, p->y, p->z, 10);
if ( ofRandom(1) < .1 ) addSomeNewOnes();
}
iter--;
}
If you are not adding, based on the info here, iterators in STL are stable so you should be able to iterate forwards and still be able to achieve the same result.
Iterators are invalidated in some cases when the underlying data changes.
You'll have to break out of the loop and start again.
You should be able to do that by wrapping your whole drawParticles function in a while(notDone) loop, and setting notDone to true when you're done modifying the vector.
Here's another SO question with the rules: Iterator invalidation rules
it = particles.erase(it);
will return an iterator pointing to the new location of the element following the one erased. If the one erased happens to be the last one, it will point to particles.end(). it++ on "end" is an error.
Also, if:
if ( ofRandom(1) < .1 ) addSomeNewOnes();
evaluates to true and addSomeNewOnes() is called, as others have said, that will also invalidate the iterator.
Are you inserting and deleting at the location of the iterator? If so,
insert and erase return iterators which will be valid, and you can use
those. Something like:
std::vector<T>::iterator i = v.begin();
while ( i != v.end() ) {
if ( someCondition ) {
i = v.erase( i );
} else {
++ i;
}
}
is a more or less standard idiom.
For random insertions and deletions, you have to work with indexs, which
you update according to the number of elements inserted or deleted, and
whether the insertion or deletion was in front of or behind the current
position.
There's an easy way to make this work correctly without worrying about invalidation rules: just build a new vector for the next iteration.
typedef vector<Particle*> ParticleVector;
// modifies the particle vector passed in
void testApp::drawParticles(ParticleVector &particles)
{
ParticleVector next;
next.reserve(particles.size()); // seems like a reasonable guess
for (auto it = particles.begin(); it != particles.end(); ++it)
{
Particle *p = *it;
if (ofRandom(1) > .9) {
// don't copy to the next cycle
delete p;
} else {
// copy to the next cycle
next.push_back(p);
ofSetColor(255, 255, 0);
ofCircle(p->x, p->y, p->z, 10);
if (ofRandom(1) < .1)
addSomeNewOnesTo(next);
}
}
particles.swap(next);
}
Note how much easier it is to refactor like this when you're not using globals, btw.
void testApp::addSomeNewOnesTo(ParticleVector &particles)
{
int max = ofRandom(4);
for ( int i=0; i<max; i++ ) {
Particle * p = new Particle();
p->x = ofRandom( -ofGetWidth()/2, ofGetWidth()/2 );
p->y = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
p->z = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
particles.push_back( p );
}
}
On another note : Isn't there a memory leak in your implementation?
You are using
vector<Particle*> particles
and also using particles.erase().
Wont that just delete your pointer to Particle,and not created object?