fix my code:

import numpy as np
import pygame

class BasicMeshMaterial:
    def __init__(self, color, obj):
        self.color = color  # Kolor w formacie RGB, np. (255, 0, 0) dla czerwonego
        self.object = obj

    def draw_material(self, screen, screen_width, screen_height, fov=256, viewer_distance=5):
        # Obracanie i projekcja wierzchołków
        vertices = self.object.rotate(self.object.angle_x, self.object.angle_y, self.object.angle_z)
        projected_vertices = [self.object.project(v, screen_width, screen_height, fov, viewer_distance) for v in vertices]

        # Usuń wierzchołki, których nie można wyrzutować
        projected_vertices = [v for v in projected_vertices if v is not None]

        if len(projected_vertices) == len(vertices):  # Jeśli wszystkie wierzchołki są poprawnie wyrzutowane
            self.draw_faces(screen, projected_vertices)

    def draw_faces(self, screen, projected_vertices):
        faces = self.object.get_faces()  # Pobiera ściany z obiektu
        for face in faces:
            pygame.draw.polygon(screen, self.color, [projected_vertices[i] for i in face])

class Cube:
    def __init__(self, x, y, z, width, height, depth, material=None):
        self.position = np.array([x, y, z])
        self.width = width
        self.height = height
        self.depth = depth
        self.angle_x = 0
        self.angle_y = 0
        self.angle_z = 0
        self.material = material if material else BasicMeshMaterial((255, 255, 255), self)  # domyślnie biały materiał

    def setPosition(self, new_position):
        self.position = np.array(new_position)

    def getVertices(self):
        x, y, z = self.position
        return [
            [x, y, z],
            [x + self.width, y, z],
            [x + self.width, y + self.height, z],
            [x, y + self.height, z],
            [x, y, z + self.depth],
            [x + self.width, y, z + self.depth],
            [x + self.width, y + self.height, z + self.depth],
            [x, y + self.height, z + self.depth]
        ]

    def get_faces(self):
        return [
            (0, 1, 2, 3),  # Przód
            (4, 5, 6, 7),  # Tył
            (0, 3, 7, 4),  # Lewo
            (1, 2, 6, 5),  # Prawo
            (3, 2, 6, 7),  # Góra
            (0, 1, 5, 4)   # Dół
        ]

    def rotate(self, angle_x, angle_y, angle_z):
        cos_x, sin_x = np.cos(angle_x), np.sin(angle_x)
        cos_y, sin_y = np.cos(angle_y), np.sin(angle_y)
        cos_z, sin_z = np.cos(angle_z), np.sin(angle_z)

        rotation_x = np.array([
            [1, 0, 0],
            [0, cos_x, -sin_x],
            [0, sin_x, cos_x]
        ])

        rotation_y = np.array([
            [cos_y, 0, sin_y],
            [0, 1, 0],
            [-sin_y, 0, cos_y]
        ])

        rotation_z = np.array([
            [cos_z, -sin_z, 0],
            [sin_z, cos_z, 0],
            [0, 0, 1]
        ])

        rotation_matrix = rotation_x @ rotation_y @ rotation_z
        rotated_vertices = []
        for vertex in self.getVertices():
            rotated_vertex = np.dot(rotation_matrix, np.array(vertex) - self.position) + self.position
            rotated_vertices.append(rotated_vertex)
    
        return rotated_vertices

    def project(self, vertex, screen_width, screen_height, fov, viewer_distance, epsilon=0.1):
        z = vertex[2] + epsilon
        if z <= 0:
            return None  # Ignoruj wierzchołki "za kamerą"
        
        factor = fov / z
        x = vertex[0] * factor + screen_width / 2
        y = -vertex[1] * factor + screen_height / 2
        return (int(x), int(y))

    def draw(self, screen, screen_width, screen_height, fov=256, viewer_distance=5):
        self.material.draw_material(screen, screen_width, screen_height, fov, viewer_distance)

class Piramyd:
    def __init__(self, x, y, z, width, height, depth, material=None):
        self.position = np.array([x, y, z])
        self.width = width
        self.height = height
        self.depth = depth
        self.angle_x = 0
        self.angle_y = 0
        self.angle_z = 0
        self.material = material if material else BasicMeshMaterial((255, 255, 255), self)  # Domyślnie biały materiał

    def setPosition(self, new_position):
        self.position = np.array(new_position)

    def getVertices(self):
        x, y, z = self.position
        return [
            [x, y, z],  # Wierzchołek 0 (dół lewy przedni)
            [x + self.width, y, z],  # Wierzchołek 1 (dół prawy przedni)
            [x + self.width, y, z + self.depth],  # Wierzchołek 2 (dół prawy tylny)
            [x, y, z + self.depth],  # Wierzchołek 3 (dół lewy tylny)
            [(x + x + self.width) / 2, y + self.height, (z + z + self.depth) / 2]  # Wierzchołek 4 (wierzchołek piramidy)
        ]

    def get_faces(self):
        return [
            (0, 1, 4),  # Przód
            (1, 2, 4),  # Prawo
            (2, 3, 4),  # Tył
            (3, 0, 4),  # Lewo
            (0, 1, 2, 3)  # Dół
        ]

    def rotate(self, angle_x, angle_y, angle_z):
        cos_x, sin_x = np.cos(angle_x), np.sin(angle_x)
        cos_y, sin_y = np.cos(angle_y), np.sin(angle_y)
        cos_z, sin_z = np.cos(angle_z), np.sin(angle_z)

        rotation_x = np.array([
            [1, 0, 0],
            [0, cos_x, -sin_x],
            [0, sin_x, cos_x]
        ])

        rotation_y = np.array([
            [cos_y, 0, sin_y],
            [0, 1, 0],
            [-sin_y, 0, cos_y]
        ])

        rotation_z = np.array([
            [cos_z, -sin_z, 0],
            [sin_z, cos_z, 0],
            [0, 0, 1]
        ])

        rotation_matrix = rotation_x @ rotation_y @ rotation_z
        rotated_vertices = []
        for vertex in self.getVertices():
            rotated_vertex = np.dot(rotation_matrix, np.array(vertex) - self.position) + self.position
            rotated_vertices.append(rotated_vertex)
        
        return rotated_vertices

    def project(self, vertex, screen_width, screen_height, fov, viewer_distance):
        factor = fov / (viewer_distance + vertex[2])
        x = vertex[0] * factor + screen_width / 2
        y = -vertex[1] * factor + screen_height / 2
        return (int(x), int(y))

    def draw(self, screen, screen_width, screen_height, fov=256, viewer_distance=5):
        self.vertices = self.rotate(self.angle_x, self.angle_y, self.angle_z)
        projected_vertices = [self.project(v, screen_width, screen_height, fov, viewer_distance) for v in self.vertices]
        
        if all(v is not None for v in projected_vertices):  # Sprawdź, czy wszystkie wierzchołki są widoczne
            self.draw_faces(screen, projected_vertices)

    def draw_faces(self, screen, projected_vertices):
        faces = self.get_faces()  # Pobiera ściany z obiektu
        
        for i, face in enumerate(faces):
            color = self.material.color
            if i == 4:  # Kolor dolnej ściany
                color = (150, 150, 150)
            pygame.draw.polygon(screen, color, [projected_vertices[j] for j in face])

class Plane:
    def __init__(self, x, y, z, width, depth, material=None):
        self.position = np.array([x, y, z])
        self.width = width
        self.depth = depth
        self.material = material if material else BasicMeshMaterial((255, 255, 255), self)  # Domyślnie biały materiał

    def setPosition(self, new_position):
        self.position = np.array(new_position)

    def getVertices(self):
        x, y, z = self.position
        return [
            [x, y, z],
            [x + self.width, y, z],
            [x + self.width, y + self.depth, z],
            [x, y + self.depth, z]
        ]

    def get_faces(self):
        return [(0, 1, 2, 3)]  # Dół

    def draw(self, screen, screen_width, screen_height, fov=256, viewer_distance=5):
        vertices = self.getVertices()
        projected_vertices = [self.project(v, screen_width, screen_height, fov, viewer_distance) for v in vertices]
        pygame.draw.polygon(screen, self.material.color, projected_vertices)

    def project(self, vertex, screen_width, screen_height, fov, viewer_distance):
        factor = fov / (viewer_distance + vertex[2])
        x = vertex[0] * factor + screen_width / 2
        y = -vertex[1] * factor + screen_height / 2
        return (int(x), int(y))