-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathemulator.py
More file actions
182 lines (158 loc) · 6.8 KB
/
emulator.py
File metadata and controls
182 lines (158 loc) · 6.8 KB
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import math
import tkinter as tk
import sys
from time import perf_counter
# Set constants
import grid
BOX_WIDTH = 80 # Width of each of the squares
MARGIN = 75 # Distance between outside of game and edge of window
RAILING_DIST = 75 # Distance between railing and tiles
BOX_SPACING = 20 # Distance between squares
CIRCLE_MARGIN = 0 # Distance between coordinates and first circle of each Segment
PUMPKIN_RADIUS = 20
PUMPKIN_DISTANCE = 70 # Distance between pumpkins and bottom of grid
LINE_WIDTH = 2
CIRCLE_SIZE = 7
RAIL_CIRCLE_SIZE = 9
PUMPKIN_CIRCLE_SIZE = 8
# Determine window size
WINDOW_WIDTH = MARGIN * 2 + RAILING_DIST * 2 + BOX_WIDTH * 2 + BOX_SPACING
WINDOW_HEIGHT = MARGIN * 2 + BOX_WIDTH * 5 + PUMPKIN_DISTANCE + BOX_SPACING * 4
# Generate railing coordinates
TL = (MARGIN, MARGIN)
BL = (MARGIN, MARGIN + BOX_WIDTH * 5 + BOX_SPACING * 4)
TR = (MARGIN + RAILING_DIST * 2 + BOX_WIDTH * 2 + BOX_SPACING, MARGIN)
BR = (MARGIN + RAILING_DIST * 2 + BOX_WIDTH * 2 + BOX_SPACING, MARGIN + BOX_WIDTH * 5 + BOX_SPACING * 4)
# Generate grid coordinates
coords = []
for y in range(10):
for x in range(4):
# Generate top left coordinate, then add on as needed
coord = [MARGIN + RAILING_DIST, MARGIN + (BOX_WIDTH * 5) + (BOX_SPACING * 4)]
coord[0] += BOX_WIDTH * (math.ceil(x / 2))
coord[0] += BOX_SPACING * (x // 2)
coord[1] -= BOX_WIDTH * (math.ceil(y / 2))
coord[1] -= BOX_SPACING * (y // 2)
coords.append(coord)
# Image generator for the current state of the light show.
class Emulator:
def __init__(self, grid):
self.grid = grid
self.circles = {} # LED circle objects, indexed by segments
# Initialize window/canvas
self.root = tk.Tk()
self.root.title("Glass Stepping Stones")
self.root.protocol("WM_DELETE_WINDOW", self.exit)
self.canvas = tk.Canvas(self.root, bg="black", width=WINDOW_WIDTH, height=WINDOW_HEIGHT)
self.canvas.pack()
self.gen_circles()
self.update()
def gen_circles(self):
"""
Create all circle objects representative of individual LEDs.
"""
# Railings
seg_length = (BOX_WIDTH * 5) + (BOX_SPACING * 4) - (CIRCLE_MARGIN * 2)
for x in range(2):
seg_id = x
seg = self.grid.get_seg(seg_id)
led_count = seg.size()
led_space = seg_length / led_count
if x == 0:
origin = (MARGIN,
MARGIN + BOX_WIDTH * 5 + BOX_SPACING * 4)
else:
origin = (MARGIN + RAILING_DIST * 2 + BOX_WIDTH * 2 + BOX_SPACING,
MARGIN + BOX_WIDTH * 5 + BOX_SPACING * 4)
pixel_x = origin[0]
circles = []
for i in range(led_count):
pixel_y = origin[1] - round(led_space * i)
circles.append(self.draw_circle(pixel_x, pixel_y, size=RAIL_CIRCLE_SIZE))
self.circles[seg_id] = circles
# Rows
for y in range(10):
for x in range(2):
seg_id = 2 + x + y * 2
seg = self.grid.get_seg(seg_id)
led_count = seg.size()
seg_length = BOX_WIDTH - (CIRCLE_MARGIN * 2)
led_space = seg_length / led_count
origin = coords[x * 2 + y * 4]
pixel_y = origin[1] - round(CIRCLE_SIZE / 2)
circles = []
for i in range(led_count):
pixel_x = origin[0] + CIRCLE_MARGIN + round(led_space * i)
circles.append(self.draw_circle(pixel_x, pixel_y, size=CIRCLE_SIZE))
self.circles[seg_id] = circles
# Columns
for y in range(5):
for x in range(4):
seg_id = 22 + x + y * 4
seg = self.grid.get_seg(seg_id)
led_count = seg.size()
seg_length = BOX_WIDTH - (CIRCLE_MARGIN * 2)
led_space = seg_length / led_count
origin = coords[x + y * 8]
pixel_x = origin[0] - round(CIRCLE_SIZE / 2)
circles = []
for i in range(led_count):
pixel_y = origin[1] - CIRCLE_MARGIN - round(led_space * i) - CIRCLE_SIZE
circles.append(self.draw_circle(pixel_x, pixel_y, size=CIRCLE_SIZE))
self.circles[seg_id] = circles
# Pumpkins
def gen_pumpkin_coords(center, radius, count):
pumpkin_coords = []
for i in range(count):
pumpkin_coords.append((
center[0] + radius * math.sin(i * 2 * math.pi / count),
center[1] + radius * math.cos(i * 2 * math.pi / count)
))
return pumpkin_coords
# TODO: Add this code to the above function
left_center = (MARGIN,
MARGIN + BOX_WIDTH * 5 + BOX_SPACING * 4 + PUMPKIN_DISTANCE)
left_seg = self.grid.get_seg(42)
left_coords = gen_pumpkin_coords(left_center, PUMPKIN_RADIUS, left_seg.size())
left_circles = []
for coord in left_coords:
left_circles.append(self.draw_circle(*coord, size=PUMPKIN_CIRCLE_SIZE))
self.circles[42] = left_circles
right_center = (MARGIN + RAILING_DIST * 2 + BOX_WIDTH * 2 + BOX_SPACING,
MARGIN + BOX_WIDTH * 5 + BOX_SPACING * 4 + PUMPKIN_DISTANCE)
right_seg = self.grid.get_seg(43)
right_coords = gen_pumpkin_coords(right_center, PUMPKIN_RADIUS, right_seg.size())
right_circles = []
for coord in right_coords:
right_circles.append(self.draw_circle(*coord, size=PUMPKIN_CIRCLE_SIZE))
self.circles[43] = right_circles
def update(self):
"""
Update the grid on-screen based on the Grid object.
"""
for s in range(grid.SEG_COUNT):
circle_array = self.circles[s]
color_array = self.grid.get_seg(s).get_pixels()
for l in range(len(color_array)):
color = color_array[l]
r, g, b = color[0], color[1], color[2]
# TODO: Find source of colors being out of range
if r < 0 or g < 0 or b < 0 or r > 255 or g > 255 or b > 255:
print("Error: Color", r, g, b, "out of range.")
else:
hex_color = f'#{r:02x}{g:02x}{b:02x}'
self.canvas.itemconfig(circle_array[l], fill=hex_color)
self.root.update()
def draw_circle(self, x, y, size):
"""
:param x: Left x coordinate of circle.
:param y: Top y coordinate of circle.
:param size: Circle size.
:return: The circle ID.
"""
return self.canvas.create_oval(x, y, x + size, y + size)
def exit(self):
"""
Terminate the application.
"""
sys.exit()