I have the following code, but when I run it, I intermittently get a Debug Assertion Failed error, with the reason "vector iterator not dereferencable".
What I'm trying to do with the program is create a bunch of 2D shapes (squares and circles), then move them about one by one, checking for collisions as they go. When two shapes collide, I want them to be removed, until there is (at most) one shape remaining. About 1 in 3 attempts results in a successful outcome, where the program finds four collisions, then stops. Other times, it will find 1-3 collisions and then error.
I should also mention that I'm relatively new to C++ (and yes, this is a coursework assignment), so if there are any other rookie mistakes in my code, please do point them out!
My methods for moving and colliding seem fine on their own, so I've not included them here.
Square * s1 = new Square(seedCoord(limit), seedCoord(limit), 2.0);
... //There is 9 new shapes created here (squares and circles)
std::vector<Shape*> shape;
shape.push_back(s1);
... //All 9 shapes added to the vector
int m = shape.size();
bool collision = false;
while(m>1) //Keep running until only one shape hasn't collided
{
for (auto i=shape.begin(); i!=(shape.end());) //Perform move on each shape in sequence
{
collision = false; //Set collision boolean to false
(*i)->move(seedCoord(0.5), direction[(rand() % 4)], limit); //Perform move on shape
for (auto j=shape.begin(); j!=(shape.end()-1);) //Check for collision with each shape in sequence
{
if((*i) != (*j)) //Ignore self when checking for collision
{
if((*i)->collide(*j)) //Check for collision
{
std::cout << "Collision between " << (*i) << " and " << (*j) << "\n";
m=m-2; //Lower shapes count by two (ie remove two collided shapes)
delete *i; //Delete pointer to initial shape
i = shape.erase(i); //Erase shape object
delete *j; //Delete pointer to collided shape
j = shape.erase(j); //Erase shape object
collision = true; //Set collision boolean to true
break; //Break out of internal for-loop (shape has been deleted so no more checks are necessary)
}
else
{
j++; //If no collision, move onto next shape
}
}
else
{
j++; //If i=j, move onto next shape
}
}
if(!collision) //If no collision with any shape, move onto next shape
{
i++; //If no collision with any shape, move onto next shape
}
else
{
break; //If collision has occurred, break out of external for-loop (shape has been deleted so no more checks are necessary)
}
}
}
Quoting cpprefernce on erase:
Iterators and references to the erased elements and to the elements between them and the end of the container are invalidated. The past-the-end iterator is also invalidated.
My guess is that your problem occurs when in your second call to erase(j), j is an iterator referring to a location between i and shape.end() and got invalidated.
You are running with two iterators over the same vector. When you erase an element of that vector, all iterators past that element are invalidated. That means that on each collision either i or j is invalidated as erase is called for the other one.
You continue to use the iterators after the erase. This gives undefined behavior, meaning "anything can happen". That "aything" means it can work most of the times, but there are times when it won't. Consider this case:
The last two elements collide, i is end()-2, j is end()-1. First you erase(i), meaning all elements get shoved one to the left, including the one j pointed to. j is now conceptually invalid, but being not much more than a pointer, it points now one past the last element, i.e. j == end(). You now go on and call delete *j;, dereferencing the end() iterator wich will trigger the assertion.
Looking at the code more closely, it looks like your issue is due to the erasure of elements
Namely
i = shape.erase(i); //Erase shape object
...
j = shape.erase(j); //Erase shape object
Now you're correctly using the newly returned iterator from vector.erase() but the problem is that i and j are both iterators into the same vector. This means the following happens
'i' is erased from the shape vector
shape vector gets reallocated without the 'i' element
'i' is reassigned to the next element, within the vector's memory block (which has had the remaining elements after i shuffled down a position)
'j' is still pointing at the old memory location (not taking into account the shuffling)
'j' is erased from shape vector (which is potentially now after the last element)
out of bounds error
Instead of deleting the elements within the loop, you'd be better off flagging your elements as 'dead' within the body of the loop, then using std::remove_if right at the end of the while loop
shape.erase(std::remove_if(shape.begin(),
shape.end(),
isDead),
shape.end());
where isDead is a functor defined something like:
struct isDead
{
bool operator()(const Shape* pX) const
{
return pX->isDead();
}
};
Related
I am holding some 2-D points with x-y coordinates in a list. I have a method which sorts the array according to the distances the points have with the cursor and the method returns the pointer to the point that is closest to the cursor.
However I am using &points.first() and this always points to the first element of the list. However the pointer changes after I resort the list. How do I get a pointer that points to the specific ELEMENT, not the first element of the list.
I've tried:
&points.first()
QList<Point2> points;
Point2 *DrawingWidget::closestToCursor(){
// Current mouse position
Point2 pos(m_x, m_y);
// There are no points
if(points.isEmpty()){
return NULL;
}
// Sorts according to distance to the cursor
std::sort(std::begin(points), std::end(points), [&pos](Point2 a, Point2 b) {
return pos.distanceFrom(a) < pos.distanceFrom(b);
});
// We dont allow points closer than 50px appart
if(pos.distanceFrom(points.first()) > 50){
return NULL;
}
// Even after the resort, this always points to the first element of the vector. How do I get this elements pointer instead?
// Currently it seems that the pointer is basically LIST+0x0, however if the element shifts to whatever position, how do I still have its pointer?
return &points.first();
}
Each time I call this method near a new point, the pointer just shifts to the first element of the list, which is what it's supposed to DO, I know this. But how do I do it like I need to?
You should probably do linear search to find that element because sorting is more expensive.
Linear search is O(N).
Sorting is O(N*log2(N)).
E.g.:
auto& found = *std::min_element(std::begin(points), std::end(points),
[&pos](Point a, Point b) { return pos.distanceFrom(a) < pos.distanceFrom(b); });
return pos.distanceFrom(found) > 50 ? 0 : &found;
Since your list ends up sorted, you can find the original first point in log2(n) steps using a binary search:
#include <algorithm>
Point2 *DrawingWidget::closestToCursor() {
if (points.isEmpty())
return NULL;
Point2 pos(m_x, m_y);
auto cmpfun = [&pos](Point2 a, Point2 b) {
return pos.distanceFrom(a) < pos.distanceFrom(b);
});
auto firstPoint = points.first();
std::sort(std::begin(points), std::end(points), cmpfun);
if (pos.distanceFrom(points.first()) > 50)
return NULL;
// return a pointer to the original first point
return &*std::lower_bound(std::begin(points), std::end(points),
firstPoint, cmpfun);
}
There are other approaches, such as a decorate-sort-undecorate to sort the pointers and truly retain the original point, but those would likely end up being significantly more expensive to execute.
for my application I need to create a fixed size buffer (3 elements) of point clouds.
To do this I tried the naive way in my callback (I'm working on ROS):
vector< vector<Point2d> > points_buffer(3); // buffer of point clouds ,fixed size = 3
void laserToWorldCallback(const icars_laser_roi::stx_points::ConstPtr& laser_points, const icars_2d_map_manager::Status::ConstPtr& car_pos){
double x_w, y_w;
double x, y;
vector<Point2d> temp;
for(int i = 0; i < laser_points->points_x.size(); i++){
// get the coordinates
x = laser_points->points_x[i];
y = laser_points->points_y[i];
// tranform the coordinates
x_w = car_pos->xGlobal + x*cos(car_pos->yaw) - y*sin(car_pos->yaw);
y_w = car_pos->yGlobal + x*sin(car_pos->yaw) + y*cos(car_pos->yaw);
temp.push_back(Point2d(x_w, y_w));
}
if(points_buffer.size() != 3){ // the buffer is not empty
points_buffer.push_back(temp);
}else{ // the buffer is empty, delete last element and push_back
// delete last element
points_buffer[0] = points_buffer[1];
points_buffer[1] = points_buffer[2];
points_buffer[3] = temp;
}
}
}
But this way seems to me a bit rough and not efficient at all.
Might someone suggest me a more elegant and efficient way to do what I want?
Thank you
Regards
To fix some efficiency problems. First after declaration of temp you can already reserve the memory it will use with
temp.reserve(laser_points->points_x.size());
So there will be no reallocation of memory in push_back method.
If you are using c++11 or greater, in the case buffer is not yet full, you can move the content of the temp with std::move.
points_buffer.push_back(std::move(temp));
This is a O(1) operation. The content of temp after this is valid but unspecified.
Then in the deleting the last element use vector::swap instead of copy as it will swap the content and is guaranteed to be constant in time.
points_buffer[0].swap(points_buffer[1]);
points_buffer[1].swap(points_buffer[2]);
points_buffer[2].swap(temp); //There is a typo here index should be 2 not 3.
The program would be more readable, if you would wrap point_buffer in a class. Then you could also consider not rotating the content of whole vector but keeping track of the first index. This would work well also for larger point_buffer than 3. Then adding new element to buffer would be just
point_buffer[fist_element_].swap(temp);
first_element=(first_element_+1)%3;
then to access the element at position i you could implement the operator[] as
vector<Point2d>& operator[](int i){
return point_buffer[(i+first_element)%3];
}
Suppose you've a set s of horizontal line segments in the plane described by a starting point p, an end point q and a y-value.
We can assume that all values of p and qare pairwise distinct and no two segments overlap.
I want to compute the "lower contour" of the segment.
We can sort s by p and iterate through each segment j. If i is the "active" segment and j->y < i->y we "switch to" j (and output the corresponding contour element).
However, what can we do, when no such j exists and we find a j with i->q < j->p. Then, we would need to switch to the "next higher segment". But how do we know that segment? I can't find a way such that the resulting algorithm would have a running time of O(n log n). Any ideas?
A sweep line algorithm is an efficient way to solve your problem. As explained previously by Brian, we can sort all the endpoints by the x-coordinate and process them in order. An important distinction to make here is that we are sorting the endpoints of the segment and not the segments in order of increasing starting point.
If you imagine a vertical line sweeping from left to right across your segments, you will notice two things:
At any position, the vertical line either intersects a set of segments or nothing. Let's call this set the active set. The lower contour is the segment within the active set with the smallest y-coordinate.
The only x-coordinates where the lower contour can change are the segment endpoints.
This immediately brings one observation: the lower contour should be a list of segments. A list of points does not provide sufficient information to define the contour, which can be undefined at certain x-coordinates (where there are no segments).
We can model the active set with an std::set ordered by the y position of the segment. Processing the endpoints in order of increasing x-coordinate. When encountering a left endpoint, insert the segment. When encountering a right endpoint, erase the segment. We can find the active segment with the lowest y-coordinate with set::begin() in constant time thanks to the ordering. Since each segment is only ever inserted once and erased once, maintaining the active set takes O(n log n) time in total.
In fact, it is possible to maintain a std::multiset of only the y-coordinates for each segment that intersects the sweep line, if it is easier.
The assumption that the segments are non-overlapping and have distinct endpoints is not entirely necessary. Overlapping segments are handled both by the ordered set of segments and the multiset of y-coordinates. Coinciding endpoints can be handled by considering all endpoints with the same x-coordinate at one go.
Here, I assume that there are no zero-length segments (i.e. points) to simplify things, although they can also be handled with some additional logic.
std::list<segment> lower_contour(std::list<segment> segments)
{
enum event_type { OPEN, CLOSE };
struct event {
event_type type;
const segment &s;
inline int position() const {
return type == OPEN ? s.sp : s.ep;
}
};
struct order_by_position {
bool operator()(const event& first, const event& second) {
return first.position() < second.position();
}
};
std::list<event> events;
for (auto s = segments.cbegin(); s != segments.cend(); ++s)
{
events.push_back( event { OPEN, *s } );
events.push_back( event { CLOSE, *s } );
}
events.sort(order_by_position());
// maintain a (multi)set of the y-positions for each segment that intersects the sweep line
// the ordering allows querying for the lowest segment in O(log N) time
// the multiset also allows overlapping segments to be handled correctly
std::multiset<int> active_segments;
bool contour_is_active = false;
int contour_y;
int contour_sp;
// the resulting lower contour
std::list<segment> contour;
for (auto i = events.cbegin(); i != events.cend();)
{
auto j = i;
int current_position = i->position();
while (j != events.cend() && j->position() == current_position)
{
switch (j->type)
{
case OPEN: active_segments.insert(j->s.y); break;
case CLOSE: active_segments.erase(j->s.y); break;
}
++j;
}
i = j;
if (contour_is_active)
{
if (active_segments.empty())
{
// the active segment ends here
contour_is_active = false;
contour.push_back( segment { contour_sp, current_position, contour_y } );
}
else
{
// if the current lowest position is different from the previous one,
// the old active segment ends here and a new active segment begins
int current_y = *active_segments.cbegin();
if (current_y != contour_y)
{
contour.push_back( segment { contour_sp, current_position, contour_y } );
contour_y = current_y;
contour_sp = current_position;
}
}
}
else
{
if (!active_segments.empty())
{
// a new contour segment begins here
int current_y = *active_segments.cbegin();
contour_is_active = true;
contour_y = current_y;
contour_sp = current_position;
}
}
}
return contour;
}
As Brian also mentioned, a binary heap like std::priority_queue can also be used to maintain the active set and tends to outperform std::set, even if it does not allow arbitrary elements to be deleted. You can work around this by flagging a segment as removed instead of erasing it. Then, repeatedly remove the top() of the priority_queue if it is a flagged segment. This might end up being faster, but it may or may not matter for your use case.
First sort all the endpoints by x-coordinate (both starting and ending points). Iterate through the endpoints and keep a std::set of all the y-coordinates of active segments. When you reach a starting point, add its y-coordinate to the set and "switch" to it if it's the lowest; when you reach an ending point, remove its y-coordinate from the set and recalculate the lowest y-coordinate using the set. This gives an O(n log n) solution overall.
A balanced binary search tree such as that used to implement std::set generally has a large constant factor. You can speed up this approach by using a binary heap (std::priority_queue) instead of a set, with the lowest y-coordinate at the root. In this case, you can't remove a non-root node, but when you reach such an ending point, just mark the segment inactive in an array. When the root node is popped, continue popping until there is a new root node that hasn't been marked inactive already. I think this will be about twice as fast as the set-based approach, but you'll have to code it yourself and see, if that's a concern.
I have this problem, see.
I'm using OpenCV to track a hand in a video. The hand in located with a CascadeDetector and then tracked using CamSHIFT. I'm also using a Kalman Filter, to correct the position of the hand, if the CamShift algorithm fails at some frames.
The problem arises, when I'm trying to erase an element from a std::vector, where I'm storing my Hands. The reason for erasing is that due to some issues, a face is misinterpreted as a hand, so I'm detecting faces, and if the hand region intersects with a face region, I delete that hand. I know, very naive, but I'm currently at the very start.
The code looks like this:
class Hand {
...
public:
struct
{
...
struct {
cv::Mat_<float> measurement;
cv::KalmanFilter KF;
cv::Mat state;
cv::Mat processNoise;
} KalmanTracker;
} Tracker;
};
...
std::vector<Hand> hands;
...
std::vector<cv::Rect> faces;
faceCascade.detectMultiScale(frame, faces, 1.1, 2, CV_HAAR_FIND_BIGGEST_OBJECT);
// iterate through hands
for (std::vector<Hand>::iterator it = hands.begin(); it != hands.end(); ++it) {
// iterate through faces:
for (std::vector<cv::Rect>::iterator fc = faces.begin(); fc != faces.end(); ++fc) {
cv::Rect intersection = (*it).handBox.boundingRect() & (*fc);
// check if they have at leasy 75% intersection
if (intersection.area() >= ((*it).handBox.boundingRect().area() * 0.75)) {
// delete that hand from list
hands.erase(it); // this gets me a a EXC_BAD_ACCESS
}
}
}
The hands.erase(it) line gets me an EXC_BAD_ACCESS while pointing at my KalmanFilterTracker struct, as well as this line in mat.hpp with a EXC_i386_GPFLT:
inline void Mat::release()
{
if( refcount && CV_XADD(refcount, -1) == 1 ) // EXC_BAD_ACCESS, code=EXC_i386_GPFLT
...
}
Neither hands nor faces are empty.
If I completely remove the Kalman filter from my project and any mention or use of it, the error disappears.
vector::erase(iterator) invalidates the iterator being erased. But it does return a new iterator to the next valid item in the vector. Thus the line should be:
it = hands.erase(it);
Edit after berak's comment:
Ah, I missed the inner loop. Since you're invalidating it inside the inner loop, things get a little complicated. You'll need to jump through some hoops to get it right.
for (std::vector<Hand>::iterator it = hands.begin(); it != hands.end(); ) {
bool found = false;
// iterate through faces:
for (std::vector<cv::Rect>::iterator fc = faces.begin(); fc != faces.end() && !found; ++fc) {
cv::Rect intersection = (*it).handBox.boundingRect() & (*fc);
// check if they have at leasy 75% intersection
if (intersection.area() >= ((*it).handBox.boundingRect().area() * 0.75)) {
found = true;
}
}
if (found) {
it = hands.erase(it);
}
else {
++it;
}
}
It's possible to fold some of the iterator manipulation into the inner loop, but keeping it outside makes the code a bit clearer and easier to reason about.
Ok, through some debugging I found out, that my vector of hands becomes empty at some point and the error appears while trying to erase from an empty vector.
Only thing left is to find out why does it become empty and at which point.
Thank you everyone.
I've got a very weird segmentation fault with my code. Actually, when I run my executable, it aborts. When I run it with gdb, it also aborts. But when I run it with valgrind, it terminates successfully and gives me the correct result.
That's for the introduction. Now here's a description of my algorithm. What I want to do is extracting the subsurface of a mesh that looks similar to another one, given a certain threshold, r. The key idea of my algorithm is looping on each point of the mesh (let's say, p). If I find a point of the reference mesh included in the sphere of center p and radius r, I don't do anything and I go to the next point. Otherwise, I delete the point and every element of the mesh including it (which means, edges, and triangles of the mesh to which the point belongs).
Now I jump to a short description of my classes. I have four classes to represent elements of the mesh :
Vertex. This class includes two attributes (I don't mention the getters and setters) : a position and a list, belongsTo, which contains pointers to all of the edges to which the vertex belongs.
Edge. This class includes three attributes : the two vertices delimiting the edge, and a list, also called belongsTo, wichi contains pointers to all of the triangles to which an edge belongs.
Triangle. This class includes three attributes : the three edges delimiting the triangle, and a normal vector (I mentioned it, but it doesn't really matter here : I only use it to render the triangle using OpenGL).
And, finally :
Mesh. This class includes a vector v of Vertex objets, a vector e of Edge objects and finally a vector t of Triangles.
Now here is the prototype of my function :
class Mesh {
...
void extractIdenticalSubsurfaces(Mesh *a, double threshold);
}
As you may have already guessed, this function takes two parameters into account : the reference mesh and a threshold (actually the radius of the sphere I build to determine if I have to delete the point from the mesh). Is everything clear ?
Description of my algorithm : I loop on each point of the mesh (on each point of v, actually). For each point, I build a sphere of radius 'threshold' centered in this point and I verify it there is a point of a in this sphere. If I find a point, I don't do anything. Otherwise, I delete it from v.
But before doing that, I have some operations to do.
Indeed, I have to loop on each edge to which the point belongs (using the list 'belongsTo') and then on each triangle to which the point belongs (once again using the list 'belongsTo').
For each of these triangles, I loop on the three edges delimiting it and I delete the address of the triangle from the list of edges to which this triangle belongs (because I want to delete this triangle, so I have to anticipate).
Once it's done, I do the same with the current edge of the loop : I delete the adress of the edge from the 'belongsTo' list associated to the two vertices of the edge.
But I also have to delete both suppressed edges and triangles from the vectors e and t !
It may seem not so easy to understand (I think it's better if you have a sheet of paper just in front of you and if you draw a mesh and consider a point that you want to suppress) but I am quite sure of the exactitude of this algorithm : actually, when I run my program with valgrind, it displays the good sub-mesh on my screen.
But the problem is that if I don't use valgrind, it fails.
Here's the error message displayed by gdb :
Program received signal SIGSEGV, Segmentation fault.
0x0000000000406f3a in std::list<Triangle*, std::allocator<Triangle*> >::begin (this=0x39) at /usr/include/c++/4.6/bits/stl_list.h:731
731 { return iterator(this->_M_impl._M_node._M_next); }
The backtrace :
(gdb) bt
#0 0x0000000000406f3a in std::list<Triangle*, std::allocator<Triangle*> >::begin (this=0x39) at /usr/include/c++/4.6/bits/stl_list.h:731
#1 0x0000000000406e37 in std::list<Triangle*, std::allocator<Triangle*> >::remove (this=0x39, __value=#0x7fffffffc860) at /usr/include/c++/4.6/bits/list.tcc:242
#2 0x00000000004062a1 in Edge::deleteTriangle (this=0x21, t=0xcf4ed0) at elements.cpp:109
#3 0x0000000000408aa4 in Mesh::extractIdenticalSubsurfaces (this=0xc14b10, a=0xc0f900, threshold=0) at mesh.cpp:174
#4 0x0000000000404687 in Scene::extractIdenticalSurfaces (this=0xbad8c0, threshold=0) at scene.cpp:36
#5 0x00000000004051c6 in Viewer::keyPressEvent (this=0x7fffffffde80, e=0x7fffffffd020) at viewer.cpp:104
...
As you can see, getting an error is logical here, because the system seems to try to apply the 'deleteTriangle' method on an object whose 'this' address is 0x21. But what I am wondering is, how on earth can it be 0x21. I tried to dislay the adresses of the pointers to edges and vertices included in all of the Triangles and Edges objects, and they seems to be correct (after all I can display all of the elements of my mesh using OpenGL, so...). But at a moment this 0x21 comes in and I definitely don't know why.
This is my code :
void Mesh::extractIdenticalSubsurfaces (Mesh *a, double threshold) {
vector<Vertex *>::iterator it1 = v.begin() ;
while (it1 != v.end()) {
if (a->emptySphere((*it1), threshold)) {
list<Edge *> edgesToDelete = list<Edge *>();
list<Triangle *> trianglesToDelete = list<Triangle *>();
for (list<Edge *>::iterator it2 = (*it1)->getIteratorBegin() ; it2 != (*it1)->getIteratorEnd() ; it2++) {
for (list<Triangle *>::iterator it3 = (*it2)->getIteratorBegin() ; it3 != (*it2)->getIteratorEnd() ; it3++) {
Triangle *argT = (*it3);
(*it3)->getFirstEdge()->deleteTriangle(argT);
(*it3)->getSecondEdge()->deleteTriangle(argT);
(*it3)->getThirdEdge()->deleteTriangle(argT);
trianglesToDelete.push_back(argT);
}
Edge *argE = (*it2);
(*it2)->getFirstVertex()->deleteEdge(argE);
(*it2)->getSecondVertex()->deleteEdge(argE);
edgesToDelete.push_back(argE);
}
edgesToDelete.unique();
trianglesToDelete.unique();
vector<Edge *>::iterator it4 = e.begin();
while (it4 != e.end()) {
bool isEqual = false;
for (list<Edge *>::iterator it5 = edgesToDelete.begin() ; it5 != edgesToDelete.end() ; it5++) {
if ((*it4) == (*it5)) {
isEqual = true;
edgesToDelete.erase(it5);
break;
}
}
if (isEqual) {
e.erase(it4);
} else {
it4++;
}
}
vector<Triangle *>::iterator it6 = t.begin();
while (it6 != t.end()) {
bool isEqual = false;
for (list<Triangle *>::iterator it7 = trianglesToDelete.begin() ; it7 != trianglesToDelete.end() ; it7++) {
if ((*it6) == (*it7)) {
isEqual = true;
trianglesToDelete.erase(it7);
break;
}
}
if (isEqual) {
t.erase(it6);
} else {
it6++;
}
}
v.erase(it1);
} else {
it1++;
}
}
}
(Well, not very pretty sometimes, I agree but this isn't the subject of my question (on first sight))
So, do you have any idea of why I get this error ?