Commit f9e0f21f authored by Ahmad Reza's avatar Ahmad Reza
Browse files

Merge branches '3D-swarm-sim' and 'master' of gitlab.cs.uni-duesseldorf.de:cheraghi/swarm-sim

parents 2d46066d 00671b9f
......@@ -32,7 +32,7 @@ gui = default_gui
;particle_model_file = 2d_particle.obj
;tile_model_file = 2d_quad_tile.obj
;location_model_file = 2d_location.obj
;grid_size = 1000
;grid_size = 100
;# end of Quadratic grid configs
......@@ -107,6 +107,12 @@ fov = 40
cursor_offset = -10
render_distance = 1000
# turns the rendering of the border on and off
#works only with the border flag set to True
show_border = True
# color of the border lines
border_color = (1.0, 0.0, 0.0, 1.0)
[World]
## False = Unlimited world size
......
......@@ -29,9 +29,9 @@ class CCPGrid(Grid):
def get_box(self, width):
locations = []
for x in range(-int(width / 2), int(width / 2)):
for y in range(-int(width / 2), int(width / 2)):
for z in range(-int(width / 2), int(width / 2)):
for x in range(-width, width+1):
for y in range(-width, width+1):
for z in range(-width, width+1):
if self.are_valid_coordinates((x, y, z)):
locations.append((x, y, z))
......
......@@ -22,9 +22,9 @@ class CubicGrid(Grid):
def get_box(self, width):
locations = []
for x in range(int(-width / 2), int(width / 2)):
for y in range(int(-width / 2), int(width / 2)):
for z in range(int(-width / 2), int(width / 2)):
for x in range(-width, width+1):
for y in range(-width, width+1):
for z in range(-width, width+1):
locations.append((x, y, z))
return locations
......
......@@ -208,7 +208,7 @@ class Grid(ABC):
:return: direction (float, float, float)
"""
v = (target[0]-position[0], target[1] - position[1], target[2] - position[2])
v = (target[0] - position[0], target[1] - position[1], target[2] - position[2])
v_length = np.sqrt((v[0]*v[0])+(v[1]*v[1])+(v[2]*v[2]))
s = self.get_scaling()
best = None
......@@ -226,3 +226,12 @@ class Grid(ABC):
best_angle = angle
best = d
return best
def get_shortest_path(self, start, end):
current = start
path = []
while current != end:
direction = self.get_nearest_direction(current, end)
path.append(direction)
current = self.get_coordinates_in_direction(current, direction)
return path
\ No newline at end of file
......@@ -20,8 +20,8 @@ class QuadraticGrid(Grid):
def get_box(self, width):
locations = []
for x in range(int(-width / 2), int(width / 2)):
for y in range(int(-width / 2), int(width / 2)):
for x in range(-width, width+1):
for y in range(-width, width+1):
locations.append((x, y, 0.0))
return locations
......
......@@ -24,8 +24,8 @@ class TriangularGrid(Grid):
def get_box(self, width):
locs = []
for y in range(-int(width/2),int(width/2)):
for x in range(-int(width / 2), int(width / 2)):
for y in range(-width, width+1):
for x in range(-width, width+1):
if y % 2 == 0:
locs.append((x, y, 0.0))
else:
......@@ -62,16 +62,33 @@ class TriangularGrid(Grid):
def get_dimension_count(self):
return 2
def get_distance(self, start, end):
dx = abs(start[0]-end[0])
dy = abs(start[1]-end[1])
if dx*2 >= dy:
return (dx*2+dy)/2
else:
return dy
def get_center(self):
return 0, 0, 0
def get_scaling(self):
return 1.0, math.sqrt(3/4), 1.0
def get_distance(self,start,end):
if start[1] == end[1] and start[0] != end[0]:
return abs(end[0] - start[0])
elif abs(end[0] - start[0]) - (abs(end[1] - start[1]) * 0.5) > 0:
return abs(end[1] - start[1]) + abs(end[0] - start[0]) - ( abs(end[1] - start[1]) * 0.5 )
return abs(end[1] - start[1])
def get_nearest_direction(self, start, end):
best_distance = None
best_direction = None
for d in self.get_directions_list():
next_coords =self.get_coordinates_in_direction(start, d)
tmp_best_distance = self.get_distance(next_coords, end)
if best_distance is None:
best_distance = tmp_best_distance
best_direction = d
else:
if tmp_best_distance < best_distance:
best_distance = tmp_best_distance
best_direction = d
return best_direction
This diff is collapsed.
......@@ -68,6 +68,9 @@ class ConfigData:
self.cursor_offset = config.getint("Visualization", "cursor_offset")
self.render_distance = config.getint("Visualization", "render_distance")
self.show_border = config.getboolean("Visualization", "show_border")
self.border_color = make_tuple(config.get("Visualization", "border_color"))
self.size_x = config.getfloat("World", "size_x")
self.size_y = config.getfloat("World", "size_y")
self.size_z = config.getfloat("World", "size_z")
......
......@@ -326,6 +326,12 @@ class Visualization:
def set_grid_line_color(self, color):
self._viewer.programs["grid"].set_line_color(color)
def get_grid_border_color(self):
return self._viewer.programs["grid"].get_border_color()
def set_grid_border_color(self, color):
self._viewer.programs["grid"].set_border_color(color)
def get_grid_line_width(self):
return self._viewer.programs["grid"].width
......@@ -369,6 +375,13 @@ class Visualization:
self._viewer.programs["grid"].show_lines = show_lines
self._viewer.glDraw()
def get_show_border(self):
return self._viewer.programs["grid"].show_border
def set_show_border(self, show_border: bool):
self._viewer.programs["grid"].show_border = show_border
self._viewer.glDraw()
def get_show_coordinates(self):
return self._viewer.programs["grid"].show_coordinates
......@@ -422,3 +435,6 @@ class Visualization:
if matter_type == 'tile' or matter_type == 'particle' or matter_type == 'location':
self._viewer.cursor_type = matter_type
self._viewer.update_cursor_data()
def is_running(self):
return self._running
\ No newline at end of file
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QFrame
from PyQt5.QtCore import Qt
class MatterInfoFrame(QFrame):
def __init__(self, *args, **kwargs):
super(MatterInfoFrame, self).__init__(*args, **kwargs)
self.setWindowFlag(Qt.WindowTransparentForInput)
self.setWindowFlag(Qt.WindowDoesNotAcceptFocus)
self.setWindowFlag(Qt.WindowStaysOnTopHint)
self.setStyleSheet("background-color: white; border: 2px solid black;")
self.text = QLabel()
self.text.setStyleSheet("border: 0px; padding: 0px; margin: 0px;")
vbox = QVBoxLayout()
vbox.addWidget(self.text, alignment=Qt.AlignBaseline)
self.setLayout(vbox)
def set_info(self, sim_objects):
info_text = ""
counter = 0
for o in sim_objects:
if counter > 0:
info_text += "\n\n"
info_text += str(o.type).upper()
if o.type == "particle" and o.get_carried_status():
info_text += " (carried)"
if o.type == "tile" and o.get_tile_status():
info_text += "(carried)"
info_text += "\nid: %s" % str(o.get_id())
info_text += "\ncoordinates: %s" % str(o.coordinates)
info_text += "\ncolor: %s" % str(o.color)
info_text += "\nmemory:"
mem = o.read_whole_memory()
for x in mem:
info_text += "\n\t"+str(x)+": "+str(mem[x])
counter += 1
self.text.setText(info_text)
self.adjustSize()
import inspect
import OpenGL.GL as GL
from PIL import Image
from PyQt5 import QtOpenGL, QtGui, QtCore
from lib.swarm_sim_header import eprint
from lib.visualization.MatterInfoFrame import MatterInfoFrame
from lib.visualization.programs.offset_color_carry_program import OffsetColorCarryProgram
from lib.visualization.programs.offset_color_program import OffsetColorProgram
from lib.visualization.programs.grid_program import GridProgram
import numpy as np
import time
import os
import datetime
class OGLWidget(QtOpenGL.QGLWidget):
......@@ -26,6 +26,10 @@ class OGLWidget(QtOpenGL.QGLWidget):
fmt.setSampleBuffers(True)
super(OGLWidget, self).__init__(fmt)
self.info_frame = MatterInfoFrame()
self.info_frame.setParent(self)
self.info_frame.hide()
self.debug = False
self.world = world
self.setMouseTracking(True)
......@@ -142,19 +146,20 @@ class OGLWidget(QtOpenGL.QGLWidget):
self.programs["grid"] = GridProgram(self.world.grid, self.world.config_data.line_color,
self.world.config_data.coordinates_color,
self.world.config_data.tile_model_file)
self.world.config_data.tile_model_file,
(self.world.get_world_x_size(),
self.world.get_world_y_size(),
self.world.get_world_z_size()))
self.programs["grid"].set_world_scaling(self.world.grid.get_scaling())
self.programs["grid"].set_line_scaling(self.world.config_data.line_scaling)
self.programs["grid"].show_lines = self.world.config_data.show_lines
self.programs["grid"].set_model_scaling(self.world.config_data.coordinates_scaling)
self.programs["grid"].show_coordinates = self.world.config_data.show_coordinates
if self.world.config_data.border:
self.programs["grid"].show_border = self.world.config_data.show_border
self.programs["grid"].set_border_color(self.world.config_data.border_color)
self.programs["grid"].update_offsets(self.world.grid.get_box(self.world.grid.size))
# showing coordinates in 2D is not really necessary, since all coordinates are easy to spot on the grid
# and there's no depth
if self.world.grid.get_dimension_count() == 2:
self.programs["grid"].show_coordinates = False
self.programs["center"] = OffsetColorProgram(self.world.config_data.particle_model_file)
self.programs["center"].set_world_scaling(self.world.grid.get_scaling())
self.programs["center"].set_model_scaling((0.3, 0.3, 0.3))
......@@ -237,7 +242,7 @@ class OGLWidget(QtOpenGL.QGLWidget):
:return:
"""
# updating only the current cursor program
self.programs["cursor_"+self.cursor_type].update_offsets(
self.programs["cursor_" + self.cursor_type].update_offsets(
self.world.grid.get_nearest_valid_coordinates(self.camera.cursor_position))
def rotate_light(self, angle):
......@@ -271,7 +276,7 @@ class OGLWidget(QtOpenGL.QGLWidget):
# cursor
if self.ctrl:
self.programs["cursor_"+self.cursor_type].draw()
self.programs["cursor_" + self.cursor_type].draw()
if self.show_focus:
self.programs["focus"].draw()
......@@ -284,25 +289,26 @@ class OGLWidget(QtOpenGL.QGLWidget):
:return:
"""
# starting dragging
if self.ctrl and int(a0.buttons()) & QtCore.Qt.LeftButton:
nl = self.world.grid.get_nearest_valid_coordinates(self.camera.cursor_position)
if self.cursor_type == 'tile':
if nl in self.world.tile_map_coordinates:
self.world.remove_tile_on(nl)
else:
self.world.add_tile(nl)
if self.cursor_type == 'particle':
if nl in self.world.particle_map_coordinates:
self.world.remove_particle_on(nl)
else:
self.world.add_particle(nl)
if self.cursor_type == 'location':
if nl in self.world.location_map_coordinates:
self.world.remove_location_on(nl)
else:
self.world.add_location(nl)
self.update_data()
self.glDraw()
if self.ctrl:
if int(a0.buttons()) & QtCore.Qt.LeftButton:
nl = self.world.grid.get_nearest_valid_coordinates(self.camera.cursor_position)
if self.cursor_type == 'tile':
if nl in self.world.tile_map_coordinates:
self.world.remove_tile_on(nl)
else:
self.world.add_tile(nl)
if self.cursor_type == 'particle':
if nl in self.world.particle_map_coordinates:
self.world.remove_particle_on(nl)
else:
self.world.add_particle(nl)
if self.cursor_type == 'location':
if nl in self.world.location_map_coordinates:
self.world.remove_location_on(nl)
else:
self.world.add_location(nl)
self.update_data()
self.glDraw()
else:
if a0.button() & QtCore.Qt.LeftButton or a0.button() & QtCore.Qt.RightButton:
self.drag_state = True
......@@ -328,12 +334,14 @@ class OGLWidget(QtOpenGL.QGLWidget):
:return:
"""
if self.ctrl:
self.update_info_frame()
if self.world.grid.get_dimension_count() < 3:
self.camera.update_radius(a0.angleDelta().y() / self.zoom_sensitivity)
self.camera.set_cursor_radius(-self.camera.get_radius())
else:
self.camera.update_cursor_radius(-a0.angleDelta().y() / self.cursor_zoom_sensitivity)
else:
self.update_info_frame()
self.camera.update_radius(a0.angleDelta().y() / self.zoom_sensitivity)
self.update_scene()
......@@ -346,13 +354,16 @@ class OGLWidget(QtOpenGL.QGLWidget):
:param a0: mouse move event data
:return:
"""
self.setFocus()
self.mouse_pos = [a0.x(), a0.y()]
if self.ctrl:
self.camera.update_mouse_position(self.mouse_pos)
self.update_cursor_data()
self.glDraw()
self.update_info_frame()
else:
self.update_info_frame()
if self.drag_state:
drag_amount = [self.last_position[0] - self.mouse_pos[0], self.last_position[1] - self.mouse_pos[1]]
......@@ -364,6 +375,32 @@ class OGLWidget(QtOpenGL.QGLWidget):
self.last_position = self.mouse_pos
def update_info_frame(self):
if not self.ctrl:
self.info_frame.hide()
return
vc = self.world.grid.get_nearest_valid_coordinates(self.camera.cursor_position)
matter = []
if vc in self.world.particle_map_coordinates:
matter.append(self.world.particle_map_coordinates[vc])
if self.world.particle_map_coordinates[vc].carried_tile is not None:
matter.append(self.world.particle_map_coordinates[vc].carried_tile)
if self.world.particle_map_coordinates[vc].carried_particle is not None:
matter.append(self.world.particle_map_coordinates[vc].carried_particle)
if vc in self.world.tile_map_coordinates:
matter.append(self.world.tile_map_coordinates[vc])
if vc in self.world.location_map_coordinates:
matter.append(self.world.location_map_coordinates[vc])
if len(matter) > 0:
self.info_frame.show()
self.info_frame.move(self.mouse_pos[0] + 20, self.mouse_pos[1] + 20)
self.info_frame.set_info(matter)
else:
self.info_frame.hide()
def rotate_view(self, drag_amount):
"""
is called by mouseMoveEvent when the left mouse button is being pressed.
......@@ -403,6 +440,7 @@ class OGLWidget(QtOpenGL.QGLWidget):
if a0.key() == QtCore.Qt.Key_Control:
self.ctrl = True
self.camera.update_mouse_position(self.mouse_pos)
self.update_info_frame()
self.update_cursor_data()
self.glDraw()
if self.keyPressEventHandler is not None:
......@@ -418,23 +456,37 @@ class OGLWidget(QtOpenGL.QGLWidget):
if a0.key() == QtCore.Qt.Key_Control:
self.ctrl = False
self.update_cursor_data()
self.update_info_frame()
self.glDraw()
def take_screenshot(self):
"""
takes a screenshot of the OpenGLWidget. saves it with a random name in the screenshots folder.
takes a screenshot of the OpenGLWidget. saves it in the screenshots folder with .
:return:
"""
GL.glReadBuffer(GL.GL_FRONT)
pixels = GL.glReadPixels(0, 0, self.width(), self.height(), GL.GL_RGB, GL.GL_UNSIGNED_BYTE)
i = Image.frombytes('RGB', (self.width(), self.height()), pixels, 'raw')
import os
# if screenshot folder doesn't exist -> create it
if not os.path.exists("screenshots") or not os.path.isdir("screenshots"):
os.mkdir("screenshots")
# if the screenshot folder exists save it, else print an error.
if os.path.exists("screenshots") and os.path.isdir("screenshots"):
i.save("screenshots/screenshot%s.jpg" % str(time.perf_counter_ns()), "JPEG")
now = datetime.datetime.now()
filename = str("screenshots/%d-%d-%d_%d-%d-%d_screenshot.jpg"
% (now.year, now.month, now.day, now.hour, now.minute, now.second))
i.save(filename, "JPEG")
# checks if the file exists. If not, some unknown error occured in the Image library.
if not os.path.exists(filename) or not os.path.isfile(filename):
eprint("Error: screenshot couldn't be saved due to unknown reasons.")
else:
eprint("\"screenshots\" folder doesn't exist. "
"Please create it in the running directory before taking screenshots.")
eprint("Error: couldn't create the screenshot folder.")
def set_background_color(self, color):
self.background = color
GL.glClearColor(*color, 1.0)
self.glDraw()
......@@ -12,7 +12,7 @@ class GridProgram(Program):
vertex_shader_file = "lib/visualization/shader/grid_vertex.glsl"
fragment_shader_file = "lib/visualization/shader/frag.glsl"
def __init__(self, grid, line_color, model_color, coordinate_model_file):
def __init__(self, grid, line_color, model_color, coordinate_model_file, border_size):
"""
initializes/loads/creates all necessary data/buffers/shaders for drawing the Grid
:param grid: the grid object to be visualized
......@@ -24,10 +24,14 @@ class GridProgram(Program):
self.width = 1
self.line_offset = 0
self.line_length = 0
self.border_offset = 0
self.border_length = 0
self.amount = 0
self.show_coordinates = True
self.show_lines = True
self.show_border = False
self.vbos = list()
self.border_size = border_size
super().__init__(self.vertex_shader_file, self.fragment_shader_file, coordinate_model_file)
self.set_line_color(line_color)
self.set_model_color(model_color)
......@@ -39,8 +43,12 @@ class GridProgram(Program):
self.line_offset = len(verts)
self.line_length = len(lines)
border = self._calculate_border()
self.border_offset = self.line_offset + self.line_length
self.border_length = len(border)
# prepare data for the gpu
gpu_data = np.array(verts + lines + normals, dtype=np.float32)
gpu_data = np.array(verts + lines + border + normals, dtype=np.float32)
# create VBO
self.vbos = list(gl.glGenBuffers(2))
......@@ -52,7 +60,8 @@ class GridProgram(Program):
loc = self.get_attribute_location("normal")
gl.glEnableVertexAttribArray(loc)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, gl.GL_FALSE, 0, ctypes.c_void_p((len(verts) + len(lines)) * 12))
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, gl.GL_FALSE, 0,
ctypes.c_void_p((len(verts) + len(lines) + len(border)) * 12))
gl.glBufferData(gl.GL_ARRAY_BUFFER, 12 * len(gpu_data), gpu_data, gl.GL_STATIC_DRAW)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
......@@ -65,13 +74,54 @@ class GridProgram(Program):
gl.glBufferData(gl.GL_ARRAY_BUFFER, 0, np.array([], dtype=np.float32), gl.GL_DYNAMIC_DRAW)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
def _calculate_border(self):
lines = []
if self.grid.get_dimension_count() == 3:
lines = [(-self.border_size[0], -self.border_size[1], -self.border_size[2]),
(-self.border_size[0], -self.border_size[1], self.border_size[2]),
(-self.border_size[0], -self.border_size[1], self.border_size[2]),
(-self.border_size[0], self.border_size[1], self.border_size[2]),
(-self.border_size[0], self.border_size[1], self.border_size[2]),
(-self.border_size[0], self.border_size[1], -self.border_size[2]),
(-self.border_size[0], self.border_size[1], -self.border_size[2]),
(-self.border_size[0], -self.border_size[1], -self.border_size[2]),
(self.border_size[0], -self.border_size[1], -self.border_size[2]),
(self.border_size[0], -self.border_size[1], self.border_size[2]),
(self.border_size[0], -self.border_size[1], self.border_size[2]),
(self.border_size[0], self.border_size[1], self.border_size[2]),
(self.border_size[0], self.border_size[1], self.border_size[2]),
(self.border_size[0], self.border_size[1], -self.border_size[2]),
(self.border_size[0], self.border_size[1], -self.border_size[2]),
(self.border_size[0], -self.border_size[1], -self.border_size[2]),
(-self.border_size[0], self.border_size[1], self.border_size[2]),
(self.border_size[0], self.border_size[1], self.border_size[2]),
(-self.border_size[0], -self.border_size[1], self.border_size[2]),
(self.border_size[0], -self.border_size[1], self.border_size[2]),
(-self.border_size[0], self.border_size[1], -self.border_size[2]),
(self.border_size[0], self.border_size[1], -self.border_size[2]),
(-self.border_size[0], -self.border_size[1], -self.border_size[2]),
(self.border_size[0], -self.border_size[1], -self.border_size[2])]
if self.grid.get_dimension_count() <= 2:
lines = [(-self.border_size[0], -self.border_size[1], 0),
(-self.border_size[0], self.border_size[1], 0),
(-self.border_size[0], self.border_size[1], 0),
(self.border_size[0], self.border_size[1], 0),
(self.border_size[0], self.border_size[1], 0),
(self.border_size[0], -self.border_size[1], 0),
(self.border_size[0], -self.border_size[1], 0),
(-self.border_size[0], -self.border_size[1], 0)]
return lines
def _init_uniforms(self):
"""
initializes the shader uniforms
:return:
"""
super()._init_uniforms()
self.set_line_color((0.0, 0.0, 0.0, 0.0))
self.set_line_color((0.0, 0.0, 0.0, 1.0))
self.set_border_color((1.0, 0.0, 0.0, 1.0))
self.set_model_color((0.0, 0.0, 0.0, 0.0))
self.set_line_scaling((1.0, 1.0, 1.0))
......@@ -81,13 +131,17 @@ class GridProgram(Program):
:return:
"""
self.use()
if self.show_border:
self._draw_part(2)
gl.glDrawArrays(gl.GL_LINES, self.border_offset, self.border_length)
if self.show_lines:
self._drawing_lines(True)
self._draw_part(0)
gl.glDrawArraysInstanced(gl.GL_LINES, self.line_offset, self.line_length, self.amount)
if self.show_coordinates:
self._drawing_lines(False)
self._draw_part(1)
gl.glDrawArraysInstanced(gl.GL_TRIANGLES, 0, self.size, self.amount)
def set_width(self, width):