Organized modules into individual files
This commit is contained in:
1
tools/animation-tools/.gitignore
vendored
1
tools/animation-tools/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
venv/
|
venv/
|
||||||
|
__pycache__/
|
||||||
18
tools/animation-tools/CustomWidgets.py
Normal file
18
tools/animation-tools/CustomWidgets.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from pygame_widgets.slider import Slider
|
||||||
|
|
||||||
|
class ColorPicker:
|
||||||
|
def __init__(self, screen, x_pos: int, y_pos: int, width: int, height: int):
|
||||||
|
width = max(50, width)
|
||||||
|
self.sliders: list[Slider] = [
|
||||||
|
Slider(screen, int(x_pos + i*(width/3)), y_pos, int((width-50)/3), height, min=0, max=255, step=1, vertical=True) for i in range(3)
|
||||||
|
]
|
||||||
|
for slider in self.sliders:
|
||||||
|
slider.enable()
|
||||||
|
|
||||||
|
def get_color(self) -> tuple[int]:
|
||||||
|
# return (0,0,0)
|
||||||
|
return tuple([slider.getValue() for slider in self.sliders])
|
||||||
|
|
||||||
|
def set_color(self, color: tuple[int]):
|
||||||
|
for i, slider in enumerate(self.sliders):
|
||||||
|
slider.setValue(color[i])
|
||||||
17
tools/animation-tools/MatrixMath.py
Normal file
17
tools/animation-tools/MatrixMath.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import math
|
||||||
|
from pygame.math import Vector3
|
||||||
|
|
||||||
|
def project(vector, w, h, fov, distance):
|
||||||
|
factor = math.atan(fov / 2 * math.pi / 180) / (distance + vector.z)
|
||||||
|
x = vector.x * factor * w + w / 2
|
||||||
|
y = -vector.y * factor * w + h / 2
|
||||||
|
return Vector3(x, y, vector.z)
|
||||||
|
|
||||||
|
def rotate_vertices(vertices, angle, axis):
|
||||||
|
return [v.rotate(angle, axis) for v in vertices]
|
||||||
|
def scale_vertices(vertices, s):
|
||||||
|
return [Vector3(v[0]*s[0], v[1]*s[1], v[2]*s[2]) for v in vertices]
|
||||||
|
def translate_vertices(vertices, t):
|
||||||
|
return [v + Vector3(t) for v in vertices]
|
||||||
|
def project_vertices(vertices, w, h, fov, distance):
|
||||||
|
return [project(v, w, h, fov, distance) for v in vertices]
|
||||||
70
tools/animation-tools/Scene.py
Normal file
70
tools/animation-tools/Scene.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
from Shapes import Mesh
|
||||||
|
from MatrixMath import *
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
class Scene:
|
||||||
|
def __init__(self, meshes, fov, distance):
|
||||||
|
self.meshes: list[Mesh] = meshes
|
||||||
|
self.fov = fov
|
||||||
|
self.distance = distance
|
||||||
|
self.euler_angles = [0, 0, 0]
|
||||||
|
|
||||||
|
def transform_vertices(self, vertices, width, height):
|
||||||
|
transformed_vertices = vertices
|
||||||
|
axis_list = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
|
||||||
|
for angle, axis in reversed(list(zip(list(self.euler_angles), axis_list))):
|
||||||
|
transformed_vertices = rotate_vertices(transformed_vertices, angle, axis)
|
||||||
|
transformed_vertices = project_vertices(transformed_vertices, width, height, self.fov, self.distance)
|
||||||
|
return transformed_vertices
|
||||||
|
|
||||||
|
def point_in_polygon(self, point, polygon):
|
||||||
|
""" Determine if the point (x, y) is inside the polygon """
|
||||||
|
x, y = point
|
||||||
|
n = len(polygon)
|
||||||
|
inside = False
|
||||||
|
p1x, p1y = polygon[0]
|
||||||
|
for i in range(n + 1):
|
||||||
|
p2x, p2y = polygon[i % n]
|
||||||
|
if y > min(p1y, p2y):
|
||||||
|
if y <= max(p1y, p2y):
|
||||||
|
if x <= max(p1x, p2x):
|
||||||
|
if p1y != p2y:
|
||||||
|
xints = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
|
||||||
|
if p1x == p2x or x <= xints:
|
||||||
|
inside = not inside
|
||||||
|
p1x, p1y = p2x, p2y
|
||||||
|
return inside
|
||||||
|
|
||||||
|
def get_mesh_from_xy(self, pos: tuple[int]) -> Mesh:
|
||||||
|
x, y = pos
|
||||||
|
closest_mesh = None
|
||||||
|
closest_z = float('inf')
|
||||||
|
|
||||||
|
for mesh in self.meshes:
|
||||||
|
transformed_vertices = self.transform_vertices(mesh.get_vertices(), *pygame.display.get_surface().get_size())
|
||||||
|
avg_z = mesh.calculate_average_z(transformed_vertices)
|
||||||
|
for i, avg_z_value in avg_z:
|
||||||
|
polygon = mesh.create_polygon(mesh.get_face(i), transformed_vertices)
|
||||||
|
if self.point_in_polygon((x, y), polygon):
|
||||||
|
if avg_z_value < closest_z:
|
||||||
|
closest_z = avg_z_value
|
||||||
|
closest_mesh = mesh
|
||||||
|
|
||||||
|
return closest_mesh
|
||||||
|
|
||||||
|
def draw(self, surface):
|
||||||
|
|
||||||
|
polygons = []
|
||||||
|
for mesh in self.meshes:
|
||||||
|
transformed_vertices = self.transform_vertices(mesh.get_vertices(), *surface.get_size())
|
||||||
|
avg_z = mesh.calculate_average_z(transformed_vertices)
|
||||||
|
for z in avg_z:
|
||||||
|
#for z in sorted(avg_z, key=lambda x: x[1], reverse=True):
|
||||||
|
pointlist = mesh.create_polygon(mesh.get_face(z[0]), transformed_vertices)
|
||||||
|
polygons.append((pointlist, z[1], mesh.face_color, mesh.edge_color))
|
||||||
|
#pygame.draw.polygon(surface, (128, 128, 192), pointlist)
|
||||||
|
#pygame.draw.polygon(surface, (0, 0, 0), pointlist, 3)
|
||||||
|
|
||||||
|
for poly in sorted(polygons, key=lambda x: x[1], reverse=True):
|
||||||
|
pygame.draw.polygon(surface, poly[2], poly[0])
|
||||||
|
pygame.draw.polygon(surface, poly[3], poly[0], 3)
|
||||||
42
tools/animation-tools/Shapes.py
Normal file
42
tools/animation-tools/Shapes.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
from MatrixMath import *
|
||||||
|
from pygame.math import Vector3
|
||||||
|
|
||||||
|
class Mesh():
|
||||||
|
def __init__(self, vertices, faces):
|
||||||
|
self.__vertices = [Vector3(v) for v in vertices]
|
||||||
|
self.__faces = faces
|
||||||
|
self.face_color = (0, 0, 0)
|
||||||
|
self.edge_color = (0,0,0)
|
||||||
|
|
||||||
|
def rotate(self, angle, axis):
|
||||||
|
self.__vertices = rotate_vertices(self.__vertices, angle, axis)
|
||||||
|
def scale(self, s):
|
||||||
|
self.__vertices = scale_vertices(self.__vertices, s)
|
||||||
|
def translate(self, t):
|
||||||
|
self.__vertices = translate_vertices(self.__vertices, t)
|
||||||
|
|
||||||
|
def calculate_average_z(self, vertices):
|
||||||
|
return [(i, sum([vertices[j].z for j in f]) / len(f)) for i, f in enumerate(self.__faces)]
|
||||||
|
|
||||||
|
def get_face(self, index):
|
||||||
|
return self.__faces[index]
|
||||||
|
def get_vertices(self):
|
||||||
|
return self.__vertices
|
||||||
|
|
||||||
|
def create_polygon(self, face, vertices):
|
||||||
|
return [(vertices[i].x, vertices[i].y) for i in [*face, face[0]]]
|
||||||
|
|
||||||
|
def set_face_color(self, color : tuple[int]):
|
||||||
|
self.face_color = color
|
||||||
|
|
||||||
|
def set_edge_color(self, color : tuple[int]):
|
||||||
|
self.edge_color = color
|
||||||
|
|
||||||
|
class Cube(Mesh):
|
||||||
|
def __init__(self):
|
||||||
|
vertices = [(-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1), (-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1)]
|
||||||
|
faces = [(0,1,2,3), (1,5,6,2), (5,4,7,6), (4,0,3,7), (3,2,6,7), (1,0,4,5)]
|
||||||
|
super().__init__(vertices, faces)
|
||||||
|
|
||||||
|
def set_position(self, position : tuple):
|
||||||
|
super().translate(position)
|
||||||
@@ -1,151 +1,13 @@
|
|||||||
import math
|
|
||||||
import pygame
|
import pygame
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from pygame_widgets.slider import Slider
|
|
||||||
import pygame_widgets
|
import pygame_widgets
|
||||||
|
from Shapes import *
|
||||||
def project(vector, w, h, fov, distance):
|
from Scene import Scene
|
||||||
factor = math.atan(fov / 2 * math.pi / 180) / (distance + vector.z)
|
from CustomWidgets import ColorPicker
|
||||||
x = vector.x * factor * w + w / 2
|
|
||||||
y = -vector.y * factor * w + h / 2
|
|
||||||
return pygame.math.Vector3(x, y, vector.z)
|
|
||||||
|
|
||||||
def rotate_vertices(vertices, angle, axis):
|
|
||||||
return [v.rotate(angle, axis) for v in vertices]
|
|
||||||
def scale_vertices(vertices, s):
|
|
||||||
return [pygame.math.Vector3(v[0]*s[0], v[1]*s[1], v[2]*s[2]) for v in vertices]
|
|
||||||
def translate_vertices(vertices, t):
|
|
||||||
return [v + pygame.math.Vector3(t) for v in vertices]
|
|
||||||
def project_vertices(vertices, w, h, fov, distance):
|
|
||||||
return [project(v, w, h, fov, distance) for v in vertices]
|
|
||||||
|
|
||||||
class Mesh():
|
|
||||||
def __init__(self, vertices, faces):
|
|
||||||
self.__vertices = [pygame.math.Vector3(v) for v in vertices]
|
|
||||||
self.__faces = faces
|
|
||||||
self.face_color = (0, 0, 0)
|
|
||||||
self.edge_color = (0,0,0)
|
|
||||||
|
|
||||||
def rotate(self, angle, axis):
|
|
||||||
self.__vertices = rotate_vertices(self.__vertices, angle, axis)
|
|
||||||
def scale(self, s):
|
|
||||||
self.__vertices = scale_vertices(self.__vertices, s)
|
|
||||||
def translate(self, t):
|
|
||||||
self.__vertices = translate_vertices(self.__vertices, t)
|
|
||||||
|
|
||||||
def calculate_average_z(self, vertices):
|
|
||||||
return [(i, sum([vertices[j].z for j in f]) / len(f)) for i, f in enumerate(self.__faces)]
|
|
||||||
|
|
||||||
def get_face(self, index):
|
|
||||||
return self.__faces[index]
|
|
||||||
def get_vertices(self):
|
|
||||||
return self.__vertices
|
|
||||||
|
|
||||||
def create_polygon(self, face, vertices):
|
|
||||||
return [(vertices[i].x, vertices[i].y) for i in [*face, face[0]]]
|
|
||||||
|
|
||||||
def set_face_color(self, color : tuple[int]):
|
|
||||||
self.face_color = color
|
|
||||||
|
|
||||||
def set_edge_color(self, color : tuple[int]):
|
|
||||||
self.edge_color = color
|
|
||||||
|
|
||||||
class Scene:
|
|
||||||
def __init__(self, meshes, fov, distance):
|
|
||||||
self.meshes: list[Mesh] = meshes
|
|
||||||
self.fov = fov
|
|
||||||
self.distance = distance
|
|
||||||
self.euler_angles = [0, 0, 0]
|
|
||||||
|
|
||||||
def transform_vertices(self, vertices, width, height):
|
|
||||||
transformed_vertices = vertices
|
|
||||||
axis_list = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
|
|
||||||
for angle, axis in reversed(list(zip(list(self.euler_angles), axis_list))):
|
|
||||||
transformed_vertices = rotate_vertices(transformed_vertices, angle, axis)
|
|
||||||
transformed_vertices = project_vertices(transformed_vertices, width, height, self.fov, self.distance)
|
|
||||||
return transformed_vertices
|
|
||||||
|
|
||||||
def point_in_polygon(self, point, polygon):
|
|
||||||
""" Determine if the point (x, y) is inside the polygon """
|
|
||||||
x, y = point
|
|
||||||
n = len(polygon)
|
|
||||||
inside = False
|
|
||||||
p1x, p1y = polygon[0]
|
|
||||||
for i in range(n + 1):
|
|
||||||
p2x, p2y = polygon[i % n]
|
|
||||||
if y > min(p1y, p2y):
|
|
||||||
if y <= max(p1y, p2y):
|
|
||||||
if x <= max(p1x, p2x):
|
|
||||||
if p1y != p2y:
|
|
||||||
xints = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
|
|
||||||
if p1x == p2x or x <= xints:
|
|
||||||
inside = not inside
|
|
||||||
p1x, p1y = p2x, p2y
|
|
||||||
return inside
|
|
||||||
|
|
||||||
def get_mesh_from_xy(self, pos: tuple[int]) -> Mesh:
|
|
||||||
x, y = pos
|
|
||||||
closest_mesh = None
|
|
||||||
closest_z = float('inf')
|
|
||||||
|
|
||||||
for mesh in self.meshes:
|
|
||||||
transformed_vertices = self.transform_vertices(mesh.get_vertices(), *pygame.display.get_surface().get_size())
|
|
||||||
avg_z = mesh.calculate_average_z(transformed_vertices)
|
|
||||||
for i, avg_z_value in avg_z:
|
|
||||||
polygon = mesh.create_polygon(mesh.get_face(i), transformed_vertices)
|
|
||||||
if self.point_in_polygon((x, y), polygon):
|
|
||||||
if avg_z_value < closest_z:
|
|
||||||
closest_z = avg_z_value
|
|
||||||
closest_mesh = mesh
|
|
||||||
|
|
||||||
return closest_mesh
|
|
||||||
|
|
||||||
def draw(self, surface):
|
|
||||||
|
|
||||||
polygons = []
|
|
||||||
for mesh in self.meshes:
|
|
||||||
transformed_vertices = self.transform_vertices(mesh.get_vertices(), *surface.get_size())
|
|
||||||
avg_z = mesh.calculate_average_z(transformed_vertices)
|
|
||||||
for z in avg_z:
|
|
||||||
#for z in sorted(avg_z, key=lambda x: x[1], reverse=True):
|
|
||||||
pointlist = mesh.create_polygon(mesh.get_face(z[0]), transformed_vertices)
|
|
||||||
polygons.append((pointlist, z[1], mesh.face_color, mesh.edge_color))
|
|
||||||
#pygame.draw.polygon(surface, (128, 128, 192), pointlist)
|
|
||||||
#pygame.draw.polygon(surface, (0, 0, 0), pointlist, 3)
|
|
||||||
|
|
||||||
for poly in sorted(polygons, key=lambda x: x[1], reverse=True):
|
|
||||||
pygame.draw.polygon(surface, poly[2], poly[0])
|
|
||||||
pygame.draw.polygon(surface, poly[3], poly[0], 3)
|
|
||||||
|
|
||||||
|
|
||||||
|
# generate a list of cubes
|
||||||
class Cube(Mesh):
|
meshes: list[Mesh] = []
|
||||||
def __init__(self):
|
|
||||||
vertices = [(-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1), (-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1)]
|
|
||||||
faces = [(0,1,2,3), (1,5,6,2), (5,4,7,6), (4,0,3,7), (3,2,6,7), (1,0,4,5)]
|
|
||||||
super().__init__(vertices, faces)
|
|
||||||
|
|
||||||
def set_position(self, position : tuple):
|
|
||||||
super().translate(position)
|
|
||||||
|
|
||||||
class ColorPicker:
|
|
||||||
def __init__(self, screen, x_pos: int, y_pos: int, width: int, height: int):
|
|
||||||
width = max(50, width)
|
|
||||||
self.sliders: list[Slider] = [
|
|
||||||
Slider(screen, int(x_pos + i*(width/3)), y_pos, int((width-50)/3), height, min=0, max=255, step=1, vertical=True) for i in range(3)
|
|
||||||
]
|
|
||||||
for slider in self.sliders:
|
|
||||||
slider.enable()
|
|
||||||
|
|
||||||
def get_color(self) -> tuple[int]:
|
|
||||||
# return (0,0,0)
|
|
||||||
return tuple([slider.getValue() for slider in self.sliders])
|
|
||||||
|
|
||||||
def set_color(self, color: tuple[int]):
|
|
||||||
for i, slider in enumerate(self.sliders):
|
|
||||||
slider.setValue(color[i])
|
|
||||||
|
|
||||||
meshes: list[Cube] = []
|
|
||||||
for origin in product([-1, 0, 1],[-1, 0, 1],[-1, 0, 1]):
|
for origin in product([-1, 0, 1],[-1, 0, 1],[-1, 0, 1]):
|
||||||
cube = Cube()
|
cube = Cube()
|
||||||
cube.scale((0.35, 0.35, 0.35))
|
cube.scale((0.35, 0.35, 0.35))
|
||||||
|
|||||||
Reference in New Issue
Block a user