Kreis in Pygame unterteilen

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Hallo,

ich habe in Pygame mit

Code: Alles auswählen

pygame.draw.circle(screen, white, (250, 250), 250, 1)
einen Kreis gezeichnet.
Ich möchte nun Linien durch diesen Kreis zeichnen. Und zwar acht Linien, die den Kreis in 16 gleichgroße Teile teilen.
Einige Linien, wie diese hier

Code: Alles auswählen

pygame.draw.aaline(screen, yellow, [0, 0], [500, 500])
(Das Fenster ist 500x500 groß) sind ja denkbar einfach. Aber ich kann mich natürlich nicht auf Dauer an den Abmessungen des Fensters orientieren, denn dann werden die Teile nicht gleich groß.

Kann mir jemand einen Tipp geben, wie ich das hinbekomme? Ich stelle mir vor, dass er die Werte für die Koordinaten der Linien aus einer Winkelangabe nimmt. Oder gibt es andere Wege diese zu berechnen. Ich habe schon gesucht und mit dem math modul und pi rumexperimentiert, komme aber nicht weiter.

Wäre toll, wenn jemand da eine Idee hätte.

Danke!
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Startpunkt ist immer das Zentrum. Und die x und y Koordinate ist cos(Winkel) * Radius und sin(Winkel) * Radius. Und der Winkel ist i * PI * 2 / 8, wobei i von 0 - 7 läuft.
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Danke, das hat schonmal sehr geholfen.

Ich führe das in folgender Funktion aus:

Code: Alles auswählen

def lines():
    zahlenliste = range(0, 7)
    r = 250
    circle = pygame.draw.circle(screen, white, (250, 250), r, 1)
    for i in zahlenliste:
        w = i * math.pi * 2 / 4
        line_01 = pygame.draw.aaline(screen, yellow, [250, 250], [math.cos(w)*r, math.sin(w)*r])

Es ist aber nur ein Viertel des Kreises mit Linien befüllt. Wenn ich die Formel zu "w" wie von dir beschrieben übernehme, sind es zu viele Linien in dem Viertel, daher habe ich aus der 8 eine 4 gemacht. Aber irgendwas scheine ich zudem noch falsch zu machen... :?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Du läßt nur sieben statt acht Linien zeichnen. Und die Endpositionen sind liegen auf einem Kreis der um 0/0 zentriert ist, und nicht um 250/250. Mit den Rückgabewerten von aaline und circle machst Du nichts.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also meine Formel war schon korrekt. Wenn da was bei dir nicht gepasst hat, war es etwas anderes.

Code: Alles auswählen

>>> import math as m
>>> [i * m.pi * 2 / 8 for i in range(8)]
[0.0, 0.7853981633974483, 1.5707963267948966, 2.356194490192345, 3.141592653589793, 3.9269908169872414, 4.71238898038469, 5.497787143782138]
>>> rads = [i * m.pi * 2 / 8for i in range(8)]
>>> [m.degrees(r) for r in rads]
[0.0, 45.0, 90.0, 135.0, 180.0, 225.0, 270.0, 315.0]
Du musst aber auch den Startpunkt auf die errechnete Kreiskoordinate addieren. Denn das geht ja nur um den Nullpunkt.
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Tut mir leid, was du da gemacht hast, verstehe ich nicht :|
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es zeigt einfach nur, das meine Formel korrekt war. Wenn du es nicht verstehst ist das nicht schlimm, dann musst du es eben glauben. Wobei das ja nur zwei for schleifen sind. Was daran ist so ungewöhnlich?

Wenn dein Ding also nicht richtig dargestellt wird, liegt es an etwas andrem.
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Ich glaube dir das ganz sicher dass die Formel richtig ist, mein mathematisches Verständnis hinkt aber noch hinterher.
Nach diversen Tutorials über Kreisgleichungen und Umrechnung von Bogenmaß zu Winkelangaben etc... bin ich nur ein bisschen schlauer... nur ein bisschen.

Was genau meinst du mit
Denn das geht ja nur um den Nullpunkt.
?
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Formel berechnet die Koordinaten um den Nullpunkt. Du malst deinen Kreis aber irgendwo. Also musst du den errechneten Punkt auf den Kreismittelpunkt addieren. Mal dir die Sache mal auf Kästchenpapier auf. Dann kommt das Verständnis einfacher.
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Ah, okay... geschnallt.

aktuell sieht es so aus:

Code: Alles auswählen

line_01 = pygame.draw.aaline(screen, yellow, [250, 250], [math.cos(math.radians(0)) * r + 250, math.sin(math.radians(0)) * r + 250])
line_02 = pygame.draw.aaline(screen, yellow, [250, 250], [math.cos(math.radians(22.5)) * r + 250, math.sin(math.radians(22.5)) * r + 250])
usw... für jede Linie einzeln eben.

Es funktioniert. Ich werde das aber noch ändern. Ich musste mir das erstmal so herleiten, damit ich verstehe, was passiert und wie es funktioniert. Aus den einzelnen Linien mache ich aber noch eine Schleife und werde deine Formel wieder einbringen. Aber schonmal vielen Dank für die Hilfe!
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst wie Sirius3 schon sagte die Ergebnisse von aaline nicht an einen Namen binden. Und du solltest auch den Kreismittelpunkt nicht fest verdrahten. Sondern mit Variablen gestalten.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@k-net,

kleiner Tipp, bzw. Herausforderung: Wenn du die Linien durch den Mittelpunkt kreuzen lässt, brauchst du sie nur vier mal zu zeichnen.

Code: Alles auswählen

# Nur ein Ausschnitt, kein fertiges Programm, angle, radius, center_x, center_y müssen natürlich vorher definiert sein
x_distance = math.cos(math.radians(angle)) * radius
y_distance = math.sin(math.radians(angle)) * radius
pygame.draw.aaline(screen, yellow, [center_x - x_distance, center_y - y_distance], [center_x + x_distance, center_y + y_distance])
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Hallo,

ich habe erneut ein kleines Rechenproblem.

Auf meinem Rad habe ich nun mehrere Kugeln, die sich die Speichen hin und her entlang bewegen.

Bild

Kugel 1 (links unten):

Code: Alles auswählen

    def draw_ball_01(self):
        ball_01 = pygame.draw.circle(screen, self.color, (self.ball_01_x, self.ball_01_y), self.radius, self.width)
        self.ball_01_x += self.speed
        if self.ball_01_x <= math.cos(math.radians(180)) * self.r + self.r:
            self.speed = 3
        elif self.ball_01_x >= math.cos(math.radians(0)) * self.r + self.r:
            self.speed = -3
Kugel 2 (darüber)

Code: Alles auswählen

    def draw_ball_02(self):
        ball_02 = pygame.draw.circle(screen, self.color, (self.ball_02_x, self.ball_02_y), self.radius, self.width)
        self.ball_02_x += self.speed 
        self.ball_02_y += self.speed
        if self.ball_02_x <= math.cos(math.radians(202.5)) * self.r + self.r:
            self.speed = 3
        elif self.ball_02_y >= math.sin(math.radians(22.5)) * self.r + self.r:
            self.speed = -3
Kugel 3 (über 2)

Code: Alles auswählen

    def draw_ball_03(self):
        ball_03 = pygame.draw.circle(screen, self.color, (self.ball_03_x, self.ball_03_y), self.radius, self.width)
        self.ball_03_x += self.speed / 1.414
        self.ball_03_y += self.speed / 1.414
        if self.ball_03_y <= math.sin(math.radians(225)) * self.r + self.r:
            self.speed = 3
        elif self.ball_03_x >= math.cos(math.radians(45)) * self.r + self.r:
            self.speed = -3
usw...

Das hin und her bewegen zwischen den Punkten und auf der Linie klappt gut. Kugel 1 und 3 und natürlich 5 laufen synchron und passieren gleichzeitig den Mittelpunkt. bei Kugel drei habe ich mich an dem Verhältnis einer Diagonalen zu den Seitenlängen eines Quadrates orientiert.

Aber Teufel auch für Kugel 2 und 4 bekomme ich das nicht hin :x
Kann mir bitte jemand da weiterhelfen?

Danke!
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mir ist unklar was diese Kugeln tun sollen. Kannst du das mal beschreiben?

Was aber klar ist: du musst dringend Klassen, Argumente und Variablen benutzen. Statt mehrfach den nahezu gleichen Code zu kopieren. Das ist fehlerträchtig und kompliziert.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt für jeden Ball eine eigene Funktion zu schreiben, benutzt man eine passende Datenstruktur.
Wenn alle Bälle immer den gleichen Abstand zum Mittelpunkt haben sollen, ist das noch einfacher:

Code: Alles auswählen

    def draw_balls(self):
        for i in range(8):
            angle = i * math.pi / 4
            x = math.cos(angle) * self.ball_position + self.r
            y = math.sin(angle) * self.ball_position + self.r
            pygame.draw.circle(screen, self.color, (x, y), self.radius, self.width)
        self.ball_position -= 1
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

__deets__ hat geschrieben: Donnerstag 24. Juni 2021, 17:48 Mir ist unklar was diese Kugeln tun sollen. Kannst du das mal beschreiben?
Letztendlich möchte ich diesen optischen Effekt nachbilden:
https://www.youtube.com/watch?v=pNe6fsaCVtI

Jede Kugel bewegt sich eigentlich nur auf ihrer Linie hin und her, aber mit den passenden Abständen entsteht der Effekt, dass sie sich umeinander drehen würden. So wie ich das bisher habe, sieht das auch schon ganz nett aus. Es fehlt noch, dass die Kugeln zum Ende hin ihre Geschwindigkeit verlangsamen und eben die richtige Geschwindigkeit für die Kugeln die ich benannt habe.
Was aber klar ist: du musst dringend Klassen, Argumente und Variablen benutzen. Statt mehrfach den nahezu gleichen Code zu kopieren. Das ist fehlerträchtig und kompliziert.
Das ist mir absolut klar! Ich schreibe den Code aktuell so, weil ich es nicht besser kann. Ich mache das nur, um Python autodidaktisch zu lernen (aus Spaß übrigens, in meinem Alltag oder Beruf hab ich davon nix) und dafür gehe ich langsam vor (auch weil ich nicht so wahnsinnig viel Freizeit habe). Bei dem Code so wie er jetzt ist, weiß ich in jeder Zeile, was passiert. Das ist mir wichtig. Ich möchte auch keine fertigen Lösungen haben (naja, manchmal vielleicht schon) sondern mir das selber erarbeiten, eben um das zu kapieren. Ich möchte aber natürlich eure Unterstützungsbereitschaft auch nicht überstrapazieren. Aber wenn ich eine Formel genannt bekomme, die ich nicht verstehe, habe ich nichts davon und kann die nicht auf spätere Projekte anwenden. Beim Autofahren fängt man ja auch nicht auf der Rennstrecke an, sondern übt erstmal. Daher mache ich das so scheibchenweise in der Hoffnung, niemanden zu nerven :)

Im weiteren Verlauf würde ich natürlich versuchen, den häufig gleich oder ähnlich verwendeten Code in eine elegantere Datenstruktur zu setzen. Das ist aber häufig eine Verständnisfrage. Kennt ihr die Leute, wenn der Lehrer in der Oberstufe irgendwelche wüsten mathematischen Formeln an die Tafel geschrieben hat, die darin direkt irgendwelche Zusammenhänge gesehen haben und angefangen haben, in ihre Hefte zu schreiben? Ich war so einer nicht :D
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@k-net: Was die Geschwindigkeit angeht, würde ich mal einfach eine Sinus-Schwingung probieren.

Das `pygame.math`-Modul könnte auch interessant sein.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Hi,
könntest du das etwas präziser benennen? Vielleicht hast du irgendwo ein Beispiel oder Tutorial an dem ich mich orientieren kann?
Danke!
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du willst eine oszillierende Bewegung. Der Sinus ist eine oszillierende Schwingung.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe jetzt erst das Video gesehen. Und damit ist klar, dass du einen Sinus nehmen *musst*. Das sind 8 Phasenverschobene Sinusschwingungen über den gesamten Durchmesser.
Antworten