gedrehte Rechteck - Punk Kollisionserkennung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Hallo,

Ich versuche gerade gedrehte zu erkennen, ob ein gedrehtes Rechteck mit einem Punkt kollidiert.

Ich habe dazu folgendes beim suchen gefunden:
https://gamedev.stackexchange.com/quest ... -rectangle

Das Funktioniert aber nicht richtig :(
jenachdem welche größe und drehung ich nehme, klappt es manchmal und manchmal nicht..

ich habe folgenden Code:

Code: Alles auswählen

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import Color, Rectangle, Rotate, PushMatrix, PopMatrix
import math

def collides(point_x, point_y, x_rect, y_rect, width_rect, height_rect, center_rect_x, center_rect_y, angle_rect):
    angle_sin = math.sin(angle_rect)
    angle_cos = math.cos(angle_rect)

    x = ((point_x - center_rect_x) * angle_cos - (point_y - center_rect_y) * angle_sin) + center_rect_x
    y = ((point_x - center_rect_x) * angle_sin + (point_y - center_rect_y) * angle_cos) + center_rect_y

    return x > x_rect and x < x_rect + width_rect and y > y_rect and y < y_rect + height_rect

class TestApp(App):
    def __init__(self, **kwargs):
        super(TestApp, self).__init__(**kwargs)
    def build(self):
        layout = BoxLayout()
        widget = Widget()

        with widget.canvas.before:
            PushMatrix()
            Rotate(angle=75, origin=(200, 150))
        with widget.canvas:
            Color(1, 0, 0, 1)
            self.rect = Rectangle(pos=(100, 100), size=(200, 100))
        with widget.canvas.after:
            PopMatrix()

        widget.bind(on_touch_down=self.check)
        layout.add_widget(widget)

        return layout

    def check(self, root, touch):
        print(collides(*touch.pos, *self.rect.pos, *self.rect.size, self.rect.pos[0] + self.rect.size[0]/2, self.rect.pos[1] + self.rect.size[1]/2, 75))

if __name__ ==  "__main__":
    app = TestApp()
    app.run()

wo liegt der fehler und was muss ich anders machen?
einfachTobi
User
Beiträge: 512
Registriert: Mittwoch 13. November 2019, 08:38

Hab mir ehrlich gesagt nicht genau angesehen, aber beim Überfliegen fiel mir auf: Sinus/Kosinus benötigen den Winkel im Bogenmaß.
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Hm...
ich hab das aber eigentlich genau so gemacht wie das auf der Seite steht. 🤔
Benutzeravatar
__blackjack__
User
Beiträge: 14054
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@MupfSpace: Auf der Seite steht kein konkreter Aufruf und Du rufst das halt mit einem falschen Winkelwert auf. 75 liegt nicht im erwarteten Wertebereich und sieht halt deshalb so gar nicht nach einem Winkel im Bogenmass aus sondern eher nach 75°.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Was soll ich dann da als r verwenden?
Das ist ja ein Rechteck und kein Kreis...
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du einen Winkel in Grad hast, musst du den ins Bogenmass konvertieren. Ich habe lange nicht geschaut, ob es eine eingebaute Funktion gibt, weil ich die Formel auswendig kenne:

radians = degree / 180.0 * math.pi
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: math.radians
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Das Funktioniert nicht.
Ich habe sowohl math.radians als auch angle / 180 * math.pi versucht.
Es sieht so aus als ob das ein bischen verschoben ist.

Ich habe hier ein Video dazu gemacht: https://youtu.be/Egdh6USHKnA

Code: Alles auswählen

def collides(point_x, point_y, x_rect, y_rect, width_rect, height_rect, center_rect_x, center_rect_y, angle_rect):
    radians = angle_rect / 180.0 * math.pi
    angle_sin = math.sin(radians)
    angle_cos = math.cos(radians)
    

    x = ((point_x - center_rect_x) * angle_cos - (point_y - center_rect_y) * angle_sin) + center_rect_x
    y = ((point_x - center_rect_x) * angle_sin + (point_y - center_rect_y) * angle_cos) + center_rect_y

    return x > x_rect and x < x_rect + width_rect and y > y_rect and y < y_rect + height_rect

__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist schwierig, das so rauszufummeln. Alternativ kannst du dir auch einfach die aktuelle Transformationsmatrix an der Stelle merken, wo du das Rectangle anlegst. Davon nimmst du die inverse Transformation, und die Mauskoordinate kannst du damit multiplizieren. Damit solltest du eigentlich eine Koordinate im Rectangle-Koordinatensystem bekommen.

Was auch noch sein kann: so wie due gerade vergleichst, koennte es sein, dass du annimmst, das Rectangle waere an einem Eckpunkt (0, 0) definiert. Ich glaube aber eher, dass es um den Mittelpunkt herum definiert ist. Dann saehe die Vergleichsbedingung eher so aus:

Code: Alles auswählen

    return abs(x - x_rect) < width_rect / 2 and abs(y - y_rect) < height_rect
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Wo muss ich das

Code: Alles auswählen

 return abs(x - x_rect) < width_rect / 2 and abs(y - y_rect) < height_rect
 
genau hinschreiben? anstelle von dem bisherigen?

Code: Alles auswählen

return x > x_rect and x < x_rect + width_rect and y > y_rect and y < y_rect + height_rect
das Funktioniert nicht.

wie würde das mit dem Matrix, was du gesagt hast funktionieren?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also ich habe damit mal ein bisschen rumgespielt, und ich muss sagen, ich verstehe schon nicht, wie Kivy das rotiert. Ich haette erwartet, dass das um seinen Mittelpunkt rotiert. Oder wegen mir um einen Eckpunkt. Oder um den Szenen-Mittelpuntk. Oder den Koordinatenursprung.

Nichts davon passiert. Das ist zwar rotiert, aber es ist *irgendwo*. Darum hast du wohl auch das "origin" der Rotation explizit angegeben. Das ist natuerlich auch ein Weg, aber wo der sonst steht - man weiss es nicht.

Damit ist aber auch klar, welche Information du hier zugrunde legen musst: rotiert wird der Vektor zwischen Mausposition & origin, und dann geprueft, wie sich das zum Rechteck verhaelt.

Das hier tut fuer mich:

Code: Alles auswählen

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import Color, Rectangle, Rotate, PushMatrix, PopMatrix
import math


def collides(point_x, point_y, rect_x, rect_y, width_rect, height_rect, angle_rect):
    # we need to rotate *against* the rotation of the rectangle
    # to get from world to local coordinates
    s = math.sin(math.radians(-angle_rect))
    c = math.cos(math.radians(-angle_rect))

    rel_x = point_x - rect_x
    rel_y = point_y - rect_y

    local_x = c * rel_x - s * rel_y
    local_y = s * rel_x + c * rel_y
    return abs(local_x) < width_rect / 2 and abs(local_y) < height_rect / 2


class TestApp(App):

    def __init__(self, **kwargs):
        super(TestApp, self).__init__(**kwargs)
        self._origin = (200, 150)
        self._rotation = 75

    def build(self):
        layout = BoxLayout()
        widget = Widget()

        with widget.canvas.before as c:
            PushMatrix()
            Rotate(angle=self._rotation, origin=self._origin)
        with widget.canvas:
            Color(1, 0, 0, 1)
            self.rect = Rectangle(pos=(100, 100), size=(200, 100))
        with widget.canvas.after:
            PopMatrix()

        widget.bind(on_touch_down=self.check)
        layout.add_widget(widget)

        return layout

    def check(self, root, touch):
        print(collides(*touch.pos, *self._origin, *self.rect.size, self._rotation))

if __name__ ==  "__main__":
    app = TestApp()
    app.run()
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

__deets__ hat geschrieben: Montag 8. März 2021, 12:05 Also ich habe damit mal ein bisschen rumgespielt
Wow, danke schön 😊.
Funktioniert 👍
Antworten