Commit aa02e01a authored by Karol Actun's avatar Karol Actun
Browse files

- added video recording

- scenario and screenshots "save as" buttons
- anti aliasing (default 4 samples)
- bug fixes + many small changes to the default gui
- svg screenshots (toms_svg_generator, only for TriagularGrid)
- todo next: svg screenshots for all grids
parent 14b47aa3
...@@ -162,4 +162,6 @@ config.ini ...@@ -162,4 +162,6 @@ config.ini
.dropbox .dropbox
outputs/ outputs/
lib/__pycache__/* screenshots/
\ No newline at end of file videos/
lib/__pycache__/*
...@@ -19,6 +19,8 @@ For Linux: ...@@ -19,6 +19,8 @@ For Linux:
6. sudo pip3 install PyQt5 6. sudo pip3 install PyQt5
7. sudo pip3 install opencv-python
for older Systems (e.g. Ubuntu 14.04) install the PyQt5 version 5.10.1 for older Systems (e.g. Ubuntu 14.04) install the PyQt5 version 5.10.1
6. sudo pip3 install PyQt5==5.10.1 6. sudo pip3 install PyQt5==5.10.1
...@@ -52,6 +54,7 @@ For Windows/Linux/MacOs: ...@@ -52,6 +54,7 @@ For Windows/Linux/MacOs:
4. PyOpenGL 4. PyOpenGL
5. Pillow 5. Pillow
6. PyQt5 (in version 5.10.1 for older Systems like Ubuntu 14.04) 6. PyQt5 (in version 5.10.1 for older Systems like Ubuntu 14.04)
7. opencv-python
- press Okey - press Okey
- wait until everything is installed - wait until everything is installed
- chose Run->swarm-sim.py - chose Run->swarm-sim.py
......
from PyQt5.QtGui import QColor, QIntValidator from PyQt5.QtGui import QColor, QIntValidator
from PyQt5.QtWidgets import (QVBoxLayout, QPushButton, QColorDialog, QRadioButton, QLabel, QTabWidget, from PyQt5.QtWidgets import (QVBoxLayout, QPushButton, QColorDialog, QRadioButton, QLabel, QTabWidget,
QSlider, QHBoxLayout, QCheckBox, QTabBar, QLineEdit) QSlider, QHBoxLayout, QCheckBox, QTabBar, QLineEdit, QGroupBox, QComboBox, QStyle)
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from lib.swarm_sim_header import eprint from lib.visualization.utils import show_msg
start_stop_button = None start_stop_button = None
world = None world = None
vis = None vis = None
simtab = None
def create_gui(w, v): def create_gui(w, v):
global world, vis global world, vis, simtab
world = w world = w
vis = v vis = v
tabbar = QTabWidget() tabbar = QTabWidget()
tabbar.setMinimumWidth(200) tabbar.setMinimumWidth(200)
tabbar.addTab(sim_tab(), "Simulation") simtab = sim_tab()
tabbar.addTab(simtab, "Simulation")
tabbar.addTab(vis_tab(), "Visualization") tabbar.addTab(vis_tab(), "Visualization")
tabbar.addTab(grid_tab(), "Grid") tabbar.addTab(grid_tab(), "Grid")
tabbar.addTab(matter_tab(), "Matter") tabbar.addTab(matter_tab(), "Matter")
tabbar.addTab(help_tab(), "Help") tabbar.addTab(help_tab(), "Help")
return tabbar return tabbar
def set_disable_sim(disable_flag):
global simtab
simtab.setDisabled(disable_flag)
def key_handler(key, w, v): def key_handler(key, w, v):
if key == Qt.Key_Space: if key == Qt.Key_Space:
start_stop_button.click() start_stop_button.click()
...@@ -77,61 +83,153 @@ def create_slider(tick_interval: int, tick_position: int, max_position: int, min ...@@ -77,61 +83,153 @@ def create_slider(tick_interval: int, tick_position: int, max_position: int, min
def sim_tab(): def sim_tab():
tab = QTabBar() tab = QTabBar()
layout = QVBoxLayout() layout = QVBoxLayout()
global start_stop_button global start_stop_button
# start stop button # start stop button
start_stop_button = QPushButton("start Simulation") start_stop_button = QPushButton("start Simulation")
start_stop_button.setIcon(start_stop_button.style().standardIcon(QStyle.SP_MediaPlay))
start_stop_button.setStyleSheet("font-weight:bold;")
status = QLabel("Simulation not running") status = QLabel("Simulation not running")
status.setStyleSheet("color:#ff0000;") status.setStyleSheet("font-weight:bold;color:#bb4444;")
def start_stop_sim(): def start_stop_sim():
vis.start_stop() vis.start_stop()
if vis.is_running(): if vis.is_running():
status.setText("Simulation running") status.setText("Simulation running")
status.setStyleSheet("color:#00ff00;") status.setStyleSheet("font-weight:bold;color:#44bb44;")
start_stop_button.setText("pause Simulation") start_stop_button.setText("pause Simulation")
start_stop_button.setIcon(start_stop_button.style().standardIcon(QStyle.SP_MediaPause))
else: else:
status.setText("Simulation paused") status.setText("Simulation paused")
status.setStyleSheet("color:#0000ff;") status.setStyleSheet("font-weight:bold;color:#4444bb;")
start_stop_button.setText("unpause Simulation") start_stop_button.setText("unpause Simulation")
start_stop_button.setIcon(start_stop_button.style().standardIcon(QStyle.SP_MediaPlay))
start_stop_button.clicked.connect(start_stop_sim) start_stop_button.clicked.connect(start_stop_sim)
# screenshots button # screenshot buttons
screenshot_button = QPushButton("take Screenshot") quick_screenshot = QPushButton("quick")
save_as_screenshot = QPushButton("save as")
def qs():
vis.take_screenshot(True)
def take_screenshot(): def sas():
vis.take_screenshot() vis.take_screenshot(False)
screenshot_button.clicked.connect(take_screenshot)
quick_screenshot.clicked.connect(qs)
save_as_screenshot.clicked.connect(sas)
# WARNING: VECTOR SCREENSHOTS ONLY FOR TRIANGULARGRID IMPLEMENTED!
vector_screenshot = QPushButton("export as vector graphics (svg)")
def eas():
vis.take_vector_screenshot()
vector_screenshot.clicked.connect(eas)
screenshot_buttons = QHBoxLayout()
screenshot_buttons.addWidget(quick_screenshot)
screenshot_buttons.addWidget(save_as_screenshot)
vbox = QVBoxLayout()
vbox.addLayout(screenshot_buttons)
vbox.addWidget(vector_screenshot)
screenshot_box = QGroupBox("screenshot")
screenshot_box.setLayout(vbox)
# recording buttons
start_recording_button = QPushButton("start")
del_recording_button = QPushButton("delete")
export_recording_button = QPushButton("export")
hbox = QHBoxLayout()
hbox.addWidget(start_recording_button)
hbox.addWidget(del_recording_button)
hbox.addWidget(export_recording_button)
recording_box = QGroupBox("Recording")
recording_box.setLayout(hbox)
def startstoprec():
if vis.is_recording():
start_recording_button.setText("start")
recording_box.setTitle("recording")
if vis.is_running():
start_stop_sim()
vis.stop_recording()
else:
recording_box.setTitle("Recording (running)")
start_recording_button.setText("stop")
vis.start_recording()
start_recording_button.clicked.connect(startstoprec)
def del_rec():
if vis.is_recording():
startstoprec()
vis.delete_recording()
del_recording_button.clicked.connect(del_rec)
export_recording_button.clicked.connect(vis.export_recording)
# reset button
reset_button = QPushButton("reset Simulation") reset_button = QPushButton("reset Simulation")
def reset_sim(): def reset_sim():
world.reset() world.reset()
start_stop_button.setText("start Simulation")
start_stop_button.setIcon(start_stop_button.style().standardIcon(QStyle.SP_MediaPlay))
status.setText("Simulation not running") status.setText("Simulation not running")
status.setStyleSheet("color:#ff0000;") status.setStyleSheet("font-weight:bold;color:#bb4444;")
reset_button.clicked.connect(reset_sim) reset_button.clicked.connect(reset_sim)
save_scenario_button = QPushButton("save Scenario") quick_scenario_button = QPushButton("quick")
save_as_scenario_button = QPushButton("save as")
def qs_scenario(): world.save_scenario(True)
def sas_scenario(): world.save_scenario(False)
def save_scenario(): quick_scenario_button.clicked.connect(qs_scenario)
world.save_scenario() save_as_scenario_button.clicked.connect(sas_scenario)
scenario_buttons = QHBoxLayout()
scenario_buttons.addWidget(quick_scenario_button)
scenario_buttons.addWidget(save_as_scenario_button)
scenario_box = QGroupBox("scenario")
scenario_box.setLayout(scenario_buttons)
sim_box = QGroupBox("main controls")
vbox = QVBoxLayout()
vbox.addWidget(status)
vbox.addWidget(start_stop_button)
vbox.addLayout(get_rps_slider())
vbox.addWidget(reset_button)
sim_box.setLayout(vbox)
layout.addWidget(sim_box)
layout.addWidget(screenshot_box)
layout.addWidget(scenario_box)
layout.addWidget(recording_box)
new_matter_box = QGroupBox("new matter")
vbox = QVBoxLayout()
vbox.addLayout(get_matter_radios())
vbox.addWidget(get_new_matter_color_picker())
new_matter_box.setLayout(vbox)
layout.addWidget(new_matter_box)
save_scenario_button.clicked.connect(save_scenario)
layout.addWidget(status, alignment=Qt.AlignBaseline)
layout.addWidget(start_stop_button, alignment=Qt.AlignBaseline)
layout.addLayout(get_rps_slider())
layout.addWidget(screenshot_button, alignment=Qt.AlignBaseline)
layout.addWidget(reset_button, alignment=Qt.AlignBaseline)
layout.addWidget(save_scenario_button, alignment=Qt.AlignBaseline)
layout.addLayout(get_matter_radios())
layout.addStretch(0) layout.addStretch(0)
tab.setLayout(layout) tab.setLayout(layout)
return tab return tab
def get_new_matter_color_picker():
cp_button = QPushButton("change color of new matter")
def cp():
cd = QColorDialog()
cd.setCurrentColor(QColor.fromRgbF(*vis.get_added_matter_color()))
cd.exec()
if cd.result() == 1:
vis.set_added_matter_color((cd.selectedColor().getRgbF()))
cp_button.clicked.connect(cp)
return cp_button
def get_matter_radios(): def get_matter_radios():
p_radio = QRadioButton("particle") p_radio = QRadioButton("particle")
p_radio.setChecked(False) p_radio.setChecked(False)
...@@ -139,7 +237,6 @@ def get_matter_radios(): ...@@ -139,7 +237,6 @@ def get_matter_radios():
t_radio.setChecked(True) t_radio.setChecked(True)
l_radio = QRadioButton("location") l_radio = QRadioButton("location")
l_radio.setChecked(False) l_radio.setChecked(False)
desc = QLabel("matter type to be created/removed:")
def on_toggle(): def on_toggle():
if p_radio.isChecked(): if p_radio.isChecked():
...@@ -156,10 +253,9 @@ def get_matter_radios(): ...@@ -156,10 +253,9 @@ def get_matter_radios():
l_radio.toggled.connect(on_toggle) l_radio.toggled.connect(on_toggle)
vbox = QVBoxLayout() vbox = QVBoxLayout()
vbox.addWidget(desc, alignment=Qt.AlignBaseline) vbox.addWidget(p_radio)
vbox.addWidget(p_radio, alignment=Qt.AlignBaseline) vbox.addWidget(t_radio)
vbox.addWidget(t_radio, alignment=Qt.AlignBaseline) vbox.addWidget(l_radio)
vbox.addWidget(l_radio, alignment=Qt.AlignBaseline)
return vbox return vbox
...@@ -167,29 +263,43 @@ def get_matter_radios(): ...@@ -167,29 +263,43 @@ def get_matter_radios():
def get_rps_slider(): def get_rps_slider():
hbox = QVBoxLayout() hbox = QVBoxLayout()
desc = QLabel("rounds per second (%d) : " % vis.get_rounds_per_second()) desc = QLabel("rounds per second (%d) : " % vis.get_rounds_per_second())
hbox.addWidget(desc, alignment=Qt.AlignBaseline) hbox.addWidget(desc)
def set_rps(value): def set_rps(value):
vis.set_rounds_per_second(value) vis.set_rounds_per_second(value)
desc.setText("rounds per second (%d) : " % vis.get_rounds_per_second()) desc.setText("rounds per second (%d) : " % vis.get_rounds_per_second())
hbox.addWidget(create_slider(10, 2, 120, 1, vis.get_rounds_per_second(), set_rps), alignment=Qt.AlignBaseline) hbox.addWidget(create_slider(10, 2, 120, 1, vis.get_rounds_per_second(), set_rps))
return hbox return hbox
def vis_tab(): def vis_tab():
tab = QTabBar() tab = QTabBar()
layout = QVBoxLayout() layout = QVBoxLayout()
layout.addLayout(get_projection_switch()) proj_box = QGroupBox("projection settings")
layout.addLayout(get_fov_slider()) vbox = QVBoxLayout()
layout.addLayout(get_render_distance_slider()) vbox.addLayout(get_projection_switch())
layout.addLayout(get_drag_sens_slider()) vbox.addLayout(get_fov_slider())
layout.addLayout(get_zoom_sens_slider()) vbox.addLayout(get_render_distance_slider())
layout.addLayout(get_rota_sens_slider()) proj_box.setLayout(vbox)
layout.addLayout(get_vis_show_checkboxes()) layout.addWidget(proj_box)
reset_position_button = QPushButton("reset position") sens_slider_box = QGroupBox("control sensitivity")
vbox = QVBoxLayout()
vbox.addLayout(get_drag_sens_slider())
vbox.addLayout(get_zoom_sens_slider())
vbox.addLayout(get_rota_sens_slider())
sens_slider_box.setLayout(vbox)
layout.addWidget(sens_slider_box)
misc_box = QGroupBox("miscellaneous")
vbox = QVBoxLayout()
vbox.addLayout(get_antialiasing_combobox())
vbox.addLayout(get_vis_show_checkboxes())
reset_position_button = QPushButton("reset camera position")
reset_position_button.clicked.connect(vis.reset_camera_position) reset_position_button.clicked.connect(vis.reset_camera_position)
layout.addWidget(reset_position_button, alignment=Qt.AlignBaseline) vbox.addWidget(reset_position_button)
misc_box.setLayout(vbox)
layout.addWidget(misc_box)
layout.addStretch(0) layout.addStretch(0)
tab.setLayout(layout) tab.setLayout(layout)
return tab return tab
...@@ -221,6 +331,8 @@ def matter_tab(): ...@@ -221,6 +331,8 @@ def matter_tab():
def get_scaler(mattertype): def get_scaler(mattertype):
if mattertype == "particle": if mattertype == "particle":
current_scaling = vis.get_particle_scaling()
def x_scaler_change(value): def x_scaler_change(value):
cs = vis.get_particle_scaling() cs = vis.get_particle_scaling()
new_scaling = (value/10.0, cs[1], cs[2]) new_scaling = (value/10.0, cs[1], cs[2])
...@@ -236,6 +348,8 @@ def get_scaler(mattertype): ...@@ -236,6 +348,8 @@ def get_scaler(mattertype):
new_scaling = (cs[0], cs[1], value/10.0) new_scaling = (cs[0], cs[1], value/10.0)
vis.set_particle_scaling(new_scaling) vis.set_particle_scaling(new_scaling)
elif mattertype == "tile": elif mattertype == "tile":
current_scaling = vis.get_tile_scaling()
def x_scaler_change(value): def x_scaler_change(value):
cs = vis.get_tile_scaling() cs = vis.get_tile_scaling()
new_scaling = (value / 10.0, cs[1], cs[2]) new_scaling = (value / 10.0, cs[1], cs[2])
...@@ -251,6 +365,8 @@ def get_scaler(mattertype): ...@@ -251,6 +365,8 @@ def get_scaler(mattertype):
new_scaling = (cs[0], cs[1], value / 10.0) new_scaling = (cs[0], cs[1], value / 10.0)
vis.set_tile_scaling(new_scaling) vis.set_tile_scaling(new_scaling)
else: else:
current_scaling = vis.get_location_scaling()
def x_scaler_change(value): def x_scaler_change(value):
cs = vis.get_location_scaling() cs = vis.get_location_scaling()
new_scaling = (value / 10.0, cs[1], cs[2]) new_scaling = (value / 10.0, cs[1], cs[2])
...@@ -266,7 +382,6 @@ def get_scaler(mattertype): ...@@ -266,7 +382,6 @@ def get_scaler(mattertype):
new_scaling = (cs[0], cs[1], value / 10.0) new_scaling = (cs[0], cs[1], value / 10.0)
vis.set_location_scaling(new_scaling) vis.set_location_scaling(new_scaling)
current_scaling = vis.get_particle_scaling()
x_desc = QLabel("x scale:") x_desc = QLabel("x scale:")
y_desc = QLabel("y scale:") y_desc = QLabel("y scale:")
z_desc = QLabel("z scale:") z_desc = QLabel("z scale:")
...@@ -275,19 +390,19 @@ def get_scaler(mattertype): ...@@ -275,19 +390,19 @@ def get_scaler(mattertype):
z_scaler = create_slider(2, 2, 20, 1, current_scaling[0]*10, z_scaler_change) z_scaler = create_slider(2, 2, 20, 1, current_scaling[0]*10, z_scaler_change)
hbox1 = QHBoxLayout() hbox1 = QHBoxLayout()
hbox1.addWidget(x_desc, alignment=Qt.AlignBaseline) hbox1.addWidget(x_desc)
hbox1.addWidget(x_scaler, alignment=Qt.AlignBaseline) hbox1.addWidget(x_scaler)
hbox2 = QHBoxLayout() hbox2 = QHBoxLayout()
hbox2.addWidget(y_desc, alignment=Qt.AlignBaseline) hbox2.addWidget(y_desc)
hbox2.addWidget(y_scaler, alignment=Qt.AlignBaseline) hbox2.addWidget(y_scaler)
hbox3 = QHBoxLayout() hbox3 = QHBoxLayout()
hbox3.addWidget(z_desc, alignment=Qt.AlignBaseline) hbox3.addWidget(z_desc)
hbox3.addWidget(z_scaler, alignment=Qt.AlignBaseline) hbox3.addWidget(z_scaler)
vbox = QVBoxLayout() vbox = QVBoxLayout()
vbox.addWidget(QLabel("%s scaling:" % mattertype), alignment=Qt.AlignBaseline) vbox.addWidget(QLabel("%s scaling:" % mattertype))
vbox.addLayout(hbox1) vbox.addLayout(hbox1)
vbox.addLayout(hbox2) vbox.addLayout(hbox2)
vbox.addLayout(hbox3) vbox.addLayout(hbox3)
...@@ -298,20 +413,20 @@ def get_scaler(mattertype): ...@@ -298,20 +413,20 @@ def get_scaler(mattertype):
def get_fov_slider(): def get_fov_slider():
hbox = QVBoxLayout() hbox = QVBoxLayout()
desc = QLabel("(only for perspective projection)\nfield of view (%d°) : " % vis.get_field_of_view()) desc = QLabel("(only for perspective projection)\nfield of view (%d°) : " % vis.get_field_of_view())
hbox.addWidget(desc, alignment=Qt.AlignBaseline) hbox.addWidget(desc)
def set_fov(value): def set_fov(value):
vis.set_field_of_view(value) vis.set_field_of_view(value)
desc.setText("(only for perspective projection)\nfield of view (%d°) : " % vis.get_field_of_view()) desc.setText("(only for perspective projection)\nfield of view (%d°) : " % vis.get_field_of_view())
hbox.addWidget(create_slider(10, 2, 120, 10, vis.get_field_of_view(), set_fov), alignment=Qt.AlignBaseline) hbox.addWidget(create_slider(10, 2, 120, 10, vis.get_field_of_view(), set_fov))
return hbox return hbox
def get_drag_sens_slider(): def get_drag_sens_slider():
hbox = QVBoxLayout() hbox = QHBoxLayout()
desc = QLabel("drag sensitivity:") desc = QLabel("drag:")
hbox.addWidget(desc, alignment=Qt.AlignBaseline) hbox.addWidget(desc)
def set_ds(value): def set_ds(value):
vis.set_drag_sensitivity(5100-value) vis.set_drag_sensitivity(5100-value)
...@@ -322,56 +437,57 @@ def get_drag_sens_slider(): ...@@ -322,56 +437,57 @@ def get_drag_sens_slider():
def get_zoom_sens_slider(): def get_zoom_sens_slider():
hbox = QVBoxLayout() hbox = QHBoxLayout()
desc = QLabel("zoom sensitivity:") desc = QLabel("zoom:")
hbox.addWidget(desc, alignment=Qt.AlignBaseline) hbox.addWidget(desc)
def set_zs(value): def set_zs(value):
vis.set_zoom_sensitivity(1001-value) vis.set_zoom_sensitivity(1001-value)
hbox.addWidget(create_slider(100, 2, 1000, 1, 1001-vis.get_zoom_sensitivity(), set_zs), alignment=Qt.AlignBaseline) hbox.addWidget(create_slider(100, 2, 1000, 1, 1001-vis.get_zoom_sensitivity(), set_zs))
return hbox return hbox
def get_rota_sens_slider(): def get_rota_sens_slider():
hbox = QVBoxLayout() hbox = QHBoxLayout()
desc = QLabel("(only for 3D)\nrotation sensitivity:") desc = QLabel("(only 3D)\nrotation:")
hbox.addWidget(desc, alignment=Qt.AlignBaseline) hbox.addWidget(desc)
def set_rs(value): def set_rs(value):
vis.set_rotation_sensitivity(11-value) vis.set_rotation_sensitivity(11-value)
hbox.addWidget(create_slider(1, 2, 10, 1, 11-vis.get_rotation_sensitivity(), set_rs), alignment=Qt.AlignBaseline) hbox.addWidget(create_slider(1, 2, 10, 1, 11-vis.get_rotation_sensitivity(), set_rs))
return hbox return hbox
def get_projection_switch(): def get_projection_switch():
vbox = QVBoxLayout() vbox = QVBoxLayout()
desc = QLabel("projection type:") desc = QLabel("projection type:")
vbox.addWidget(desc, alignment=Qt.AlignBaseline) vbox.addWidget(desc)
o = QRadioButton("orthographic") o = QRadioButton("orthographic")
def orth_toggle(): def orth_toggle():
if o.isChecked(): if o.isChecked():
vis.set_projection_type("ortho") vis.set_projection_type("ortho")
o.toggled.connect(orth_toggle)
p = QRadioButton("perspective") p = QRadioButton("perspective")
def pers_toggle():
if p.isChecked():
vis.set_projection_type("perspective")
if vis.get_projection_type() == "ortho": if vis.get_projection_type() == "ortho":
o.setChecked(True) o.setChecked(True)
else: else:
p.setChecked(True) p.setChecked(True)
def pers_toggle():