En évitant les collisions de QGraphicsItem formes déplacé par la souris

0

La question

Une discussion intéressante a été soulevée ici sur la prévention des collisions entre des milieux, faite de QGraphicsEllipseItems, dans une QGraphicsScene. La question qui réduit la portée à 2 entrer en collision, mais le plus grand but est toujours resté, et pour n'importe quel nombre de collisions?

C'est le comportement souhaité:

  • Quand un élément est déplacée sur d'autres objets qu'ils ne devraient pas se chevaucher, il devrait plutôt se déplacer autour de ces éléments, aussi près que possible de la souris.
  • Il ne doit pas “se téléporter” si elle est bloquée par d'autres éléments.
  • Il doit être lisse et prévisible mouvement.

Comme cela devient de plus en plus complexe de trouver le meilleur “sécuritaire” de la position du cercle alors qu'il est en mouvement je voulais présenter une autre façon de mettre en œuvre ce à l'aide d'un simulateur physique.

collision pymunk pyqt5 python
2021-11-23 02:01:24
1

La meilleure réponse

3

Étant donné le comportement décrit ci-dessus, il est un bon candidat pour la 2D physique de corps rigide, peut-être, il peut être fait sans, mais il serait difficile d'obtenir la perfection. Je suis à l'aide de pymunk dans cet exemple parce que je suis familier avec elle, mais les mêmes concepts de travailler avec d'autres bibliothèques.

La scène a une cinématique corps pour représenter la souris et les cercles sont représentés par des organismes d'abord. Alors qu'un cercle est sélectionné, il passe à un organisme dynamique et est contraint à la souris par une amortie de printemps. Sa position est mise à jour à mesure que l'espace est mis à jour par un pas de temps sur chaque intervalle de délai d'attente.

L'article n'est pas réellement déplacé de la même manière que le ItemIsMovable indicateur n'est pas activé, ce qui signifie qu'il ne bouge plus instantanément avec la souris. Il est très proche, mais il y a un petit retard, bien que vous préférerez peut-être de mieux voir comment il réagit à des collisions. (Même si, vous pouvez affiner les paramètres de l'avoir à se déplacer plus vite ou plus proche de la souris que j'ai fait**).

D'autre part, les collisions sont gérées parfaitement et va déjà prendre en charge d'autres types de formes.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import pymunk

class Circle(QGraphicsEllipseItem):

    def __init__(self, r, **kwargs):
        super().__init__(-r, -r, r * 2, r * 2, **kwargs)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.static = pymunk.Body(body_type=pymunk.Body.STATIC)
        self.circle = pymunk.Circle(self.static, r)
        self.circle.friction = 0
        mass = 10
        self.dynamic = pymunk.Body(mass, pymunk.moment_for_circle(mass, 0, r))
        self.updatePos = lambda: self.setPos(*self.dynamic.position, dset=False)

    def setPos(self, *pos, dset=True):
        super().setPos(*pos)
        if len(pos) == 1:
            pos = pos[0].x(), pos[0].y()
        self.static.position = pos
        if dset:
            self.dynamic.position = pos

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemSelectedChange:
            space = self.circle.space
            space.remove(self.circle.body, self.circle)
            self.circle.body = self.dynamic if value else self.static
            space.add(self.circle.body, self.circle)
        return super().itemChange(change, value)

    def paint(self, painter, option, widget):
        option.state &= ~QStyle.State_Selected
        super().paint(painter, option, widget)


class Scene(QGraphicsScene):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.space = pymunk.Space()
        self.space.damping = 0.02
        self.body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
        self.space.add(self.body)
        self.timer = QTimer(self, timerType=Qt.PreciseTimer, timeout=self.step)
        self.selectionChanged.connect(self.setConstraint)

    def setConstraint(self):
        selected = self.selectedItems()
        if selected:
            shape = selected[0].circle
            if not shape.body.constraints:
                self.space.remove(*self.space.constraints)
                spring = pymunk.DampedSpring(
                    self.body, shape.body, (0, 0), (0, 0),
                    rest_length=0, stiffness=100, damping=10)
                spring.collide_bodies = False
                self.space.add(spring)

    def step(self):
        for i in range(10):
            self.space.step(1 / 30)
        self.selectedItems()[0].updatePos()

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if self.selectedItems():
            self.body.position = event.scenePos().x(), event.scenePos().y()
            self.timer.start(1000 / 30)
            
    def mouseMoveEvent(self, event):            
        super().mouseMoveEvent(event)
        if self.selectedItems():
            self.body.position = event.scenePos().x(), event.scenePos().y()
        
    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        self.timer.stop()

    def addCircle(self, x, y, radius):
        item = Circle(radius)
        item.setPos(x, y)
        self.addItem(item)
        self.space.add(item.circle.body, item.circle)
        return item


if __name__ == '__main__':
    app = QApplication(sys.argv)
    scene = Scene(0, 0, 1000, 800)
    for i in range(7, 13):
        item = scene.addCircle(150 * (i - 6), 400, i * 5)
        item.setBrush(Qt.GlobalColor(i))    
    view = QGraphicsView(scene, renderHints=QPainter.Antialiasing)
    view.show()
    sys.exit(app.exec_())

**Pouvez régler les éléments suivants:

  • Printemps stiffness et damping
  • Corps mass et moment d'inertie
  • L'espace damping
  • Space.step pas de temps / combien d'appels par QTimer délai d'attente
  • QTimer interval
2021-12-01 01:57:12

C'est parfait!!
drivereye

Dans d'autres langues

Cette page est dans d'autres langues

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................