import colorsys
import copy
from typing import NamedTuple
import imageio
import numpy
class Colour:
r = 0
g = 0
b = 0
a = 0
def __init__(self, rgb):
if len(rgb) == 3:
self.r = rgb[0]
self.g = rgb[1]
self.b = rgb[2]
elif len(rgb) == 4:
self.r = rgb[0]
self.g = rgb[1]
self.b = rgb[2]
self.a = rgb[3]
def set_rgb(self, rgb: list[int]):
self.r = rgb[0]
self.g = rgb[1]
self.b = rgb[2]
def get_rgb(self):
return [self.r, self.g, self.b]
def set_hsv(self, hsv):
rgb = [x * 256 for x in colorsys.hsv_to_rgb(hsv[0] / 256, hsv[1] / 256, hsv[2] / 256)]
self.r = rgb[0]
self.g = rgb[1]
self.b = rgb[2]
def get_hsv(self):
hsv = [x * 256 for x in colorsys.hsv_to_rgb(self.r / 256, self.g / 256, self.b / 256)]
return [self.r, self.g, self.b]
def get_alpha(self):
if self.a > 128:
return True
else:
return False
class WindowSize(NamedTuple):
height: int
width: int
class Point2D(NamedTuple):
x: float
y: float
def get(self):
return [self.x, self.y]
def conv_coord(self, size, scale):
return Point2D((self.x * scale) + size[0]/2, (self.y * scale) + size[1]/2)
class Point3D(NamedTuple):
# todo:
# check if i have missed anything obvious
x: float
y: float
z: float
def xyz(self):
xyz: list[float] = [self.x, self.y, self.z]
return xyz
def translate(self, t_vec): # but whyy
self.x += t_vec.x
self.y += t_vec.y
self.z += t_vec.z
return self
def inv_translate(self, t_vec): # but whyy
self.x -= t_vec.x
self.y -= t_vec.y
self.z -= t_vec.z
return self
def snap(self): # sets x y z to the nearest integer
self.x = round(self.x)
self.y = round(self.y)
self.z = round(self.z)
def apply_matrix(self, rot_matrix: numpy.array):
point = numpy.array([self.x, self.y, self.z])
numpy.multiply(point, rot_matrix)
def get_2d_point(self, fp_distance):
# converts a 3d point into a 2d point (replacement for the projection function)
distance = 0
# calculate x pos
try:
x = ((fp_distance * self.x) / (self.y - distance))
# calculate y pos
y = ((fp_distance * self.z) / (self.y - distance))
except ZeroDivisionError:
x = 0
y = 0
return Point2D(x, y)
def to_list(self):
return [self.x, self.y, self.z]
class Texture:
# this class *should* be finished
# Y -> X -> Colour
pixels: list[list[Colour]] = []
def __init__(self, path: str):
self.pixels = []
image = imageio.imread(path)
for i, image_y in enumerate(image):
self.pixels.append([])
for image_x in image_y:
self.pixels[i].append(Colour(image_x))
def get(self):
return self.pixels
def get_pixel(self, x, y):
return self.pixels[y][x]
def get_pixel_alpha(self, x, y):
return self.pixels[y][x].get_alpha()
class Face:
# The face class stores a face with N points, a colour, information about edges and faces with nothing else
# todo:
# make a cleaner way of extracting values
points: list[Point3D]
colour: Colour
has_faces: bool
has_edges: bool
def __init__(self, points: list[Point3D], colour: Colour, has_edges: bool, has_faces: bool):
self.hasEdges = has_edges # whether the main face has edges
if type(colour) is list:
raise ZeroDivisionError
self.colour = colour
self.points = points
self.hasFaces = has_faces
def get_points(self):
# returns the positions of points (not rotated, not relative to the player)
return self.points
def fix_points_behind_camera(self):
for point in self.points:
if point.y <= 0.5:
self.points.remove(point)
class TexturedFace(Face):
# todo:
# write the ability to get sub faces
# figure out a way of storing the 'index' of the face (probably in the cube class)
texture: Texture
# This class inherits from Face, and instead of a base colour it has a texture
def set_texture(self, texture: Texture):
# sets the texture of the textured Face
self.texture = texture
def get_sub_faces(self, face_point_dict):
# idk
self.texture = 0
class Cube:
position: Point3D
colour: list[Colour]
texture: list[Texture]
faces: list[Face]
# ordered top - north - east - south - west - bottom
cubePoints = [Point3D([.5, .5, .5]), Point3D([.5, .5, -.5]), Point3D([.5, -.5, .5]), Point3D([.5, -.5, -.5]),
Point3D([-.5, .5, .5]), Point3D([-.5, .5, -.5]), Point3D([-.5, -.5, .5]), Point3D([-.5, -.5, -.5])]
# same as the dictionary, but no longer used. not sure why it still exists.
# cubeSides = [[3, 7, 5, 1], [2, 6, 7, 3], [4, 5, 7, 6], [0, 1, 5, 4], [0, 2, 3, 1], [6, 2, 0, 4]]
# hardcoded dict for the relative positions of cube faces
cubeFaces: list[list[Point3D]] = [
[cubePoints[3], cubePoints[5], cubePoints[7], cubePoints[1]], # top
[cubePoints[2], cubePoints[6], cubePoints[7], cubePoints[3]], # north
[cubePoints[4], cubePoints[5], cubePoints[7], cubePoints[6]], # east
[cubePoints[0], cubePoints[1], cubePoints[5], cubePoints[4]], # south
[cubePoints[0], cubePoints[2], cubePoints[3], cubePoints[1]], # west
[cubePoints[6], cubePoints[2], cubePoints[0], cubePoints[4]]] # bottom]
def __init__(self, position: Point3D, texture : list[Colour], has_faces, has_edges):
self.faces = []
self.position = Point3D([0, 0, 0])
# todo
# implement texture
self.position = position
self.colours = texture
self.has_faces = has_faces
self.has_edges = has_edges
# initialises faces when the cube is placed. Faces are in global coordinates
for i in range(6):
new_points = 0
self.faces.append(Face([point.translate(self.position) for point in copy.deepcopy(self.cubeFaces[i])],
self.colours[i], self.has_edges, self.has_faces))
print(self.position.xyz())
def get_faces(self):
return copy.deepcopy(self.faces)