PDA

View Full Version : determining collision of item groups



ratsrcute
11th December 2009, 19:36
In my problem, I'm creating item groups which consist of a number of QGraphicsRectItem rectangles. That is, each group is a complex shape made of several overlapping rectangles. Call these "rectangle groups."

Next, I want to determine if two "rectangle groups" are colliding. I thought, "Hey, use collidesWithItem()"

So if I have group1 (which is a QGraphicsItemGroup) and also group2, I could write

group1.collidesWithItem(group2)

Well, the problem is that to determine collision it is checking the overall bounding rectangle of each group. It is not looking at it rectangle-by-rectangle. Potentially two groups could "nestle" in close to each other without any of their sub-rectangles overlapping, but that is not what it is checking.

I could write code that loops on every pair of sub-rectangles, but I'm actually working in PyQt and I'm trying to avoid writing loops in Python.

Just wondering if anyone has an idea how to do what I want without resorting to custom functionality.

Thanks,
Mike

nikolastojkoski
29th November 2017, 22:51
Please tell me you found the solution... I need it

d_stranz
29th November 2017, 23:38
Since the OP hasn't posted anything here in over 7 years, I doubt he is going to answer your question.

As the OP states, QGraphicsItem::collidesWithItem() will probably not work well, since it uses the bounding rectangles for each of the groups. You could try using the QGraphicsItem::boundingRegion() method to retrieve the QRegion for each group and calling QRegion::intersects() to see if the two QRegions are overlapping. However, QGraphicsItemGroup does not re-implement boundingRegion(), so it might simply return boundingRect() as a QRegion so you would be out of luck there. You will have to test this.

If you have to look at each child item inside the two groups, you can do a little bit of optimizing. If collidesWithItem() return false, then you know that there cannot be any collision between any child items in the groups.

If there is a collision between the two groups, you do not have to examine every pair of children in each group. You have to examine only the children which lie within the intersection of the two group bounding rects (QRectF::intersected()).

nikolastojkoski
30th November 2017, 18:17
Thanks for your answer. I am kind of new to Qt and I wasn't sure if the QGraphicsItemGroup collision was a bug in my code, or it always uses the bounding rectangle.

My current solution is using a QGraphicsScene instead of QGraphicsItemGroup, that way the collision detection works perfectly. It's not an optimal solution, but hey, it works right?

The only problem is that I cannot have the same item in two QGraphicsScenes, so I end up using two copies of the same item, one in the scene connected with the view, and one in the scene representing the group, for collision detection.

in the function definition of QGraphicsScene::addItem(QGraphicsItem *item) it is stated:

If the item is already in a different scene, it will first be removed from its old scene, and then added to this scene as a top-level.

Is there a workaround for this, so that the same model could be shared by multiple scenes?

Also thanks for your solution. I will try it out and post an update.

d_stranz
30th November 2017, 21:09
The only problem is that I cannot have the same item in two QGraphicsScenes, so I end up using two copies of the same item, one in the scene connected with the view, and one in the scene representing the group, for collision detection.

This seems like a very strange solution. The whole idea of scenes is that they are meant to be shared among multiple views, and it is the view that determines which part of the scene it displays and at what scale. You are turning this idea upside-down by using two different scenes but only one view.

QGraphicsItem instances have the same kind of parent-child relationships that QWidgets do: they can have only one parent, and if you try to add them to more than one parent as a child, they will be removed from the first parent and become "owned" by the second parent instead.

I think a better solution would be to derive a new class from QGraphicsItemGroup and override the collidesWithItem() method. All of your item groups will be instances of this new class, and all methods remain the same as defined in QGraphicsItemGroup except the overridden one. In the new method, you do what I suggested: first, determine if the overall bounding rects intersect (by calling the QGraphicsItemGroup implementation). If they don't you're done. If they do, then depending on whether the other colliding item is a single item or another group, you check each item in your group to see if it intersects with the other item or with a member of the other group.

The overhead of trying to keep the items in two scenes synchronized has to be at least as complex as an optimized algorithm for detecting collisions between the members of two item groups.