276 lines
8.1 KiB
Python
276 lines
8.1 KiB
Python
#Katzenaufgabe: 1. funktionierender Stand war committed.
|
|
#2. in compute.py und pyproject.toml absichtlich Änderungen/Quatsch gemacht.
|
|
#3. Diese Änderungen nicht committed.
|
|
#4. ↩ Git → Uncommitted Changes → Rollback ausgeführt.
|
|
#5. Danach waren die Katzen-Änderungen wieder verschwunden und die Dateien wieder wie vorher.
|
|
|
|
#Dann: Katze erledigt.
|
|
|
|
|
|
# Matrixfunktionen für das Projekt MatrixMania
|
|
|
|
#Was die Aufgabe war:
|
|
#opencode verwenden und python code generieren lassen
|
|
#den generierten code auf funktionalität und clean code prüfen
|
|
#in compute.py sollten wir eine matrixmultiplikation anwenden
|
|
#und das modul danach um eine Transposition und 2d rotationmatrix erweitern
|
|
#gefordert: type hints, docstrings und fehlerbehandlung mit valueerror
|
|
|
|
#auf kurz:
|
|
#Agent generiert code
|
|
#wir prüfen ihn selbst
|
|
#pep 8 beachten
|
|
#verständlicher name
|
|
#type hints
|
|
#docstrings
|
|
#fehler sinnvoll behandeln
|
|
#mindestens einen verbesserungspunkt am Agentencode erkennen
|
|
|
|
|
|
"""Matrix-Operationen ohne externe Bibliotheken (reine Python-Listen)."""
|
|
import math
|
|
from math import cos, sin
|
|
|
|
from tabulate import tabulate
|
|
|
|
#from typing import List #da wir eine aktuelle python version verwenden wäre eingebaute schreibweise mit list[float] beser
|
|
|
|
Matrix = list[list[float]] #Matrix ist hier ein typ alias für eine verschachtelte liste aus zahlen dadurch werden die type hints lesbarer
|
|
|
|
#Funktion matmul: zwei matrizen multiplizieren
|
|
def matmul(matrix_a: Matrix, matrix_b: Matrix) -> Matrix:
|
|
#jetzt kommt mein docstring hier dokumentiert open code:
|
|
#was macht die funktion?
|
|
#wleche parameter bekommt sie? (args)
|
|
#was gibt sie zurück?(returns)
|
|
#welche fehler können auftreten?(raises)
|
|
"""Multipliziere zwei Matrizen ``A * B``. #
|
|
|
|
Args:
|
|
matrix_a: Linke Matrix der Form ``(m, n)``.
|
|
matrix_b: Rechte Matrix der Form ``(n, p)``.
|
|
|
|
Returns:
|
|
Die Produktmatrix der Form ``(m, p)``.
|
|
|
|
Raises:
|
|
ValueError: Wenn ``matrix_a`` weniger Spalten hat, als ``matrix_b``
|
|
Zeilen hat, oder wenn eine der Matrizen leer ist.
|
|
"""
|
|
if not matrix_a or not matrix_b: #sicherheitsprüfung wenn matrix a leer oder matrix b leer ist, breche ab
|
|
raise ValueError("Matrizen dürfen nicht leer sein.") #fehler wird absichtlich ausgeführt
|
|
|
|
#die größe wird bestimmt
|
|
rows_a = len(matrix_a)
|
|
cols_a = len(matrix_a[0])
|
|
rows_b = len(matrix_b)
|
|
cols_b = len(matrix_b[0])
|
|
|
|
#Dimensionsprüfung
|
|
#hier ist auch mein zweiter verbesserungspunkt siehe ausführlich unten
|
|
if cols_a != rows_b:
|
|
raise ValueError(
|
|
"Breite der ersten Matrix muss der Höhe der zweiten entsprechen."
|
|
)
|
|
|
|
result: Matrix = []
|
|
for i in range(rows_a): #i steht für die zeile von a
|
|
result_row: list[float] = []
|
|
for j in range(cols_b): #j steht für die spalte von b
|
|
total = 0
|
|
for k in range(cols_a): #k läuft durch die einszlnen werte die miteinander multipliziert und addiert werden
|
|
total += matrix_a[i][k] * matrix_b[k][j] #"nimm einen wert aus der aktuellen zeile von a multipliziere ihn mit den passenden wert aus der aktuellen spalte von b und addiere das ergebnis zu total
|
|
result_row.append(total) #fügt die fertige zeile zur ergebnismatrix hinzu
|
|
result.append(result_row)
|
|
|
|
return result
|
|
|
|
#Zeilen und Spalten einer Matrix vertauschen
|
|
def transpose(matrix: Matrix) -> Matrix:
|
|
"""Transponiere eine Matrix (Zeilen werden zu Spalten).
|
|
|
|
Args:
|
|
matrix: Nicht leere, rechteckige Matrix.
|
|
|
|
Returns:
|
|
Die transponierte Matrix.
|
|
|
|
Raises:
|
|
ValueError: Wenn die Matrix leer ist oder die Zeilen unterschiedlich
|
|
viele Spalten enthalten.
|
|
"""
|
|
if not matrix or not matrix[0]:
|
|
raise ValueError("Die Matrix darf nicht leer sein.")
|
|
|
|
num_cols = len(matrix[0])
|
|
if any(len(row) != num_cols for row in matrix):
|
|
raise ValueError("Alle Zeilen müssen gleich viele Spalten haben.")
|
|
|
|
return [[matrix[row][col] for row in range(len(matrix))]
|
|
for col in range(num_cols)]
|
|
|
|
|
|
|
|
#Rotationsmatrix für einen Winkel erzeugen
|
|
def rot_2D(theta: float) -> Matrix:
|
|
"""Erzeuge die 2D-Rotationsmatrix für einen Winkel ``theta``.
|
|
|
|
Args:
|
|
theta: Rotationswinkel im Bogenmaß.
|
|
|
|
Returns:
|
|
Die Rotationsmatrix ``[[cos(theta), -sin(theta)],
|
|
[sin(theta), cos(theta)]]``.
|
|
"""
|
|
cosine = math.cos(theta)
|
|
sine = math.sin(theta)
|
|
return [[cosine, -sine], [sine, cosine]]
|
|
|
|
|
|
def rot_3D(angle: float, axis: str) -> list[list[float]]:
|
|
"""Erzeugt eine Rotationsmatrix für eine Drehung im 3D-Raum.
|
|
|
|
Args:
|
|
angle: Drehwinkel im Bogenmaß.
|
|
axis: Drehachse "x", "y" oder "z".
|
|
|
|
Returns:
|
|
Die zur Achse passende 3x3-Rotationsmatrix.
|
|
|
|
Raises:
|
|
ValueError: Wenn axis weder "x", "y" noch "z" ist.
|
|
|
|
Formeln mit c = cos(angle) und s = sin(angle):
|
|
|
|
Rotation um die x-Achse:
|
|
[1, 0, 0]
|
|
[0, c, -s]
|
|
[0, s, c]
|
|
|
|
Rotation um die y-Achse:
|
|
[ c, 0, s]
|
|
[ 0, 1, 0]
|
|
[-s, 0, c]
|
|
|
|
Rotation um die z-Achse:
|
|
[c, -s, 0]
|
|
[s, c, 0]
|
|
[0, 0, 1]
|
|
"""
|
|
c = cos(angle)
|
|
s = sin(angle)
|
|
|
|
if axis == "x":
|
|
return [
|
|
[1.0, 0.0, 0.0],
|
|
[0.0, c, -s],
|
|
[0.0, s, c],
|
|
]
|
|
|
|
if axis == "y":
|
|
return [
|
|
[c, 0.0, s],
|
|
[0.0, 1.0, 0.0],
|
|
[-s, 0.0, c],
|
|
]
|
|
|
|
if axis == "z":
|
|
return [
|
|
[c, -s, 0.0],
|
|
[s, c, 0.0],
|
|
[0.0, 0.0, 1.0],
|
|
]
|
|
|
|
raise ValueError("axis muss 'x', 'y' oder 'z' sein.")
|
|
|
|
|
|
def project_ortho(
|
|
point: tuple[float, float, float],
|
|
scale: float = 1,
|
|
) -> tuple[float, float]:
|
|
"""Projiziert einen 3D-Punkt auf die xy-Ebene und skaliert ihn."""
|
|
|
|
# Die ersten beiden Koordinaten des Punktes auslesen.
|
|
# Python beginnt beim Zählen mit 0.
|
|
x = point[0]
|
|
y = point[1]
|
|
|
|
# Die dritte Koordinate point[2] ist z.
|
|
# Für diese Projektion brauchen wir sie nicht.
|
|
|
|
# x und y skalieren und als Tupel zurückgeben.
|
|
return (x * scale, y * scale)
|
|
|
|
def project_perspective(
|
|
point: tuple[float, float, float],
|
|
focal_length: float = 1.0,
|
|
scale: float = 1.0,
|
|
camera_distance: float = 4.0,
|
|
) -> tuple[float, float]:
|
|
"""Projiziert einen 3D-Punkt perspektivisch auf eine 2D-Ebene."""
|
|
|
|
# Wir verteilen die drei Koordinaten auf einzelne Variablen.
|
|
x, y, z = point
|
|
|
|
# Die Tiefe des Punktes wird um den Kameraabstand verschoben.
|
|
depth = z + camera_distance
|
|
|
|
# Durch 0 darf nicht geteilt werden.
|
|
# Ein Punkt auf der Kameraebene kann so nicht projiziert werden.
|
|
if depth == 0:
|
|
raise ValueError(
|
|
"Der Punkt darf nicht auf der Kameraebene liegen."
|
|
)
|
|
|
|
# Wir berechnen die horizontalen und vertikalen Bildschirmkoordinaten.
|
|
projected_x = x * focal_length / depth * scale
|
|
projected_y = y * focal_length / depth * scale
|
|
|
|
# Aus dem 3D-Punkt wird ein 2D-Punkt.
|
|
return projected_x, projected_y
|
|
|
|
if __name__ == "__main__": #führe den testcode nur aus wenn compute.py direkt gestartet wird
|
|
matrix_a = [
|
|
[3, 4, -1, 4],
|
|
[-2, 2, 5, 1],
|
|
]
|
|
|
|
matrix_b = [
|
|
[1, 3, -2],
|
|
[2, 5, 1],
|
|
[-1, 4, -4],
|
|
[2, 3, 6],
|
|
]
|
|
|
|
matrix_c = matmul(matrix_a, matrix_b)
|
|
|
|
print("Ergebnis C = A * B:")
|
|
for row in matrix_c:
|
|
print(row)
|
|
|
|
matrix = [
|
|
[1, 2, 3],
|
|
[4, 5, 6],
|
|
]
|
|
|
|
print("\nTransponierte Matrix:")
|
|
for row in transpose(matrix):
|
|
print(row)
|
|
|
|
print("\nRotationsmatrix für 90 Grad (pi / 2):")
|
|
for row in rot_2D(math.pi / 2):
|
|
print(row)
|
|
|
|
print(tabulate(rot_2D(1.0)))
|
|
|
|
|
|
|
|
|
|
#Code Beurteilung durch opencode generiert
|
|
#1. Der matmul-DocString sagt "weniger Spalten", obwohl der Code allgemein auf != prüft
|
|
#Das sollte präziser dokumentiert werden weil zb 4 != 3 ist True aber ValueError
|
|
#der Code prüft nicht weniger sondern generell nicht gleich
|
|
|
|
|
|
#2.opencode verwendet noch typing.list obwohl meine python version schon die
|
|
#moderne Schreibweise mit list[...] möglich ist und sogar im skript verwendet wird |