vis.py 17.4 KB
Newer Older
Ahmad Reza's avatar
Ahmad Reza committed
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
import datetime, math, os, time
from pyglet.gl import *
from pyglet.window import mouse
import pyglet.window.key as key
import importlib
import subprocess
import pandas as pd

# screenshot manager parameters
screenshot_directory = 'screenshots/'
screenshot_file_type = '.png'

# control parameters
zoom_enabled = True
zoom_min = 4
zoom_max = 128
zoom_init = 16
zoom_speed = 1 / 50

translation_enabled = True
translation_init = (0, 0)

# presentation parameters
window_width = 800
window_height = 600
window_resizable = True  # set to False to force resolution even if window does not fit on screen
show_grid = True
rotate_thirty_degree = False  # the grid is not drawn correctly if the view is rotated!

# rendering parameters
target_frame_rate = 60
busy_waiting_time = 0.0015
print_frame_stats = False

# simulation parameters
rounds_per_second = 10

# tile_alpha = 0.6
particle_alpha = 1

41
marker_alpha = 1
Ahmad Reza's avatar
Ahmad Reza committed
42
43
44



45
def coords_to_sim(coords):
Ahmad Reza's avatar
Ahmad Reza committed
46
47
48
    return coords[0], coords[1] * math.sqrt(3/4)


49
def sim_to_coords(x, y):
Ahmad Reza's avatar
Ahmad Reza committed
50
51
52
    return x, round(y / math.sqrt(3/4), 0)


53
def window_to_sim(x, y, view):
Ahmad Reza's avatar
Ahmad Reza committed
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
    x_coord = view.left + (view.right - view.left) * (x / view.width)  # correct
    y_coord = view.bottom + (view.top - view.bottom) * (y / view.height)  # not correct
    return x_coord, y_coord


class ScreenshotManager:
    dt = datetime.datetime.now()
    #prefix = dt.isoformat(sep = '_', timespec = 'seconds').replace(':', '') + '_'
    prefix = dt.isoformat(sep='_').replace(':', '') + '_'
    def takeScreenshot():
        if not os.path.exists(screenshot_directory):
            os.makedirs(screenshot_directory)

        index = math.floor(time.monotonic() * 10**3)
        file_name = screenshot_directory + ScreenshotManager.prefix + str(index) + screenshot_file_type
        pyglet.image.get_buffer_manager().get_color_buffer().save(file_name)


class View:
    def __init__(self):
        self.focusPos = translation_init
        self.zoom = zoom_init

    def setDimensions(self, width, height):
        self.width = width
        self.height = height
        self.update()

    def drag(self, dx, dy):
        if not translation_enabled:
            return

        self.focusPos = (self.focusPos[0] - dx / self.zoom, self.focusPos[1] - dy / self.zoom)
        self.update()

    def scroll(self, x, y, scroll_x, scroll_y):
        if not zoom_enabled:
            return

        oldPos = (self.left + x / self.zoom, self.bottom + y / self.zoom)
        self.zoom = self.zoom * math.exp(-scroll_y * zoom_speed)
        self.zoom = max(self.zoom, zoom_min)
        self.zoom = min(self.zoom, zoom_max)
        self.update()
        newPos = (self.left + x / self.zoom, self.bottom + y / self.zoom)
        self.focusPos = (self.focusPos[0] + oldPos[0] - newPos[0], self.focusPos[1] + oldPos[1] - newPos[1])
        self.update()

    def update(self):
        halfZoomRec = 0.5 / self.zoom
        self.left = self.focusPos[0] - halfZoomRec * self.width;
        self.right = self.focusPos[0] + halfZoomRec * self.width;
        self.bottom = self.focusPos[1] - halfZoomRec * self.height;
        self.top = self.focusPos[1] + halfZoomRec * self.height;


class VisWindow(pyglet.window.Window):
111
112
    def __init__(self, window_size_x, window_size_y, sim):
        #super().__init__(sim.get_sim_x_size(), sim.get_sim_y_size(), resizable=window_resizable, vsync=False, caption="Simulator")
Ahmad Reza's avatar
Ahmad Reza committed
113
114
115
116
117
118
119
120
        super().__init__(window_size_x, window_size_y , resizable=window_resizable, vsync=False, caption="Simulator")
        self.window_active = True
        glClearColor(0.0, 0.0, 0.0, 0.0)
        glClearDepth(1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.sim = sim
        self.init_tile_vertex_list()
        self.init_particle_vertex_list()
121
        self.init_marker_vertex_list()
Ahmad Reza's avatar
Ahmad Reza committed
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
        self.view = View()

        self.particleTexture = pyglet.image.load('lib/images/particle.png').get_mipmapped_texture()
        self.gridTexture = pyglet.image.load('lib/images/grid.png').get_mipmapped_texture()

        glDisable(GL_DEPTH_TEST)
        glDisable(GL_CULL_FACE)

        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        glEnable(self.gridTexture.target)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

        if rotate_thirty_degree:
            glRotatef(30, 0, 0, 1)

        glMatrixMode(GL_PROJECTION)

        self.simulation_running = False
        self.video_mode = False

    def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
        if buttons & mouse.LEFT:
            self.view.drag(dx, dy)

    def exit_callback(self):
        self.close()
    def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
        self.view.scroll(x, y, scroll_x, scroll_y)

    def on_mouse_press(self, x, y, button, modifiers):
        if modifiers & key.MOD_CTRL:
            # get correct coordinates
158
159
            sim_coords = window_to_sim(x, y, self.view)
            coords_coords = sim_to_coords(sim_coords[0], sim_coords[1])
Ahmad Reza's avatar
Ahmad Reza committed
160
161
162
163
164
            rounded_coords=0
            if coords_coords[1]%2!=0:
                rounded_coords = round(coords_coords[0],0) + 0.5
            else:
                rounded_coords =round(coords_coords[0], 0)
165
            if (rounded_coords,coords_coords[1]) not in self.sim.tile_map_coords:
Ahmad Reza's avatar
Ahmad Reza committed
166
                # add tile and vertices
167
168
169
170
171
172
                if self.sim.add_tile_vis(rounded_coords, coords_coords[1]):
                    self.tile_vertex_list.resize(4 * len(self.sim.tiles), 4 * len(self.sim.tiles))
                    #self.tile_vertex_list.resize(4 * len(self.sim.tiles), 8 * len(self.sim.tiles))
                    self.tile_vertex_list.indices[4 * (len(self.sim.tiles) - 1) : 4 * (len(self.sim.tiles) - 1) + 4] = range(4 * (len(self.sim.tiles) - 1), 4 * (len(self.sim.tiles) - 1) + 4)
                   # self.tile_vertex_list.indices = list(range(0, 8 * len(self.sim.tiles)))
                   # self.update_tile(len(self.sim.tiles) - 1, tile)
Ahmad Reza's avatar
Ahmad Reza committed
173
174
175
                    self.update_tiles(True)
            else:
                # delete tile
176
177
                self.sim.remove_tile_on((rounded_coords,coords_coords[1]))
                self.tile_vertex_list.resize(4 * len(self.sim.tiles), 4 * len(self.sim.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
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
                self.update_tiles(True)

    def on_resize(self, width, height):
        glViewport(0, 0, width, height)
        self.view.setDimensions(width, height)
        return pyglet.event.EVENT_HANDLED

    def on_close(self):
        self.window_active = False
        return pyglet.event.EVENT_HANDLED

    def on_key_press(self, symbol, modifiers):
        if symbol == key.Q and modifiers & key.MOD_COMMAND:  # cmd+q: quit application
            self.window_active = False
        elif symbol == key.SPACE:  # space: pause / unpause simulation
            self.pause()
        elif symbol == key.S and modifiers & key.MOD_COMMAND:  # cmd+s: save screenshot
            ScreenshotManager.takeScreenshot()
        elif symbol == key.V and modifiers & key.MOD_COMMAND:  # cmd+v: toggle video mode
            if not self.video_mode:
                self.video_mode = True
                self.simulation_running = True
                self.elapsed_frame_time = 0  # make videos completely reproducible
            else:
                self.video_mode = False
                self.simulation_running = False
        return pyglet.event.EVENT_HANDLED

    def draw(self):
        self.update_tiles()
        self.update_particles()
209
        self.update_markers()
Ahmad Reza's avatar
Ahmad Reza committed
210
211
212
213
214
215
216
217
218
219
220
221
        glLoadIdentity()
        glOrtho(self.view.left, self.view.right, self.view.bottom, self.view.top, 1, -1)


        if show_grid:
            self.drawGrid()
        else:
            glClearColor(1, 1, 1, 1)
            glClear(GL_COLOR_BUFFER_BIT)

        glBindTexture(self.particleTexture.target, self.particleTexture.id)

222
        if len(self.sim.tiles) != 0:
Ahmad Reza's avatar
Ahmad Reza committed
223
224
            self.tile_vertex_list.draw(GL_QUADS)
        self.particle_vertex_list.draw(GL_QUADS)
225
        self.marker_vertex_list.draw(GL_QUADS)
Ahmad Reza's avatar
Ahmad Reza committed
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

        self.flip()

        if self.video_mode:
            ScreenshotManager.takeScreenshot()

    def drawGrid(self):
        texLeft = math.fmod(self.view.left, 1)
        texRight = texLeft + self.view.right - self.view.left

        texHeight = 2 * math.sqrt(3/4)
        texBottom = math.fmod(self.view.bottom, texHeight)
        texTop = texBottom + self.view.top - self.view.bottom
        texBottom = texBottom / texHeight
        texTop = texTop / texHeight

        glColor4f(1, 1, 1, 1)
        glBindTexture(self.gridTexture.target, self.gridTexture.id)

        glBegin(GL_QUADS)
        glTexCoord2f(texLeft, texBottom)
        glVertex2f(self.view.left, self.view.bottom)
        glTexCoord2f(texRight, texBottom)
        glVertex2f(self.view.right, self.view.bottom)
        glTexCoord2f(texRight, texTop)
        glVertex2f(self.view.right, self.view.top)
        glTexCoord2f(texLeft, texTop)
        glVertex2f(self.view.left, self.view.top)
        glEnd()

    def pause(self):
        self.simulation_running = not self.simulation_running

    def round(self):
260
        if self.sim.run_sim():
Ahmad Reza's avatar
Ahmad Reza committed
261
262
263
            return True

    def init_tile_vertex_list(self):
264
265
266
        self.tile_vertex_list = pyglet.graphics.vertex_list_indexed(4 * len(self.sim.tiles),
                                                                    list(range(0, 4 * len(self.sim.tiles))),
                                                                    #list(range(0,8 * len(self.sim.tiles))),
Ahmad Reza's avatar
Ahmad Reza committed
267
268
269
270
271
272
273
274
                                                                    'v2f', 't2f', 'c4f')
        self.update_tiles(True)


    def update_tiles(self, update_all=False):
        foreground = []
        background = []

275
276
277
        if (len(self.sim.tiles) != 0):
            if self.sim.get_tile_deleted():
                self.tile_vertex_list.resize(4 * len(self.sim.tiles), 4 * len(self.sim.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
278
                update_all=True
279
280
                self.sim.set_tile_deleted()
            for i, tile in enumerate(self.sim.tiles):
Ahmad Reza's avatar
Ahmad Reza committed
281
                if tile.created:
282
                    self.tile_vertex_list.resize(4 * len(self.sim.tiles), 4 * len(self.sim.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
283
                    self.tile_vertex_list.indices[
284
285
                    4 * (len(self.sim.tiles) - 1): 4 * (len(self.sim.tiles) - 1) + 4] = range(
                        4 * (len(self.sim.tiles) - 1), 4 * (len(self.sim.tiles) - 1) + 4)
Ahmad Reza's avatar
Ahmad Reza committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
                    tile.created =  False
                if update_all or tile.modified:
                    self.update_tile(i, tile)
                    tile.modified = False

                indices = list(range(4 * i, 4 * i + 4))
                if tile.get_tile_status():
                    foreground += indices
                else:
                    background += indices

            self.tile_vertex_list.indices = background + foreground
        else:
            pass#self.tile_vertex_list.indices = list(range(0,4))

    def update_tile(self, i, tile):
        weird = 256 / 220
303
        pos = coords_to_sim(tile.coords)
Ahmad Reza's avatar
Ahmad Reza committed
304
305
306
307
308
309
310
311
312
313
314
        x = pos[0]
        y = pos[1]

        self.tile_vertex_list.vertices[8 * i: 8 * i + 8] = [x - weird, y - weird, x + weird, y - weird, x + weird,
                                                            y + weird, x - weird, y + weird]

        if tile.get_tile_status():
            texLeft = 0 / 8
            texRight = 1 / 8
            texBottom = 5 / 8
            texTop = 6 / 8
315
            #tile_alpha = 1
Ahmad Reza's avatar
Ahmad Reza committed
316
317
        else:
            texLeft = 7 / 8
318
            texRight = 1 # 8/8
Ahmad Reza's avatar
Ahmad Reza committed
319
320
            texBottom = 4 / 8
            texTop = 5 / 8
321
            #tile_alpha = 0.5
Ahmad Reza's avatar
Ahmad Reza committed
322
323
324
325
326
327
328
329

        self.tile_vertex_list.tex_coords[8 * i: 8 * i + 8] = [texLeft, texBottom, texRight, texBottom, texRight, texTop,
                                                              texLeft, texTop]

        self.tile_vertex_list.colors[16 * i: 16 * i + 16] = (tile.color + [tile.get_alpha()]) * 4

    def init_particle_vertex_list(self):
        self.particle_vertex_list = self.particle_vertex_list = pyglet.graphics.vertex_list \
330
            (4 * len(self.sim.particles), 'v2f', 't2f', 'c4f')
Ahmad Reza's avatar
Ahmad Reza committed
331
332
333
        self.update_particles(True)

    def update_particles(self, update_all = False):
334
335
336
337
        if (len(self.sim.particles) != 0):
            if self.sim.get_particle_deleted():
                self.particle_vertex_list.resize(4 * len(self.sim.particles))
                self.sim.set_particle_deleted()
Ahmad Reza's avatar
Ahmad Reza committed
338
                update_all = True
339
            for i, particle in enumerate(self.sim.particles):
Ahmad Reza's avatar
Ahmad Reza committed
340
                if particle.created:
341
342
                    self.particle_vertex_list.resize(4 * len(self.sim.particles))
                    # self.tile_vertex_list.resize(4 * len(self.sim.tiles), 8 * len(self.sim.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
343
344
345
346
347
348
349
350
351
352
                    particle.created=False
                if update_all or particle.modified:
                    self.update_particle(i, particle)
                    particle.modified = False
        else:
            pass


    def update_particle(self, i, particle):
        weird = 256 / 220
353
        pos = coords_to_sim(particle.coords)
Ahmad Reza's avatar
Ahmad Reza committed
354
355
356
357
358
359
360
361
362
363
364
365
        x = pos[0]
        y = pos[1]

        self.particle_vertex_list.vertices[8 * i: 8 * i + 8] = [x - weird, y - weird, x + weird, y - weird, x + weird,
                                                                y + weird, x - weird, y + weird]
        """UV works in such away that there are 8 Columns and 8 rows and if you want to take one you have to add the direction
        and reduce one from the inverse direction"""
        if particle.get_carried_status():
            texLeft = 0 / 8
            texRight = 1 / 8
            texBottom = 7 / 8
            texTop = 6 / 8
366
            #particle.set_alpha(0.5)
Ahmad Reza's avatar
Ahmad Reza committed
367
368
369
370
371
        else:
            texLeft = 0 / 8
            texRight = 1 / 8
            texBottom = 0 / 8
            texTop = 1 / 8
372
            #particle.set_alpha(1)
Ahmad Reza's avatar
Ahmad Reza committed
373
374
375
376
377
        self.particle_vertex_list.tex_coords[8 * i: 8 * i + 8] = [texLeft, texBottom, texRight, texBottom,
                                                                  texRight, texTop, texLeft, texTop]

        self.particle_vertex_list.colors[16 * i: 16 * i + 16] = (particle.color + [particle.get_alpha()]) * 4

378
379
380
381
    def init_marker_vertex_list(self):
        self.marker_vertex_list = self.marker_vertex_list = pyglet.graphics.vertex_list \
            (4 * len(self.sim.markers), 'v2f', 't2f', 'c4f')
        self.update_markers(True)
Ahmad Reza's avatar
Ahmad Reza committed
382

383
384
385
386
387
    def update_markers(self, update_all=True):
        if (len(self.sim.markers) != 0):
            if self.sim.get_marker_deleted():
                self.marker_vertex_list.resize(4 * len(self.sim.markers))
                self.sim.set_marker_deleted()
Ahmad Reza's avatar
Ahmad Reza committed
388
                update_all = True
389
390
391
            for i, marker in enumerate(self.sim.markers):
                if marker.created:
                    self.marker_vertex_list.resize(4 * len(self.sim.markers))
392
                    # self.tile_vertex_list.resize(4 * len(self.sim.tiles), 8 * len(self.sim.tiles))
393
394
395
396
                    marker.created = False
                if update_all or marker.modified:
                    self.update_marker(i, marker)
                    marker.modified = False
Ahmad Reza's avatar
Ahmad Reza committed
397
398
399
400
        else:
            pass


401
    def update_marker(self, i, marker):
Ahmad Reza's avatar
Ahmad Reza committed
402
        weird = 256 / 220
403
        pos = coords_to_sim(marker.coords)
Ahmad Reza's avatar
Ahmad Reza committed
404
405
406
        x = pos[0]
        y = pos[1]

407
        self.marker_vertex_list.vertices[8 * i: 8 * i + 8] = [x - weird, y - weird, x + weird, y - weird, x + weird,
Ahmad Reza's avatar
Ahmad Reza committed
408
409
                                                                y + weird, x - weird, y + weird]
        texLeft = 7/8
410
        texRight = 1 #8/8
Ahmad Reza's avatar
Ahmad Reza committed
411
412
413
        texBottom = 0/8
        texTop = 1/8

414
        self.marker_vertex_list.tex_coords[8 * i: 8 * i + 8] = [texLeft, texBottom, texRight, texBottom,
Ahmad Reza's avatar
Ahmad Reza committed
415
416
                                                                  texRight, texTop, texLeft, texTop]

417
        self.marker_vertex_list.colors[16 * i: 16 * i + 16] = (marker.color + [marker.get_alpha()]) * 4
Ahmad Reza's avatar
Ahmad Reza committed
418
419
420
421
422
423
424
425
426
427
428

    def run(self):
        p = 0
        target_frame_time = 1 / target_frame_rate
        round_time = 1 / rounds_per_second
        self.elapsed_frame_time = 0
        mod = importlib.import_module('solution.' + self.sim.get_solution())
        while (self.sim.get_actual_round() <= self.sim.get_max_round()) and self.window_active and self.sim.get_end()==False:
            #while actual simulation round is below max round
            last_time = time.perf_counter()
            while self.elapsed_frame_time >= round_time:
429
                mod.solution(self.sim)
Ahmad Reza's avatar
Ahmad Reza committed
430
431
                self.elapsed_frame_time -= round_time
                if self.elapsed_frame_time <= round_time:
432
                    self.sim.csv_round_writer.next_line(self.sim.get_actual_round())
Ahmad Reza's avatar
Ahmad Reza committed
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
                    self.sim.inc_round_cnter()  # increase simulation round counter by one.
            self.dispatch_events()
            self.draw()
            while time.perf_counter() - last_time < target_frame_time:
                sleep_time = max(target_frame_time - (time.perf_counter() - last_time), 0)
                if sleep_time >= busy_waiting_time:
                    time.sleep(sleep_time - busy_waiting_time)
                else:
                    pass

            if self.simulation_running:
                self.elapsed_frame_time += target_frame_time

            if print_frame_stats:
                frame_time_delta = time.perf_counter() - last_time - target_frame_time
                frame_time_ok = abs(frame_time_delta) <= 0.0001
                print('frame time:', format(time.perf_counter() - last_time, '.6f'), '\tdelta:', format(frame_time_delta, '0.6f'), '\tok:', frame_time_ok)

        self.window_active =  False
        return