pygame Clipping an runder Fläche

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Jakob.brunner
User
Beiträge: 3
Registriert: Donnerstag 5. Februar 2015, 17:58
Wohnort: Schwyz

Hallo, hat jemand Erfahrungen mit pygame.
Kann man eine Clipping-Area in Form eines Kreises erstellen?
Oder wie könnte man es lösen, dass Die gezeichneten Objekte nicht über eine Kreislinie hinaus gezeichnet werden?

Code: Alles auswählen

import datetime, math, pygame

class GrafikAnzeige:
    def __init__(self, datum=datetime.datetime.today()):
        self.BLAU = (0, 102, 204)
        self.SCHWARZ = (0, 0, 0)
        self.ROT = (255, 0, 0)
        self.GRUEN = (0, 102, 0)
        self.WEISS = (255, 255, 255)
        self.datum = datum

    def Grafik(self):
        pygame.init()
        sbreite = shöhe = 600
        self.mX =  self.mY = sbreite / 2.0

        self.screen = pygame.display.set_mode((600, 600))
        pygame.display.set_caption("Sonneverlauf")
        self.screen.fill(self.WEISS)

        for i in range(1, 10, 1):
            pygame.draw.ellipse(self.screen, self.SCHWARZ, (self.mX - self.mX / 10.0 * i, self.mY - self.mY / 10.0 * i, sbreite / 10.0 * i, shöhe / 10.0 * i), 1)
        for i in range(0, 360, 10):
            r1 = self.mX / 10.0 * 9.0
            r2 = self.mX / 10.0 * 1.0
            w = float(i / 180.0 * math.pi)
            sinI = math.sin(w)
            cosI = math.cos(w)
            px1 = self.mX + sinI * r1
            py1 = self.mY - cosI * r1
            px2 = self.mX + sinI * r2
            py2 = self.mY - cosI * r2
            pygame.draw.line(self.screen, self.SCHWARZ, (px1, py1), (px2, py2), 1)

        font = pygame.font.Font(None, 12)
        for i in range(1, 10, 1):
            text = font.render("%02d" % (90 -(i * 10)), 1, self.SCHWARZ)
            self.screen.blit(text, (self.mX - 10, self.mY - 5 + self.mY / 10.0 * i))

        pygame.draw.line(self.screen, self.ROT, (0.0, 400.0),(600.0, 150.0), 4)
        pygame.display.flip()

        mainloop = True
        while mainloop:
            for event in pygame.event.get():
                if event.type == pygame.QUIT or (event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE):
                    mainloop = False

        pygame.quit()

grafik = GrafikAnzeige(datetime.datetime.now)
grafik.Grafik()
Jakob Brunner
BlackJack

@Jakob.brunner: Man könnte mit einem `Surface` mit einem Alphakanal arbeiten. Aber letztendlich betreibst Du da am Ende doch sowieso schon 2D-Vektorrechnungen, dann könntest Du auch das klippen von Linien auf einen Kreis selber auf mathematischem Weg lösen.

Anmerkungen zum Quelltext: Einige Namen entsprechen nicht der empfohlenen Schreibweise aus dem Style Guide for Python Code. Durchgehend in Grossbuchstaben schreibt man eigentlich nur Namen für Konstanten, die wiederum gehören auf Modulebene oder als Attribute an Klassen, aber nicht immer wieder erneut an jedes Exemplar.

Abgesehen von der Schreibweise ist `Grafik` kein guter Name für eine Funktion oder Methode. Dafür wählt man normalerweise Tätigkeiten die beschreiben was die Funktion oder Methode tut.

Desweiteren sollte man keine Abkürzungen oder kryptischen Prä- oder Postfixe verwenden. Wenn `sbreite` für die Breite von `screen` stehen soll, dann sollte das auch `screen_breite` heissen, damit der Leser nicht raten muss wofür das `s` steht.

Eine Klasse die nur aus einer `__init__()` und *einer* weiteren Methode besteht ist in aller Regel keine Klasse sondern eine unnötig kompliziert ausgedrückte Funktion. Da die Hauptfunktion am Ende nur aus diesem einen Funktionsaufruf besteht, könnte man sich die Funktion auch ganz sparen und in die Hauptfunktion verschieben.

Das übergebene `datum` wird überhaupt nicht benutzt. Den Default-Wert halte ich nicht für sinnvoll und beim tatsächlichen Aufruf wird gar kein Datumsobjekt überheben sondern eine Methode. Was sicher aufgefallen wäre, wenn der Wert auch tatsächlich verwendet werden würde.

Bei den literalen Zahlen kann man sicher einiges an sinnvolle Namen binden.

`mY` wird falsch berechnet. Das stimmt nur wenn ``sbreite == shoehe`` gilt. Letztendlich würde ich diese Werte auch gar nicht selber berechnen sondern mir vom `screen` das `Rect`-Objekt holen und dann dessen `center` abfragen.

Zeilen sollten nicht länger als 80 Zeichen werden. Sonst wird das schwer lesbar. Insbesondere wenn es sich um Aufrufe mit längeren Formeln handelt, kann man das deutlich leichter lesen wenn entsprechend umgebrochen wird, so dass man die einzelnen Argumente besser erkennen kann.

`float()` mit einem Wert aufzurufen der bereits vom Typ `float` ist, macht keinen Sinn. Für die Umrechnung von Grad nach Radians gibt es ausserdem in `math` schon eine Funktion. Die ganzen Winkelberechnungen könnte man mit komplexen Zahlen kürzer lösen. Ausserdem haben wir hier durchnummerierte Namen mit denen jeweils fast das gleiche gemacht wird — das schreit nach einer Auslagerung in eine Funktion.

In die Hauptschleife sollte man vielleicht etwas Verzögerung einbauen, damit der Rechner die CPU nicht mit 100% ”warten” beschäftigt.

Ich lande dann ungefähr bei diesem Zwischenergebnis:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import cmath
import math
import pygame


BLAU = (0, 102, 204)
SCHWARZ = (0, 0, 0)
ROT = (255, 0, 0)
GRUEN = (0, 102, 0)
WEISS = (255, 255, 255)


def polar2rect(radius, degrees, center=(0, 0)):
    result = cmath.rect(radius, math.radians(degrees))
    center_x, center_y = center
    return (result.real + center_x, result.imag + center_y)


def main():
    pygame.init()
    pygame.display.set_caption('Sonnenverlauf')

    screen = pygame.display.set_mode((600, 600))
    screen_rect = screen.get_rect()
    screen.fill(WEISS)

    for i in range(1, 10):
        ellipse_rect = pygame.Rect(
            0, 0, screen_rect.width / 10 * i, screen_rect.height / 10 * i
        )
        ellipse_rect.center = screen_rect.center
        pygame.draw.ellipse(screen, SCHWARZ, ellipse_rect, 1)
    
    for i in range(0, 360, 10):
        pygame.draw.line(
            screen,
            SCHWARZ,
            polar2rect(screen_rect.centerx / 10 * 9, i, screen_rect.center),
            polar2rect(screen_rect.centerx / 10 * 1, i, screen_rect.center),
            1
        )

    font = pygame.font.Font(None, 12)
    for i in range(1, 10, 1):
        text = font.render('{0:02d}'.format(90 - (i * 10)), 1, SCHWARZ)
        text_rect = text.get_rect()
        text_rect.center = screen_rect.center
        text_rect.move_ip(-5, -5 + screen_rect.centery / 10 * i)
        screen.blit(text, text_rect)

    pygame.draw.line(screen, ROT, (0, 400), (600, 150), 4)
    pygame.display.flip()

    clock = pygame.time.Clock()
    run = True
    while run:
        for event in pygame.event.get():
            if (
                event.type == pygame.QUIT
                or event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE
            ):
                run = False
        clock.tick(25)
    
    pygame.quit()


if __name__ == '__main__':
    main()
Antworten