Search code examples
pythonpyqtpyqt4pyqt5qgraphicsitem

Precise detection of collision between two graphical items


I have two graphics items, RectItem and CurveItem. I need to detect precisely the collision between these two items. I implemented for booth items the shape() method. I verify if collision is detected when I move one of the items from scene self.collidingItems(self.rect_item, mode=Qt.IntersectsItemShape.

I collide the items by moving them in the scene, but the collision is detected only on top part of the CurveItem. I don't understand where I made a mistake.

enter image description here

Code:

class RectItem(QGraphicsRectItem):
    def __init__(self, x, y, w, h):
        super().__init__(x, y, w, h)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setPen(QPen(Qt.cyan))

    def shape(self):
        path = QPainterPath()
        path.addRect(self.boundingRect())
        return path


class CurveItem(QGraphicsItem):
    def __init__(self):
        super().__init__()
        self.path = self._setupPath()

    def paint(self, painter, styles, widget=None):
        painter.drawPath(self.path)

    def boundingRect(self):
        return QRectF()

    def shape(self):
        return self.path

    def _setupPath(self):
        path = QPainterPath()
        path.moveTo(0, 0)
        path.cubicTo(99, 0, 50, 50, 99, 99)
        path.cubicTo(0, 99, 50, 50, 0, 0)
        return path


class Scene(QGraphicsScene):
    def __init__(self):
        super().__init__()
        self.curve_item = CurveItem()
        self.rect_item = RectItem(-50, -50, 25, 25)

        self.addItem(self.curve_item)
        self.addItem(self.rect_item)

    def mouseMoveEvent(self, e):
        print(self.collidingItems(self.rect_item, mode=Qt.IntersectsItemShape))
        super().mouseMoveEvent(e)

Solution

  • Although you will not use the boundingRect() to detect the collision you must return a QRect that is the minimum size that covers the shape, for this we can use the boundingRect() method of QPainterPath, besides it is not necessary to overwrite the shape QGraphicsRectItem.

    class RectItem(QGraphicsRectItem):
        def __init__(self, *args):
            super().__init__(*args)
            self.setFlag(QGraphicsItem.ItemIsMovable)
            self.setPen(QPen(Qt.cyan))
    
    class CurveItem(QGraphicsItem):
        def __init__(self):
            super().__init__()
            self.path = self._setupPath()
    
        def paint(self, painter, styles, widget=None):
            painter.drawPath(self.path)
    
        def boundingRect(self):
            return self.path.boundingRect()
    
        def shape(self):
            return self.path
    
        def _setupPath(self):
            path = QPainterPath()
            path.moveTo(0, 0)
            path.cubicTo(99, 0, 50, 50, 99, 99)
            path.cubicTo(0, 99, 50, 50, 0, 0)
            return path
    
    
    class Scene(QGraphicsScene):
        def __init__(self):
            super().__init__()
            self.curve_item = CurveItem()
            self.rect_item = RectItem(-50, -50, 25, 25)
    
            self.addItem(self.curve_item)
            self.addItem(self.rect_item)
    
        def mouseMoveEvent(self, e):
            print(self.collidingItems(self.rect_item, mode=Qt.IntersectsItemShape))
            super().mouseMoveEvent(e)