Unikając kolizji kształtów QGraphicsItem przewożonych myszą

0

Pytanie

Tu rozpętała się ciekawa dyskusja na temat zapobiegania kolizji okrążeń wykonanych z QGraphicsEllipseItems, w QGraphicScene. Pytanie zawężony obszar działania do 2 kolidujących obiektów, ale bardziej ambitny cel wciąż pozostawała, jak o dowolnej liczby kolizji?

Jest to pożądane zachowania:

  • Gdy jeden element jest przeciągnięty na wierzchu innych elementów, oni nie muszą się pokrywać, zamiast tego powinien poruszać się wokół tych elementów jak najbliżej myszy.
  • On nie musi się "teleportować", jeśli go blokują inne przedmioty.
  • To musi być płynne i przewidywalne ruch.

Ponieważ jest to coraz bardziej trudne, aby znaleźć najlepszy "bezpiecznej" pozycji do koła podczas jazdy, chciałem przedstawić inny sposób realizacji tego za pomocą fizycznego symulatora.

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

Najlepsza odpowiedź

3

Biorąc pod uwagę zachowanie opisane powyżej, to dobry kandydat dla 2D fizyki ciała stałego, możliwe, że bez tego można się obejść, ale byłoby to trudne do osiągnięcia doskonałości. Używam pymunk w tym przykładzie, ponieważ znam go, ale te same koncepcje będą pracować z innymi bibliotekami.

Scena ma кинематическое ciało do reprezentowania myszy, a kręgi początkowo prezentowane są statycznymi obiektami. Przy wyborze koła przełączy się w dynamiczne ciało i utrzymuje się za pomocą myszy dampingowanej sprężyny. Jego pozycja jest aktualizowana w miarę aktualizacji na przestrzeni określonego czasu krok w każdym przedziale czasu oczekiwania.

Element jest w rzeczywistości porusza się nie tak, jak ItemIsMovable flaga nie jest włączony, co oznacza, że nie porusza się natychmiast za pomocą myszy. To bardzo blisko, ale jest małe opóźnienie, ale można zdecydować się na to, aby lepiej widzieć, jak on reaguje na uderzenia. (Nawet w tym przypadku można dokładnie skonfigurować ustawienia, aby poruszał się szybciej/bliżej myszy, niż ja**).

Z drugiej strony, zderzenia są przetwarzane idealnie i już będą wspierać inne rodzaje kształtów.

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_())

**Można skonfigurować następujące ustawienia:

  • Wiosna stiffness i damping
  • Ciało mass i moment inercja
  • Kosmos damping
  • Space.step krok czasu / ile połączeń za timeout QTimer
  • QTimer interval
2021-12-01 01:57:12

To świetnie!!
drivereye

W innych językach

Ta strona jest w innych językach

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