sich drehender Würfel

Fragen zu Tkinter.
Antworten
beginner
User
Beiträge: 5
Registriert: Dienstag 28. April 2015, 07:49

Code: Alles auswählen

from tkinter import*

tk = Tk()
canvas = Canvas(tk, width=400, height=400)                  
canvas.pack()

canvas.create_rectangle(10, 10, 100, 100)                  
canvas.create_rectangle(50, 50, 150, 150)
canvas.create_line(10, 10, 50, 50)
canvas.create_line(10, 100, 50, 150)
canvas.create_line(100, 10, 150, 50)
canvas.create_line(100, 100, 150, 150)
Hier mal mein Problem, also ich will einen Würfel Programmieren der sich dann auch dreht. Da ist schon mal der Code für den Würfel. Jetzt weiss ich aber nicht wie ich weiter machen soll damit er sich dreht.
Schon mal vielen Dank im Vorraus. :)
Zuletzt geändert von Anonymous am Dienstag 15. März 2016, 10:30, insgesamt 1-mal geändert.
Grund: Quelltext in Code-Tags gesetzt.
BlackJack

@beginner: Du müsstest aus den hart kodierten Koordinaten eine Datenstruktur machen welche die Punkte und die Verbindungen beschreibt. Wobei die Punkte als 3D-Koordinaten repräsentiert werden müssten. Dann musst Du diese 3D-Koordinaten auf die 2D-Ebene projizieren/umrechnen und Code schreiben der Dir so eine ein 3D-Drahtgittermodell beschreibende Datenstruktur in 2D auf ein Canvas bringt, beziehungsweise die Koordinaten von bereits dargestellten Linien aktualisiert. Dann brauchst Du Code der so eine Datenstruktur her nimmt und eine neue, rotierte Datenstruktur daraus berechnet. Und das müsstest Du dann wiederholt anwenden mit sich verändernden Werten für den/die Rotationswinkel und das Ergebnis jeweils grafisch darstellen. Eigentlich ganz einfach. ;-)

Bevor Du so etwas machst, solltest Du Dich mit Funktionen, objektorientierter Programmierung, und ereignisbasierter Programmierung vertraut machen, denn das wirst Du als Voraussetzungen brauchen wenn das Programm verständlich und wartbar werden soll.
beginner
User
Beiträge: 5
Registriert: Dienstag 28. April 2015, 07:49

Danke @JackBlack. Ich werde mal probieren es umzusetzen. :)
Sirius3
User
Beiträge: 17761
Registriert: Sonntag 21. Oktober 2012, 17:20

@beginner: auf Seiten der Mathematik solltest Du Dich zusätzlich in Vektorrechnung und Rotationsmatrizen auskennen. Wenn Du statt Parallelprojektion perspektivisch projizieren willst, ist es vielleicht einfacher, statt den Würfel zu drehen den Beobachter um den Würfel wandern zu lassen.
beginner
User
Beiträge: 5
Registriert: Dienstag 28. April 2015, 07:49

Ich bin jetzt ändlich etwas weiter gekommen, aber jetzt kommt schon das nächste Problem. Ich habe jetzt eine Art gestell des Würfels, was sich auch dreht. Jetzt weiss ich nur nicht wie ich die Punkte verbinde damit der Würfel komplett ist.

Code: Alles auswählen

from tkinter import*
import math,  time
#--------------------------
tk = Tk()
canvas = Canvas(tk, width=400, height=400)                   # Breitstellen des Grafikfensters
canvas.pack()
#----------------------------Geruest zeichnen
#canvas.create_rectangle(100, 100, 200, 200)                    #Mittelpunkt vordere Flaeche (150,150)
#canvas.create_rectangle(50, 50, 150, 150)                    #Mittelpunkt hintere Flaeche (100,100)
#canvas.create_line(100, 100, 50, 50)
#canvas.create_line(100, 200, 50, 150)
#canvas.create_line(200, 100, 150, 50)
#canvas.create_line(200, 200, 150, 150)
#----------------------------Drehung
winkel, r = 0, 70
while winkel < 360 :
 x1=100 + r * math.cos(((winkel+1)/180)*math.pi)
 y1=100 + r * math.sin(((winkel+1)/180)*math.pi)
 canvas.create_rectangle(x1, y1, x1+1, y1+1, outline="black") 
 #----------------------------loeschen
 x1=100 + r * math.cos(((winkel)/180)*math.pi)
 y1=100 + r * math.sin(((winkel)/180)*math.pi)
 canvas.create_rectangle(x1, y1, x1+1, y1+1, outline="#eeeeee")
 #----------------------------------------------------------------
 x2=100 + r * math.cos(((winkel+180)/180)*math.pi)
 y2=100 + r * math.sin(((winkel+180)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="black") 
 #----------------------------loeschen
 x2=100 + r * math.cos(((winkel+179)/180)*math.pi)
 y2=100 + r * math.sin(((winkel+179)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="#eeeeee")
 #------------------------------------------------------------------
 x2=100 + r * math.cos(((winkel+90)/180)*math.pi)
 y2=100 + r * math.sin(((winkel+90)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="black") 
 #----------------------------loeschen
 x2=100 + r * math.cos(((winkel+89)/180)*math.pi)
 y2=100 + r * math.sin(((winkel+89)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="#eeeeee")
 #---------------------------------------------------------------------
 x2=100 + r * math.cos(((winkel+270)/180)*math.pi)
 y2=100 + r * math.sin(((winkel+270)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="black") 
 #----------------------------loeschen
 x2=100 + r * math.cos(((winkel+269)/180)*math.pi)
 y2=100 + r * math.sin(((winkel+269)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="#eeeeee")
 #----------------------------------------------------------------------
 #----------------------------------------------------------------------
 x1=150 + r * math.cos(((winkel+1)/180)*math.pi)
 y1=150 + r * math.sin(((winkel+1)/180)*math.pi)
 canvas.create_rectangle(x1, y1, x1+1, y1+1, outline="black") 
 #----------------------------loeschen
 x1=150 + r * math.cos(((winkel)/180)*math.pi)
 y1=150 + r * math.sin(((winkel)/180)*math.pi)
 canvas.create_rectangle(x1, y1, x1+1, y1+1, outline="#eeeeee")
 #----------------------------------------------------------------
 x2=150 + r * math.cos(((winkel+180)/180)*math.pi)
 y2=150 + r * math.sin(((winkel+180)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="black") 
 #----------------------------loeschen
 x2=150 + r * math.cos(((winkel+179)/180)*math.pi)
 y2=150 + r * math.sin(((winkel+179)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="#eeeeee")
 #------------------------------------------------------------------
 x2=150 + r * math.cos(((winkel+90)/180)*math.pi)
 y2=150 + r * math.sin(((winkel+90)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="black") 
 #----------------------------loeschen
 x2=150 + r * math.cos(((winkel+89)/180)*math.pi)
 y2=150 + r * math.sin(((winkel+89)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="#eeeeee")
 #---------------------------------------------------------------------
 x2=150 + r * math.cos(((winkel+270)/180)*math.pi)
 y2=150 + r * math.sin(((winkel+270)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="black") 
 #----------------------------loeschen
 x2=150 + r * math.cos(((winkel+269)/180)*math.pi)
 y2=150 + r * math.sin(((winkel+269)/180)*math.pi)
 canvas.create_rectangle(x2, y2, x2+1, y2+1, outline="#eeeeee")
 #----------------------------------------------------------------------
 
 
 #-----------------------------------------------------------------
 winkel = winkel + 1
 time.sleep(0.01)
 tk.update()
Zuletzt geändert von Anonymous am Dienstag 19. April 2016, 09:51, insgesamt 1-mal geändert.
Grund: Quelltext in Code-Tags gesetzt.
BlackJack

@beginner: Also als allererstes solltest Du mal die Einrückung so machen, dass man sie auch sieht. Konvention sind vier Leerzeichen pro Ebene und nicht eins.

Inhaltlich falsch ist Dein ”löschen” von Punkten. `Canvas` ist keine Pixelgraphik sondern Vektorgrafik und so wie Du vorgehst erzeugst Du in jedem Schritt mehr und mehr Grafikobjekte die Speicher und Rechenzeit verbrauchen. Wenn Du etwas vom `Canvas`-Exemplar löschen willst, musst Du Dir die ID merken und damit die `delete()`-Methode vom `Canvas` aufrufen, oder Du verpasst allen Bestandteilen des Würfels ein Tag und rufst `delete()` einmal mit dem Tag auf. Was man bei Vektorgrafik auch machen kann ist nachträglich die Koordinaten zu verändern, dann spart man sich das löschen und neu erstellen der Grafikobjekte.

In der Schleife stehen viel zu viele Codewiederholungen und hart kodierte magische Zahlen. Überleg mal was für ein Aufwand es wäre den Code so zu ändern das bei jedem Schritt um 2 Grad gedreht wird. Dafür sollte man nur an einer Stelle aus einer 1 eine 2 machen müssen, und nicht beim Code für jeden Punkt zusätzlich noch eins von einem konstanten literalen Wert abziehen müssen.

Speicher die Punktkoordinaten in einer Datenstruktur. Dann die Punktpaare die durch Linien verbunden werden sollen ebenfalls. Und dann kannst Du Code schreiben der eine Datenstruktur mit gedrehten Punktkoordinaten erstellt, und welchen der diese dann zeichnet und die Linien zwischen den Punkten auch.

Das ist ja gar keine 3D-Grafik wenn Du nur x und y für jeden Punkt hast. Bei einem Würfel haben die Eckpunkte ja eigentlich x-, y-, und z-Koordinaten.

Du benutzt Tk falsch. Normalerweise registriert man Methoden die bei bestimmten Ereignissen aufgerufen werden und ruft dann die Tk-Hauptschleife auf. Wenn man sich die Hauptschleife mit `time.sleep()` und `update()` selber bastelt, schränkt man sich unnötig ein.
BlackJack

Habe da mal was gebastelt:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import Tkinter as tk
from collections import namedtuple
from math import cos, sin
from random import random


class Point(namedtuple('Point', 'x y z')):

    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y, self.z + other.z)

    def __mul__(self, number):
        return Point(self.x * number, self.y * number, self.z * number)

    def rotated(self, alpha=0, beta=0, gamma=0):
        return Point(
            (
                self.x * cos(gamma) * cos(beta)
                + self.y * (
                    -sin(gamma) * cos(alpha)
                    + cos(gamma) * sin(beta) * sin(alpha)
                )
                + self.z * (
                    sin(gamma) * sin(alpha)
                    + cos(gamma) * sin(beta) * cos(alpha)
                )
            ),
            (
                self.x * sin(gamma) * cos(beta)
                + self.y * (
                    cos(gamma) * cos(alpha)
                    + sin(gamma) * sin(beta) * sin(alpha)
                )
                + self.z * (
                    -cos(gamma) * sin(alpha)
                    + sin(gamma) * sin(beta) * cos(alpha)
                )
            ),
            (
                self.x * -sin(beta)
                + self.y * cos(beta) * sin(alpha)
                + self.z * cos(beta) * cos(alpha)
            )
        )

    def projected(self, distance):
        quotient = 1 - self.z / distance
        return Point(self.x / quotient, self.y / quotient, 0)


class Mesh(object):

    def __init__(self, vertices, edges):
        self.vertices = vertices
        self.edges = edges
        self.tag = 'mesh{0}'.format(id(self))

    def __add__(self, point):
        return Mesh([v + point for v in self.vertices], self.edges)

    def __mul__(self, number):
        return Mesh([v * number for v in self.vertices], self.edges)

    def rotated(self, alpha, beta, gamma):
        return Mesh(
            [v.rotated(alpha, beta, gamma) for v in self.vertices], self.edges
        )

    def projected(self, distance):
        return Mesh([v.projected(distance) for v in self.vertices], self.edges)

    def draw(self, canvas):
        for start_index, end_index in self.edges:
            start_point = self.vertices[start_index]
            end_point = self.vertices[end_index]
            canvas.create_line(
                start_point.x, start_point.y, end_point.x, end_point.y,
                tag=self.tag
            )

    def delete(self, canvas):
        canvas.delete(self.tag)


class MeshUI(tk.Frame):

    def __init__(self, parent, mesh, size):
        tk.Frame.__init__(self, parent)
        self.mesh = mesh
        self.displayed_mesh = None
        self.size = size
        self.zoom_factor = size / 4
        self.distance = self.size * 2
        self.offset = Point(self.size / 2, self.size / 2, 0)
        self.alpha, self.beta, self.gamma = 0, 0, 0
        self.alpha_delta = random() * 0.025
        self.beta_delta = random() * 0.025
        self.gamma_delta = random() * 0.025
        self.canvas = tk.Canvas(self, width=self.size, height=self.size)
        self.canvas.pack(side=tk.TOP)
        self.step()

    def update_display(self):
        if self.displayed_mesh:
            self.displayed_mesh.delete(self.canvas)
        self.displayed_mesh = (
            (
                self.mesh.rotated(self.alpha, self.beta, self.gamma)
                * self.zoom_factor
            ).projected(self.distance) + self.offset
        )
        self.displayed_mesh.draw(self.canvas)

    def step(self):
        self.alpha += self.alpha_delta
        self.beta += self.beta_delta
        self.gamma += self.gamma_delta
        self.update_display()
        self.after(50, self.step)


def main():
    cube = Mesh(
        [
            Point(1, 1, 1),
            Point(-1, 1, 1),
            Point(1, -1, 1),
            Point(1, 1, -1),
            Point(1, -1, -1),
            Point(-1, -1, 1),
            Point(-1, 1, -1),
            Point(-1, -1, -1),
        ],
        [
            (0, 1),
            (0, 2),
            (0, 3),
            (1, 5),
            (1, 6),
            (2, 4),
            (2, 5),
            (3, 4),
            (3, 6),
            (4, 7),
            (5, 7),
            (6, 7),
        ]
    )

    root = tk.Tk()
    root.title('Rotating Cube')
    mesh_ui = MeshUI(root, cube, 500)
    mesh_ui.pack()
    root.mainloop()


if __name__ == '__main__':
    main()
beginner
User
Beiträge: 5
Registriert: Dienstag 28. April 2015, 07:49

Vielen Dank @BlackJack :)
Ich glaub ich sollte mir mehr Zeit für Python nehmen, bei meinen nächsten Programmen.
Antworten