166 lines
5.7 KiB
Python
166 lines
5.7 KiB
Python
from scene import Scene
|
|
from triangles import draw_line
|
|
import math
|
|
|
|
# Palette class (inherits from scene)
|
|
class ThreeD(Scene):
|
|
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.title = "Squares"
|
|
self.t = 0
|
|
|
|
self.x = 0.01
|
|
self.y = 0
|
|
self.z = 0
|
|
|
|
self.a = 10
|
|
self.b = 28
|
|
self.c = 8.0/3.0
|
|
|
|
self.scale = 5
|
|
|
|
self.focal_length = 800
|
|
self.screen_width = width * 4
|
|
self.screen_height = height * 4
|
|
self.lines = []
|
|
|
|
self.points = []
|
|
|
|
self.gradient = [4, 8, 6, 15, 18, 19, 26, 25, 24, 23, 16, 23, 22, 7, 5]
|
|
self.gradient = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
|
|
|
def update(self):
|
|
self.t += 0.00002
|
|
|
|
|
|
if(len(self.points) > 1200):
|
|
return
|
|
|
|
dx = (self.a * (self.y - self.x)) * self.t
|
|
dy = (self.x * (self.b - self.z) - self.y) * self.t
|
|
dz = (self.x * self.y - self.c * self.z) * self.t
|
|
self.x += dx
|
|
self.y += dy
|
|
self.z += dz
|
|
|
|
self.points.append((self.x, self.y, self.z))
|
|
|
|
def line3D(self, x1, y1, z1, x2, y2, z2, c):
|
|
self.lines.append(((x1, y1, z1), (x2, y2, z2), c))
|
|
|
|
|
|
def draw(self):
|
|
|
|
self.lines.clear()
|
|
clear(0)
|
|
for i in range(len(self.points)):
|
|
size = len(self.points)
|
|
|
|
if(i < size - 1):
|
|
x, y, z = self.points[i]
|
|
x2, y2, z2 = self.points[i + 1]
|
|
|
|
x *= self.scale
|
|
y *= self.scale
|
|
z *= self.scale
|
|
x2 *= self.scale
|
|
y2 *= self.scale
|
|
z2 *= self.scale
|
|
|
|
self.line3D(x, y, z, x2, y2, z2, self.gradient[int(i / 5) % len(self.gradient)])
|
|
|
|
self.render_scene(self.focal_length, self.screen_width, self.screen_height, 0, self.t*500, self.t * 200, 0, 0, 300)
|
|
|
|
|
|
def rotate_point(self, x, y, z, angle_x, angle_y, angle_z):
|
|
"""Rotates a point around the X, Y, and Z axes."""
|
|
# Rotation around the X axis
|
|
cos_x, sin_x = math.cos(angle_x), math.sin(angle_x)
|
|
y, z = y * cos_x - z * sin_x, y * sin_x + z * cos_x
|
|
|
|
# Rotation around the Y axis
|
|
cos_y, sin_y = math.cos(angle_y), math.sin(angle_y)
|
|
x, z = x * cos_y + z * sin_y, -x * sin_y + z * cos_y
|
|
|
|
# Rotation around the Z axis
|
|
cos_z, sin_z = math.cos(angle_z), math.sin(angle_z)
|
|
x, y = x * cos_z - y * sin_z, x * sin_z + y * cos_z
|
|
|
|
return x, y, z
|
|
|
|
def project_point(self, x, y, z, focal_length, screen_width, screen_height, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z):
|
|
|
|
x, y, z = self.rotate_point(x, y, z, angle_x, angle_y, angle_z)
|
|
|
|
# Apply translation
|
|
x += translate_x
|
|
y += translate_y
|
|
z += translate_z
|
|
|
|
"""Projects a 3D point onto a 2D screen using perspective projection."""
|
|
if z == 0:
|
|
z = 0.001 # Avoid division by zero
|
|
|
|
# Perspective projection formula
|
|
screen_x = (x * focal_length) / z
|
|
screen_y = (y * focal_length) / z
|
|
|
|
# Translate to screen coordinates (centered)
|
|
screen_x += screen_width / 2
|
|
screen_y += screen_height / 2
|
|
|
|
return screen_x, screen_y
|
|
|
|
def z_sort_lines(self, lines):
|
|
"""Sorts lines based on their average Z values for depth ordering."""
|
|
return sorted(lines, key=lambda line: (line[0][2] + line[1][2]) / 2, reverse=True)
|
|
|
|
def draw_3d_line(self, x1, y1, z1, x2, y2, z2, focal_length, screen_width, screen_height, color, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z):
|
|
"""Draws a 3D line projected onto a 2D screen."""
|
|
# Project the endpoints
|
|
screen_x1, screen_y1 = self.project_point(x1, y1, z1, focal_length, screen_width, screen_height, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z)
|
|
screen_x2, screen_y2 = self.project_point(x2, y2, z2, focal_length, screen_width, screen_height, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z)
|
|
|
|
screen_x12, screen_y12 = self.project_point(x1 + 5, y1, z1, focal_length, screen_width, screen_height, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z)
|
|
screen_x22, screen_y22 = self.project_point(x2 + 5, y2, z2, focal_length, screen_width, screen_height, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z)
|
|
|
|
# Use the renderer to draw the line on the screen
|
|
|
|
line(screen_x1 / 4, screen_y1 / 4, screen_x2 / 4, screen_y2 / 4, color)
|
|
|
|
|
|
def render_scene(self, focal_length, screen_width, screen_height, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z):
|
|
"""Renders the 3D lines in the scene with perspective projection and Z-sorting."""
|
|
# Sort the lines by Z value (depth)
|
|
sorted_lines = self.z_sort_lines(self.lines)
|
|
i = 0
|
|
# Draw each line
|
|
for line in sorted_lines:
|
|
(x1, y1, z1), (x2, y2, z2), c = line
|
|
self.draw_3d_line(x1, y1, z1, x2, y2, z2, focal_length, screen_width, screen_height, c, angle_x, angle_y, angle_z, translate_x, translate_y, translate_z)
|
|
i += 1
|
|
|
|
# Example usage
|
|
# renderer is an object that has a draw_line(x1, y1, x2, y2) function
|
|
liines = [
|
|
((-50, -50, -50), (50, -50, -50)),
|
|
((50, -50, -50), (50, 50, -50)),
|
|
((50, 50, -50), (-50, 50, -50)),
|
|
((-50, 50, -50), (-50, -50, -50)),
|
|
((-50, -50, 50), (50, -50, 50)),
|
|
((50, -50, 50), (50, 50, 50)),
|
|
((50, 50, 50), (-50, 50, 50)),
|
|
((-50, 50, 50), (-50, -50, 50)),
|
|
((-50, -50, -50), (-50, -50, 50)),
|
|
((50, -50, -50), (50, -50, 50)),
|
|
((50, 50, -50), (50, 50, 50)),
|
|
((-50, 50, -50), (-50, 50, 50)),
|
|
]
|
|
|
|
|
|
|
|
# Assuming you have a renderer object
|
|
|