Search code examples
qt2dcollision-detectionrectanglesaffinetransform

Mapping boundingRect()'s to parent item and intersecting yields unexpected results for collision handling


Here is how I'm handling collision of two graphics items. It gets called in mouseMoveEvent as well as itemChange. Like so:

void GraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    auto delta = event->pos() - event->lastPos();
    auto new_pos = handleCollisions(pos() + delta);
    setPos(new_pos);
}

The new_pos thing is so I can call it in itemChange and return that as the value. However, this version of collision handling leaves new_pos unchanged. Instead, we move the items that collide with this one (as we move this one by mouse). I'm trying to take the intersection of the two involved bounding rects and shifting the other one by the shorter dimension of the rectangls' intersection. The result, which is unexpected, is that the rectangles aren't moving just that little amount, but instead by the entire width (or height) of either of the boundingRects() (they're the same size). I've tracked it down to that, so the code below that computes the intersection of the bounding rects in the parent's position space (where pos(), setPos() are relative to), is wrong.

So how do I do I accomplish this?

Here is a picture of what I want: enter image description here

QPointF GraphicsItem::handleCollisions(const QPointF& new_pos) const {
    auto collisions = collidingItems();
    auto rect = mapToParent(boundingRect().translated(new_pos - pos())).boundingRect();
    for(int k=0; k < collisions.count(); k++)
    {
        auto item = collisions[k];
        if (item->parentItem() == parentItem()) {
            auto rect1 = mapToParent(item->boundingRect()).boundingRect();
            rect1 = rect1.intersected(rect);
            qDebug() << (rect1.width());
            qDebug() << (rect1.height());
            auto v = rect1.center() - rect.center();
            if (v.x() >= 0) {
                if (v.y() >= 0) {
                    if (rect1.width() <= rect1.height())
                        item->setPos(item->pos() + QPointF(-rect1.width(), 0));
                    else
                        item->setPos(item->pos() + QPointF(0, -rect1.height()));
                }
                else {
                    if (rect1.width() <= rect1.height())
                        item->setPos(item->pos() + QPointF(rect1.width(), 0));
                    else
                        item->setPos(item->pos() + QPointF(0, rect1.height()));
                }
            }
            else {
                if (v.y() >= 0) {
                    if (rect1.width() <= rect1.height())
                        item->setPos(item->pos() + QPointF(rect1.width(), 0));
                    else
                        item->setPos(item->pos() + QPointF(0, -rect1.height()));
                }
                else {
                    if (rect1.width() <= rect1.height())
                        item->setPos(item->pos() + QPointF(rect1.width(), 0));
                    else
                        item->setPos(item->pos() + QPointF(0, rect1.height()));
                }
            }
        }
    }
    return new_pos;     // return this position unchanged
}

Solution

  • I have found it.

    item->mapToParent(...)
    

    instead of

    mapToParent(...)
    

    should be called in the loop.