vis.py 16.8 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
import datetime, math, os, time
from pyglet.gl import *
from pyglet.window import mouse
import pyglet.window.key as key

# 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
Ahmad Reza's avatar
Ahmad Reza committed
28
29
target_frame_rate = 10
busy_waiting_time = 1
Ahmad Reza's avatar
Ahmad Reza committed
30
31
32
print_frame_stats = False

# simulation parameters
33
rounds_per_second = 10
Ahmad Reza's avatar
Ahmad Reza committed
34

35
36
# tile_transparency = 0.6
particle_transparency = 1
Ahmad Reza's avatar
Ahmad Reza committed
37

38
marker_transparency = 1
Ahmad Reza's avatar
Ahmad Reza committed
39
40


41
42
def coordinates_to_sim(coordinates):
    return coordinates[0], coordinates[1] * math.sqrt(3/4)
Ahmad Reza's avatar
Ahmad Reza committed
43
44


45
def sim_to_coordinates(x, y):
Ahmad Reza's avatar
Ahmad Reza committed
46
47
48
    return x, round(y / math.sqrt(3/4), 0)


49
def window_to_sim(x, y, view):
Ahmad Reza's avatar
Ahmad Reza committed
50
51
52
53
54
55
56
57
58
    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(':', '') + '_'
Ahmad Reza's avatar
Ahmad Reza committed
59

Ahmad Reza's avatar
Ahmad Reza committed
60
61
62
63
64
65
66
67
68
69
70
71
72
    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
Ahmad Reza's avatar
Ahmad Reza committed
73
        halfZoomRec = 0.5 / self.zoom
Ahmad Reza's avatar
Ahmad Reza committed
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

    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):
Ahmad Reza's avatar
Ahmad Reza committed
108
    def __init__(self, window_size_x, window_size_y, world):
109
        #super().__init__(world.get_world_x_size(), world.get_world_y_size(), resizable=window_resizable, vsync=False, caption="Simulator")
Ahmad Reza's avatar
Ahmad Reza committed
110
111
112
113
114
        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)
Ahmad Reza's avatar
Ahmad Reza committed
115
        self.world = world
Ahmad Reza's avatar
Ahmad Reza committed
116
117
        self.init_tile_vertex_list()
        self.init_particle_vertex_list()
118
        self.init_marker_vertex_list()
Ahmad Reza's avatar
Ahmad Reza committed
119
        self.view = View()
Ahmad Reza's avatar
Ahmad Reza committed
120
121
        self.view.setDimensions(window_size_x, window_size_y)
        self.elapsed_frame_time = 0
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
        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
Ahmad Reza's avatar
Ahmad Reza committed
143
144
        self.draw()

Ahmad Reza's avatar
Ahmad Reza committed
145
146
147
148
149
150
151
152
153
154
155
156
    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
157
158
159
160
161
            sim_coordinates = window_to_sim(x, y, self.view)
            coordinates_coordinates = sim_to_coordinates(sim_coordinates[0], sim_coordinates[1])
            rounded_coordinates=0
            if coordinates_coordinates[1]%2!=0:
                rounded_coordinates = round(coordinates_coordinates[0],0) + 0.5
Ahmad Reza's avatar
Ahmad Reza committed
162
            else:
163
164
                rounded_coordinates =round(coordinates_coordinates[0], 0)
            if (rounded_coordinates,coordinates_coordinates[1]) not in self.world.tile_map_coordinates:
Ahmad Reza's avatar
Ahmad Reza committed
165
                # add tile and vertices
166
                if self.world.add_tile_vis(rounded_coordinates, coordinates_coordinates[1]):
Ahmad Reza's avatar
Ahmad Reza committed
167
168
169
170
171
                    self.tile_vertex_list.resize(4 * len(self.world.tiles), 4 * len(self.world.tiles))
                    #self.tile_vertex_list.resize(4 * len(self.world.tiles), 8 * len(self.world.tiles))
                    self.tile_vertex_list.indices[4 * (len(self.world.tiles) - 1) : 4 * (len(self.world.tiles) - 1) + 4] = range(4 * (len(self.world.tiles) - 1), 4 * (len(self.world.tiles) - 1) + 4)
                   # self.tile_vertex_list.indices = list(range(0, 8 * len(self.world.tiles)))
                   # self.update_tile(len(self.world.tiles) - 1, tile)
Ahmad Reza's avatar
Ahmad Reza committed
172
173
174
                    self.update_tiles(True)
            else:
                # delete tile
175
                self.world.remove_tile_on((rounded_coordinates,coordinates_coordinates[1]))
Ahmad Reza's avatar
Ahmad Reza committed
176
                self.tile_vertex_list.resize(4 * len(self.world.tiles), 4 * len(self.world.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
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
                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()
208
        self.update_markers()
Ahmad Reza's avatar
Ahmad Reza committed
209
210
211
212
213
214
215
216
217
218
219
220
        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)

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

        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 init_tile_vertex_list(self):
Ahmad Reza's avatar
Ahmad Reza committed
259
260
261
        self.tile_vertex_list = pyglet.graphics.vertex_list_indexed(4 * len(self.world.tiles),
                                                                    list(range(0, 4 * len(self.world.tiles))),
                                                                    #list(range(0,8 * len(self.world.tiles))),
Ahmad Reza's avatar
Ahmad Reza committed
262
263
264
265
266
267
268
                                                                    'v2f', 't2f', 'c4f')
        self.update_tiles(True)

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

Ahmad Reza's avatar
Ahmad Reza committed
269
270
271
        if (len(self.world.tiles) != 0):
            if self.world.get_tile_deleted():
                self.tile_vertex_list.resize(4 * len(self.world.tiles), 4 * len(self.world.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
272
                update_all=True
Ahmad Reza's avatar
Ahmad Reza committed
273
274
                self.world.set_tile_deleted()
            for i, tile in enumerate(self.world.tiles):
Ahmad Reza's avatar
Ahmad Reza committed
275
                if tile.created:
Ahmad Reza's avatar
Ahmad Reza committed
276
                    self.tile_vertex_list.resize(4 * len(self.world.tiles), 4 * len(self.world.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
277
                    self.tile_vertex_list.indices[
Ahmad Reza's avatar
Ahmad Reza committed
278
279
                    4 * (len(self.world.tiles) - 1): 4 * (len(self.world.tiles) - 1) + 4] = range(
                        4 * (len(self.world.tiles) - 1), 4 * (len(self.world.tiles) - 1) + 4)
Ahmad Reza's avatar
Ahmad Reza committed
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
                    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
297
        pos = coordinates_to_sim(tile.coordinates)
Ahmad Reza's avatar
Ahmad Reza committed
298
299
300
301
302
303
304
305
306
307
308
        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
309
            #tile_transparency = 1
Ahmad Reza's avatar
Ahmad Reza committed
310
311
        else:
            texLeft = 7 / 8
312
            texRight = 1 # 8/8
Ahmad Reza's avatar
Ahmad Reza committed
313
314
            texBottom = 4 / 8
            texTop = 5 / 8
315
            #tile_transparency = 0.5
Ahmad Reza's avatar
Ahmad Reza committed
316
317
318
319

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

320
        self.tile_vertex_list.colors[16 * i: 16 * i + 16] = (tile.color + [tile.get_transparency()]) * 4
Ahmad Reza's avatar
Ahmad Reza committed
321
322
323

    def init_particle_vertex_list(self):
        self.particle_vertex_list = self.particle_vertex_list = pyglet.graphics.vertex_list \
Ahmad Reza's avatar
Ahmad Reza committed
324
            (4 * len(self.world.particles), 'v2f', 't2f', 'c4f')
Ahmad Reza's avatar
Ahmad Reza committed
325
326
327
        self.update_particles(True)

    def update_particles(self, update_all = False):
Ahmad Reza's avatar
Ahmad Reza committed
328
329
330
331
        if (len(self.world.particles) != 0):
            if self.world.get_particle_deleted():
                self.particle_vertex_list.resize(4 * len(self.world.particles))
                self.world.set_particle_deleted()
Ahmad Reza's avatar
Ahmad Reza committed
332
                update_all = True
Ahmad Reza's avatar
Ahmad Reza committed
333
            for i, particle in enumerate(self.world.particles):
Ahmad Reza's avatar
Ahmad Reza committed
334
                if particle.created:
Ahmad Reza's avatar
Ahmad Reza committed
335
336
                    self.particle_vertex_list.resize(4 * len(self.world.particles))
                    # self.tile_vertex_list.resize(4 * len(self.world.tiles), 8 * len(self.world.tiles))
Ahmad Reza's avatar
Ahmad Reza committed
337
338
339
340
341
342
343
344
345
                    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
346
        pos = coordinates_to_sim(particle.coordinates)
Ahmad Reza's avatar
Ahmad Reza committed
347
348
349
350
351
352
353
354
355
356
357
358
        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
359
            #particle.set_transparency(0.5)
Ahmad Reza's avatar
Ahmad Reza committed
360
361
362
363
364
        else:
            texLeft = 0 / 8
            texRight = 1 / 8
            texBottom = 0 / 8
            texTop = 1 / 8
365
            #particle.set_transparency(1)
Ahmad Reza's avatar
Ahmad Reza committed
366
367
368
        self.particle_vertex_list.tex_coords[8 * i: 8 * i + 8] = [texLeft, texBottom, texRight, texBottom,
                                                                  texRight, texTop, texLeft, texTop]

369
        self.particle_vertex_list.colors[16 * i: 16 * i + 16] = (particle.color + [particle.get_transparency()]) * 4
Ahmad Reza's avatar
Ahmad Reza committed
370

371
372
    def init_marker_vertex_list(self):
        self.marker_vertex_list = self.marker_vertex_list = pyglet.graphics.vertex_list \
Ahmad Reza's avatar
Ahmad Reza committed
373
            (4 * len(self.world.markers), 'v2f', 't2f', 'c4f')
374
        self.update_markers(True)
Ahmad Reza's avatar
Ahmad Reza committed
375

376
    def update_markers(self, update_all=True):
Ahmad Reza's avatar
Ahmad Reza committed
377
378
379
380
        if (len(self.world.markers) != 0):
            if self.world.get_marker_deleted():
                self.marker_vertex_list.resize(4 * len(self.world.markers))
                self.world.set_marker_deleted()
Ahmad Reza's avatar
Ahmad Reza committed
381
                update_all = True
Ahmad Reza's avatar
Ahmad Reza committed
382
            for i, marker in enumerate(self.world.markers):
383
                if marker.created:
Ahmad Reza's avatar
Ahmad Reza committed
384
385
                    self.marker_vertex_list.resize(4 * len(self.world.markers))
                    # self.tile_vertex_list.resize(4 * len(self.world.tiles), 8 * len(self.world.tiles))
386
387
388
389
                    marker.created = False
                if update_all or marker.modified:
                    self.update_marker(i, marker)
                    marker.modified = False
Ahmad Reza's avatar
Ahmad Reza committed
390
391
392
        else:
            pass

393
    def update_marker(self, i, marker):
Ahmad Reza's avatar
Ahmad Reza committed
394
        weird = 256 / 220
395
        pos = coordinates_to_sim(marker.coordinates)
Ahmad Reza's avatar
Ahmad Reza committed
396
397
398
        x = pos[0]
        y = pos[1]

399
        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
400
401
                                                                y + weird, x - weird, y + weird]
        texLeft = 7/8
402
        texRight = 1 #8/8
Ahmad Reza's avatar
Ahmad Reza committed
403
404
405
        texBottom = 0/8
        texTop = 1/8

406
        self.marker_vertex_list.tex_coords[8 * i: 8 * i + 8] = [texLeft, texBottom, texRight, texBottom,
Ahmad Reza's avatar
Ahmad Reza committed
407
408
                                                                  texRight, texTop, texLeft, texTop]

409
        self.marker_vertex_list.colors[16 * i: 16 * i + 16] = (marker.color + [marker.get_transparency()]) * 4
Ahmad Reza's avatar
Ahmad Reza committed
410

411
    def draw_world(self, round_start_timestamp):
Ahmad Reza's avatar
Ahmad Reza committed
412
        while not self.simulation_running:
Ahmad Reza's avatar
Ahmad Reza committed
413
            self.dispatch_events()
414
            self.draw()
Ahmad Reza's avatar
Ahmad Reza committed
415
416
            if self.simulation_running or self.window_active is False:
                return
417

Ahmad Reza's avatar
Ahmad Reza committed
418
        self.draw()
419
420
421
422
423
424
425
426
427
428
429

        # waiting until enough time passed to do the next simulation round.
        time_elapsed = time.perf_counter() - round_start_timestamp
        while time_elapsed < 1 / rounds_per_second:
            # waiting for 1/100 of the round_time
            waiting_time = (1 / rounds_per_second) / 100
            time.sleep(waiting_time)
            self.dispatch_events()
            self.draw()
            time_elapsed = time.perf_counter() - round_start_timestamp

Ahmad Reza's avatar
Ahmad Reza committed
430
        return