Evitând coliziunile de QGraphicsItem forme mutat cu mouse-ul

0

Problema

O discuție interesantă a fost ridicat aici despre prevenirea coliziunilor de cerc, făcută din QGraphicsEllipseItems, într-un QGraphicsScene. Întrebarea redus domeniul de aplicare a 2 elemente se ciocnesc dar scopul mai mare încă a rămas, pentru orice număr de coliziuni?

Acest lucru este dorit de comportament:

  • Atunci când un obiect este tras peste alte elemente care nu trebuie să se suprapună, în schimb, ar trebui să se mute în jurul valorii de aceste elemente cât mai aproape de mouse-ul.
  • Acesta nu ar trebui să "teleporta" în cazul în care este blocat de alte elemente.
  • Ar trebui să fie netedă și previzibil mișcare.

Ca acest lucru devine din ce în ce mai complexe pentru a găsi cel mai "safe" poziția de cerc în timp ce se deplasează am vrut să vă prezint un alt mod de a pune în aplicare acest lucru, folosind un simulator de fizica.

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

Cel mai bun răspuns

3

Având în vedere comportamentul descris mai sus este un bun candidat pentru 2D fizica a corpului rigid, poate se poate face fără, dar ar fi dificil să iasă perfect. Eu sunt, folosind pymunk în acest exemplu, pentru că sunt familiarizat cu ea, dar aceleași concepte va lucra cu alte biblioteci.

Scena are o cinematică a corpului pentru a reprezenta mouse-ul și cercuri sunt reprezentate prin corpuri statice inițial. În timp ce un cerc este selectat se trece de la un organism dinamic și este constrâns să mouse-ul prin amortizată de primăvară. Poziția sa este actualizat, după cum spațiul este actualizat de către un anumit pas de timp pe fiecare interval de expirare.

Articolul este, de fapt, nu s-a mutat în același mod ca ItemIsMovable steagul nu este activat, ceea ce înseamnă că acesta nu mai mișcă instantaneu cu mouse-ul. E foarte aproape, dar există o mică întârziere, deși s-ar putea prefera acest lucru pentru a vedea mai bine cum reacționează la coliziuni. (Chiar și așa, puteți ajusta parametrii să se miște mai repede/mai aproape de mouse-ul decât am făcut-o**).

Pe de altă parte, coliziunile sunt manipulate perfect și va sprijini deja alte tipuri de forme.

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

**Puteți regla următoarele:

  • Primavara stiffness și damping
  • Corpul mass și moment de inerție
  • Spațiu damping
  • Space.step pas de timp / cât de multe apeluri pe QTimer timeout
  • QTimer interval
2021-12-01 01:57:12

Acest lucru este perfect!!
drivereye

În alte limbi

Această pagină este în alte limbi

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