-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.py
295 lines (260 loc) · 10.1 KB
/
main.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
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
import os
import datetime
import tkinter as tk
import fov_demoutil
import fov_rho as fov
from mathutil import *
# Get all maps
mapnames = os.listdir('maps')
visibilityMaps = [ fov_demoutil.load_from_file(f'maps/{x}')[0] for x in mapnames]
QUERY = "Select a map:\n" + "\n".join([ f"{i+1}. {x}"for i,x in enumerate(mapnames)])
USE_VISIBILITY_MAP = 0
print(QUERY)
selection = input()
while True:
done = False
try:
USE_VISIBILITY_MAP = int(selection)-1
done = USE_VISIBILITY_MAP >= 0 and USE_VISIBILITY_MAP < len(visibilityMaps)
except:
pass
if not done:
print("ERROR. " + QUERY)
selection = input()
else:
break
visibilityMap = visibilityMaps[USE_VISIBILITY_MAP]
TILE_SIZE = 8
LOS = 10
g_canvas_rects = None
g_prev_elems = {}
g_binary_visibility = True
g_binary_visibility_threshold = 0
g_total_time = 0.0
g_num_times = 0
g_hovered_vis_pt = None
def rebuild_canvas(canvas, src, los, visibilityMap, on_fov_step_callback = None):
global g_canvas_rects
global g_prev_elems
global g_total_time
global g_num_times
w = visibilityMap.width
h = visibilityMap.height
updated_elems = {}
def cb(p,v):
updated_elems[p] = v
contributors = []
def cb_contributors( points ):
for p in points:
contributors.append(p)
start_time = datetime.datetime.now()
fnContributors = None
if g_hovered_vis_pt:
fnContributors = cb_contributors
fovmap = fov.fov( src, los, visibilityMap, cb, g_hovered_vis_pt, fnContributors)
g_total_time += (datetime.datetime.now() - start_time).total_seconds() * 0.001
g_num_times += 1
print("Avg time so far: " + str(g_total_time / g_num_times) + " updated elems: " + str(len(updated_elems.keys())))
process_elems = updated_elems.copy()
fcolor = ""
first_time = g_canvas_rects is None
if first_time:
g_canvas_rects = [None] * len(visibilityMap.data)
# First time we have to process ALL elements. So set all invisible elements to 0
for y in range(h):
for x in range(w):
p = ivec2(x,y)
if p not in updated_elems.keys():
process_elems[p] = 0
else:
#print(len(g_prev_elems))
for k,v in g_prev_elems.items():
if k not in updated_elems.keys(): # if previous point not in updated elements, set visibility to 0
process_elems[k] = 0
for p, v in process_elems.items():
x = p.x
y = p.y
vq = int(v*10)
if g_binary_visibility:
vq = 0 if g_binary_visibility_threshold >= vq else 10
blocker = visibilityMap.get(p) < 1
if src == p:
fcolor = 'red'
elif blocker:
fcolor = 'magenta'
else:
fcolor = "#0{0}0".format(hex(6+vq-1)[2:])
if first_time:
g_canvas_rects[x+y*w] = canvas.create_rectangle(x*TILE_SIZE, y*TILE_SIZE, (x+1)*TILE_SIZE, (y+1)*TILE_SIZE, fill= fcolor)
else:
canvas.itemconfig(g_canvas_rects[x+y*w], fill=fcolor)
if contributors:
for p,amt in contributors:
x = p.x
y = p.y
vq = int(amt*10)
fcolor = "#{0}{0}0".format(hex(6+vq-1)[2:])
#fcolor = "yellow" if amt > 0 else "black"
canvas.itemconfig(g_canvas_rects[x+y*w], fill=fcolor)
g_prev_elems = updated_elems
g_mousehover_cursor = False
g_cursor = ivec2(0,0)
g_last_calculated_cursor = ivec2(-1,-1)
g_last_hovered_vis_pt = None
window = tk.Tk()
max_screen_width = window.winfo_screenwidth()
max_screen_height = window.winfo_screenheight()- 50 # remove a bit for title bar or taskbar
TILE_SIZE = min(max_screen_width//visibilityMap.width, max_screen_height//visibilityMap.height)
screenw = TILE_SIZE*visibilityMap.width
screenh = TILE_SIZE*visibilityMap.height
window.geometry("{0}x{1}".format(screenw,screenh))
canvas = tk.Canvas(window, bg='#000000',
width=screenw,
height=screenh)
canvas.pack()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
g_last_calculated_cursor = g_cursor
g_blocker_transparency = 0.0
def current_config():
return [
("cursor", g_cursor),
("losRadius", LOS),
("blocker transparency", g_blocker_transparency),
("binary visibility", g_binary_visibility),
("binary visibility threshold", g_binary_visibility_threshold),
("fov decay percent", fov.DECAY_PER_TILE_PERCENT)
]
def update_title():
title_str = ", ".join([f"{kv[0]}={kv[1]}" for kv in current_config()])
window.title(title_str)
def canvas_coords(evt):
global g_cursor
global g_last_calculated_cursor
global g_hovered_vis_pt
global g_last_hovered_vis_pt
x, y = canvas.canvasx(evt.x), canvas.canvasy(evt.y)
t = ivec2(int(x) // TILE_SIZE, int(y) // TILE_SIZE)
if visibilityMap.in_bounds( t ):
if g_mousehover_cursor:
g_hovered_vis_pt = t
if g_last_hovered_vis_pt is None or (not (g_last_hovered_vis_pt == g_hovered_vis_pt)):
g_last_hovered_vis_pt = g_hovered_vis_pt
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
else:
g_cursor = t
update_title()
if not (g_last_calculated_cursor == g_cursor):
g_last_calculated_cursor = g_cursor
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def blocker_change_transparency():
global visibilityMap
visibilityMap.data = [ (x if x == 1 else g_blocker_transparency) for x in visibilityMap.data]
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def blocker_more_transparent(evt):
global g_blocker_transparency
g_blocker_transparency = min(g_blocker_transparency + 0.1,0.9999)
blocker_change_transparency()
def blocker_less_transparent(evt):
global g_blocker_transparency
g_blocker_transparency = max(g_blocker_transparency - 0.1,0)
blocker_change_transparency()
def toggle_binary_visibility(evt):
global g_binary_visibility
g_binary_visibility = not g_binary_visibility
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def binary_visibility_threshold_up(evt):
global g_binary_visibility_threshold
g_binary_visibility_threshold = min(g_binary_visibility_threshold+1,10)
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def binary_visibility_threshold_down(evt):
global g_binary_visibility_threshold
g_binary_visibility_threshold = max(g_binary_visibility_threshold-1,0)
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def binary_los_up(evt):
global LOS
LOS = min(LOS+1, fov.MAX_LOS)
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def binary_los_down(evt):
global LOS
LOS = max(LOS-1,1)
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def fov_decay_up(evt):
fov.DECAY_PER_TILE_PERCENT = min(fov.DECAY_PER_TILE_PERCENT+0.1, 1.0)
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def fov_decay_down(evt):
fov.DECAY_PER_TILE_PERCENT = max(fov.DECAY_PER_TILE_PERCENT-0.1, 0.0)
update_title()
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap)
def run_visualize(evt):
import imageio
import numpy
import subprocess
from PIL import Image, ImageDraw
images = []
# Create the base image that we'll be updating
img_tile_size = min(TILE_SIZE - (TILE_SIZE%4),16)
imgbase = Image.new("RGB", (img_tile_size*visibilityMap.width, img_tile_size*visibilityMap.height), "black")
imgbase_drawer = ImageDraw.Draw(imgbase)
def draw_rect( p, fill):
x0 = p.x*img_tile_size
y0 = p.y*img_tile_size
imgbase_drawer.rectangle([x0,y0,x0+img_tile_size,y0+img_tile_size],fill,outline = "black")
def make_rgb( vis, fov):
ambient = 0.3
rgb = [1, vis, 1] # white for floor, magenta for wall
return tuple([int(255*x*(fov*(1-ambient) + ambient)) for x in rgb])
for y in range(visibilityMap.height):
for x in range(visibilityMap.width):
p = ivec2(x,y)
v = visibilityMap.get(p)
draw_rect(p, make_rgb(v,0))
draw_rect(g_cursor, "red")
def on_fov_step_callback(p, nbs, amt):
v = visibilityMap.get(p)
draw_rect(p, make_rgb(v,amt))
img = imgbase.copy()
linedrawer = ImageDraw.Draw(img)
for nb in nbs:
xy = [ img_tile_size//2 + img_tile_size*c for c in [p.x, p.y, nb.x, nb.y]]
linedrawer.line(xy, fill='red', width=3)
images.append(img)
rebuild_canvas(canvas, g_cursor, LOS, visibilityMap, on_fov_step_callback)
title_str = ", ".join([f"{kv[0]}={kv[1]}" for kv in current_config()])
print("Saving gif...")
gif_filename = f'{mapnames[USE_VISIBILITY_MAP]}_{g_cursor}_{title_str}.gif'
imageio.mimsave(gif_filename, images)
print("Converting to mp4")
mp4_filename = gif_filename[:-3] + 'mp4'
cmd = f"ffmpeg -i \"{gif_filename}\" -preset veryslow -tune animation -pix_fmt yuv420p -crf 8 -profile:v high -an \"{mp4_filename}\""
subprocess.call(cmd, shell=True)
print("Done")
def toggle_mousehover(evt):
global g_mousehover_cursor
global g_hovered_vis_pt
global g_last_hovered_vis_pt
g_mousehover_cursor = not g_mousehover_cursor
if not g_mousehover_cursor:
g_hovered_vis_pt = None
g_last_hovered_vis_pt = None
canvas.bind('<Motion>', canvas_coords)
canvas.bind('<Enter>', canvas_coords) # handle <Alt>+<Tab> switches between windows
window.bind('<F2>', blocker_more_transparent)
window.bind('<F3>', blocker_less_transparent)
window.bind('<F4>', toggle_binary_visibility)
window.bind('<F5>', binary_visibility_threshold_up)
window.bind('<F6>', binary_visibility_threshold_down)
window.bind('<F7>', binary_los_up)
window.bind('<F8>', binary_los_down)
window.bind('<F9>', fov_decay_up)
window.bind('<F10>', fov_decay_down)
window.bind('<F11>', run_visualize)
window.bind('<F12>', toggle_mousehover)
window.focus_force()
tk.mainloop()