-
Notifications
You must be signed in to change notification settings - Fork 0
/
renderer.py
133 lines (104 loc) · 3.61 KB
/
renderer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from PIL import Image
from pygifsicle import optimize as optimize_gif
from math import sin, cos
import pygame as pg
import numpy as np
import sys, imageio
def rotate_x(a, point):
rotMatrix = np.identity(len(point))
if len(point) > 2:
rotMatrix[1, 1] = cos(a)
rotMatrix[2, 1] = sin(a)
rotMatrix[1, 2] = -sin(a)
rotMatrix[2, 2] = cos(a)
return np.dot(rotMatrix, point)
def rotate_n(a, point, axis):
rotMatrix = np.identity(len(point))
rotMatrix[ 0, 0] = cos(a)
rotMatrix[axis, 0] = sin(a)
rotMatrix[ 0, axis] = -sin(a)
rotMatrix[axis, axis] = cos(a)
return np.dot(rotMatrix, point)
def rotate(point, rot):
point = rotate_x(rot[0], point)
for dim in range(1, len(point)):
point = rotate_n(rot[dim], point, dim)
return point
def project(point, rot):
point = rotate(point, rot)
dims = len(point)
if dims < 1: raise ValueError("Too few Dimensions")
depth_scale = 4 ** (dims + 1)
scale = 1.5
# project down to 2d
while len(point) > 2:
dist = 4
dscale = 1 / (dist - point[-1])
if dscale < 0: return
point = point[:-1] * dscale # discard last coordinate
return point * scale * depth_scale
class Color:
white = (255, 255, 255)
dwhite = (150, 150, 150)
grey = (32, 32, 32)
dgrey = (16, 16, 16)
orange = (255, 100, 0)
class HyperspaceRenderer:
def __init__(self, screen_size, window, ndims, save_to=None, gif_fps=24):
self.screen_size = screen_size
self.window = window
self.save_to = save_to
self.save_gif = save_to is not None
if self.save_gif:
self.gif = imageio.get_writer(save_to, mode="I", duration=1/gif_fps)
self.center = np.array(screen_size) / 2
self.rotation = np.zeros(ndims, dtype=float)
def point(self, point, color, radius=3, scale=1):
point = project(point, self.rotation)
if point is None: return
pg.draw.circle(
self.window, color,
point * scale + self.center,
radius
)
def pixel(self, point, color, scale=1):
point = project(point, self.rotation)
if point is None: return
self.window.set_at(
(point * scale + self.center).astype(int),
color
)
def edge(self, a, b, color, thickness=2):
pg.draw.line(
self.window, color,
project(a, self.rotation) + self.center,
project(b, self.rotation) + self.center,
thickness
)
def save_frame(self):
if self.save_gif:
string = pg.image.tostring(self.window, "RGBA")
image = Image.frombytes("RGBA", self.screen_size, string)
self.gif.append_data(np.asarray(image))
errormargin = .01
if self.rotation[0] > np.pi*2 - errormargin and self.rotation[0] < np.pi*2 + errormargin:
self.gif.close()
optimize_gif(self.save_to)
pg.quit()
sys.exit()
@staticmethod
def shared_ordinates(a, b):
shared = 0
for a_ord, b_ord in zip(a, b):
if a_ord == b_ord: shared += 1
return shared
@staticmethod
def cube(dimensions):
verticies = []
for i in range(2 ** dimensions):
binary = bin(i)[2:]
binary = binary.rjust(dimensions, "0")
binary = [int(x) for x in binary]
vertex = [(x - .5) * 2 for x in binary]
verticies.append(vertex)
return np.array(verticies)