Fare tarafından hareket ettirilen QGraphicsItem şekillerinin çarpışmalarından kaçınma

0

Soru

Burada, bir Qgraphicsscene'de qgraphicsellipseıtem'lerden oluşan dairelerin çarpışmalarını önleme konusunda ilginç bir tartışma gündeme getirildi. Soru, kapsamı 2 çarpışan öğeye daralttı, ancak daha büyük hedef hala kaldı, herhangi bir sayıda çarpışma için ne olacak?

Bu istenen davranıştır:

  • Bir öğe diğer öğelerin üzerine sürüklendiğinde üst üste gelmemeli, bunun yerine bu öğelerin etrafında fareye mümkün olduğunca yakın hareket etmelidir.
  • Diğer öğeler tarafından engellenirse "ışınlanmamalıdır".
  • Düzgün ve öngörülebilir bir hareket olmalı.

Hareket halindeyken daire için en iyi “güvenli” pozisyonu bulmak gittikçe zorlaştıkça, bunu bir fizik simülatörü kullanarak uygulamak için başka bir yol sunmak istedim.

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

En iyi cevabı

3

Yukarıda açıklanan davranış göz önüne alındığında, 2D katı cisim fiziği için iyi bir adaydır, belki de onsuz yapılabilir, ancak mükemmel olması zor olacaktır. Bu örnekte pymunk kullanıyorum çünkü ona aşinayım ama aynı kavramlar diğer kütüphanelerle de çalışacak.

Sahnenin fareyi temsil edecek kinematik bir gövdesi vardır ve daireler başlangıçta statik cisimlerle temsil edilir. Bir daire seçiliyken dinamik bir gövdeye geçer ve sönümlü bir yay ile fareyle sınırlandırılır. Konumu, alan her zaman aşımı aralığında belirli bir zaman adımıyla güncellendikçe güncellenir.

Öğe aslında aynı şekilde hareket ettirilmez. ItemIsMovable bayrak etkin değil, yani artık fare ile anında hareket etmiyor. Çok yakın ama küçük bir gecikme var, ancak çarpışmalara nasıl tepki verdiğini daha iyi görmek için bunu tercih edebilirsiniz. (Yani, parametreleri ayarlamak daha hızlı/**benden fare yaklaşın için iyi bile).

Öte yandan, çarpışmalar mükemmel bir şekilde ele alınır ve zaten diğer şekil türlerini destekleyecektir.

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

**Aşağıdakileri ayarlayabilir:

  • İlkbahar stiffness ve damping
  • Vücut mass ve moment eylemsizlik
  • Uzay damping
  • Space.step zaman adımı / QTimer zaman aşımı başına kaç çağrı
  • QTimer interval
2021-12-01 01:57:12

Bu mükemmel!!
drivereye

Diğer dillerde

Bu sayfa diğer dillerde

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