From 0249d843f9987d1dee5e1ea3799e072a0534994b Mon Sep 17 00:00:00 2001 From: glipR <37640160+glipR@users.noreply.github.com> Date: Wed, 7 Apr 2021 19:13:33 +1000 Subject: [PATCH 1/4] Add randomisation settings to EV3Sim (Not tied to presets). --- ev3sim/assets/default_theme.json | 10 ++ ev3sim/simulation/loader.py | 26 ++++ ev3sim/visual/settings/elements.py | 31 +++++ ev3sim/visual/settings/main_settings.py | 24 +++- .../visual/settings/randomisation_settings.py | 115 ++++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 ev3sim/visual/settings/randomisation_settings.py diff --git a/ev3sim/assets/default_theme.json b/ev3sim/assets/default_theme.json index d8004c57..f683c01a 100644 --- a/ev3sim/assets/default_theme.json +++ b/ev3sim/assets/default_theme.json @@ -326,6 +326,16 @@ "size": "24" } }, + "settings-title": { + "colours": { + "normal_text": "#f7ede2", + "dark_bg": "#00000000" + }, + "font": { + "name": "Roboto", + "size": "24" + } + }, "checkbox-button": { "colours": { "normal_bg": "#ffffff00", diff --git a/ev3sim/simulation/loader.py b/ev3sim/simulation/loader.py index a722ac21..88f61db4 100644 --- a/ev3sim/simulation/loader.py +++ b/ev3sim/simulation/loader.py @@ -328,11 +328,37 @@ def __init__(self): "FPS": ObjectSetting(ScriptLoader, "VISUAL_TICK_RATE"), "tick_rate": ObjectSetting(ScriptLoader, "GAME_TICK_RATE"), "timescale": ObjectSetting(ScriptLoader, "TIME_SCALE"), + "randomise_sensors": ObjectSetting(ScriptLoader, "RANDOMISE_SENSORS"), "console_log": ObjectSetting(Logger, "LOG_CONSOLE"), "workspace_folder": ObjectSetting(StateHandler, "WORKSPACE_FOLDER"), } + from ev3sim.devices.colour.base import ColourSensorMixin + from ev3sim.devices.colour.ev3 import ColorSensor + from ev3sim.devices.compass.ev3 import CompassSensor + from ev3sim.devices.infrared.base import InfraredSensorMixin + from ev3sim.devices.motor.base import MotorMixin + from ev3sim.devices.ultrasonic.base import UltrasonicSensorMixin + + randomisation_settings = { + "COLOUR_SENSOR_RADIUS": ObjectSetting(ColourSensorMixin, "SENSOR_RADIUS"), + "COLOUR_SENSOR_SAMPLING_POINTS": ObjectSetting(ColourSensorMixin, "SENSOR_POINTS"), + "COLOUR_MAX_RGB_BIAS": ObjectSetting(ColorSensor, "MAX_RGB_BIAS"), + "COLOUR_MIN_RGB_BIAS": ObjectSetting(ColorSensor, "MIN_RGB_BIAS"), + "COMPASS_N_POINTS": ObjectSetting(CompassSensor, "NEAREST_POINTS_NUMBER"), + "COMPASS_POINT_VARIANCE": ObjectSetting(CompassSensor, "NEAREST_POINTS_VARIANCE"), + "COMPASS_NOISE_RATE": ObjectSetting(CompassSensor, "NOISE_WIDTH_PER_TICK"), + "COMPASS_NOISE_AMP": ObjectSetting(CompassSensor, "NOISE_AMPLIFIER"), + "COMPASS_MAXIMUM_NOISE_EFFECT": ObjectSetting(CompassSensor, "NOISE_EFFECT_MAX"), + "INFRARED_BIAS_AMP": ObjectSetting(InfraredSensorMixin, "SUBSENSOR_BIAS_MAGNITUDE"), + "MOTOR_MIN_MULT": ObjectSetting(MotorMixin, "MIN_FORCE_PCT"), + "MOTOR_MAX_MULT": ObjectSetting(MotorMixin, "MAX_FORCE_PCT"), + "MOTOR_N_SPEEDS": ObjectSetting(MotorMixin, "FIXED_SPEED_POINTS"), + "ULTRASONIC_NOISE_ANGLE_AMPLITUDE": ObjectSetting(UltrasonicSensorMixin, "ANGLE_RANDOM_AMPLITUDE"), + "ULTRASONIC_OFFSET_AMP": ObjectSetting(UltrasonicSensorMixin, "OFFSET_MAX"), + } settings.addSettingGroup("app", loader_settings) settings.addSettingGroup("screen", screen_settings) + settings.addSettingGroup("randomisation", randomisation_settings) self.shared_info = {} def closeProcesses(self): diff --git a/ev3sim/visual/settings/elements.py b/ev3sim/visual/settings/elements.py index 3d2ae671..1007c247 100644 --- a/ev3sim/visual/settings/elements.py +++ b/ev3sim/visual/settings/elements.py @@ -41,6 +41,37 @@ def generateVisual(self, size, container, manager, idx): raise NotImplementedError() +class Title(SettingsVisualElement): + + num_objs = 1 + + def __init__(self, title, offset): + self.title = title + self.offset = offset + self.menu = None + self.json_keys = "__filename__" + + def getFromJson(self, json_obj): + pass + + def setToJson(self, json_obj): + pass + + def generateVisual(self, size, container, manager, idx): + self.container = container + off = self.offset(size) + label_size = ((size[0] - 40), 40) + label_pos = (off[0] + 20, off[1]) + self.button = pygame_gui.elements.UILabel( + relative_rect=pygame.Rect(*label_pos, *label_size), + manager=manager, + object_id=pygame_gui.core.ObjectID(f"{idx}-title-label", "settings-title"), + container=container, + text=self.title, + ) + return [self.button] + + class Button(SettingsVisualElement): num_objs = 1 diff --git a/ev3sim/visual/settings/main_settings.py b/ev3sim/visual/settings/main_settings.py index c5d4acd1..2fe4604a 100644 --- a/ev3sim/visual/settings/main_settings.py +++ b/ev3sim/visual/settings/main_settings.py @@ -1,4 +1,19 @@ -from ev3sim.visual.settings.elements import NumberEntry, FileEntry, Checkbox +from ev3sim.file_helper import find_abs +from ev3sim.search_locations import config_locations +from ev3sim.visual.settings.elements import NumberEntry, FileEntry, Checkbox, Button +from ev3sim.visual.settings.randomisation_settings import randomisation_settings + + +def onClickConfigEditor(filename): + from ev3sim.visual.manager import ScreenObjectManager + + ScreenObjectManager.instance.pushScreen( + ScreenObjectManager.SCREEN_SETTINGS, + file=find_abs("user_config.yaml", config_locations()), + settings=randomisation_settings, + ) + ScreenObjectManager.instance.screens[ScreenObjectManager.SCREEN_SETTINGS].clearEvents() + main_settings = [ { @@ -16,6 +31,13 @@ Checkbox(["app", "console_log"], True, "Console", (lambda s: (0, 170) if s[0] < 540 else (s[0] / 2, 70))), ], }, + { + "height": (lambda s: 90), + "objects": [ + Checkbox(["app", "randomise_sensors"], False, "Random Noise", (lambda s: (0, 20))), + Button("Randomisation Config", (lambda s: (0, 70) if s[0] < 540 else (s[0] / 2, 20)), onClickConfigEditor), + ], + }, { "height": (lambda s: 90), "objects": [ diff --git a/ev3sim/visual/settings/randomisation_settings.py b/ev3sim/visual/settings/randomisation_settings.py new file mode 100644 index 00000000..58f63e64 --- /dev/null +++ b/ev3sim/visual/settings/randomisation_settings.py @@ -0,0 +1,115 @@ +from ev3sim.visual.settings.elements import NumberEntry, Title + +randomisation_settings = [ + { + "height": (lambda s: 290 if s[0] < 580 else 190), + "objects": [ + Title("Colour Sensor", (lambda s: (0, 20))), + NumberEntry(["randomisation", "COLOUR_SENSOR_RADIUS"], 1, "Sampling Radius", (lambda s: (0, 70)), float), + NumberEntry( + ["randomisation", "COLOUR_SENSOR_SAMPLING_POINTS"], + 100, + "Sampling Points", + (lambda s: (0, 120) if s[0] < 540 else (s[0] / 2, 70)), + int, + ), + NumberEntry( + ["randomisation", "COLOUR_MAX_RGB_BIAS"], + 400, + "Max RGB Bias", + (lambda s: (0, 170) if s[0] < 540 else (0, 120)), + float, + ), + NumberEntry( + ["randomisation", "COLOUR_MIN_RGB_BIAS"], + 230, + "Min RGB Bias", + (lambda s: (0, 220) if s[0] < 540 else (s[0] / 2, 120)), + float, + ), + ], + }, + { + "height": (lambda s: 340 if s[0] < 580 else 240), + "objects": [ + Title("Compass Sensor", (lambda s: (0, 20))), + NumberEntry(["randomisation", "COMPASS_N_POINTS"], 51, "Static Points", (lambda s: (0, 70)), int), + NumberEntry( + ["randomisation", "COMPASS_POINT_VARIANCE"], + 16, + "Static Variance", + (lambda s: (0, 120) if s[0] < 540 else (s[0] / 2, 70)), + float, + ), + NumberEntry( + ["randomisation", "COMPASS_NOISE_RATE"], + 0.03, + "Noise rate of change", + (lambda s: (0, 170) if s[0] < 540 else (0, 120)), + float, + ), + NumberEntry( + ["randomisation", "COMPASS_NOISE_AMP"], + 0.2, + "Noise amplitude", + (lambda s: (0, 220) if s[0] < 540 else (s[0] / 2, 120)), + float, + ), + NumberEntry( + ["randomisation", "COMPASS_MAXIMUM_NOISE_EFFECT"], + 15, + "Max Noise effect", + (lambda s: (0, 270) if s[0] < 540 else (0, 170)), + float, + ), + ], + }, + { + "height": (lambda s: 140), + "objects": [ + Title("Infrared Sensor", (lambda s: (0, 20))), + NumberEntry(["randomisation", "INFRARED_BIAS_AMP"], 5, "Bias Amplitude", (lambda s: (0, 70)), float), + ], + }, + { + "height": (lambda s: 190 if s[0] < 580 else 140), + "objects": [ + Title("Ultrasonic Sensor", (lambda s: (0, 20))), + NumberEntry( + ["randomisation", "ULTRASONIC_NOISE_ANGLE_AMPLITUDE"], + 40, + "Angle change effect", + (lambda s: (0, 70)), + float, + ), + NumberEntry( + ["randomisation", "ULTRASONIC_OFFSET_AMP"], + 5, + "Static noise", + (lambda s: (0, 120) if s[0] < 540 else (s[0] / 2, 70)), + float, + ), + ], + }, + { + "height": (lambda s: 240 if s[0] < 580 else 190), + "objects": [ + Title("Motors", (lambda s: (0, 20))), + NumberEntry(["randomisation", "MOTOR_MIN_MULT"], 0.9, "Min speed mult", (lambda s: (0, 70)), float), + NumberEntry( + ["randomisation", "MOTOR_MAX_MULT"], + 1.05, + "Max speed mult", + (lambda s: (0, 120) if s[0] < 540 else (s[0] / 2, 70)), + float, + ), + NumberEntry( + ["randomisation", "MOTOR_N_SPEEDS"], + 71, + "Static Points", + (lambda s: (0, 170) if s[0] < 540 else (0, 120)), + int, + ), + ], + }, +] From 1478137c7b0ecebd5f9610d9c8fb7f02a19749ba Mon Sep 17 00:00:00 2001 From: glipR <37640160+glipR@users.noreply.github.com> Date: Fri, 9 Apr 2021 11:01:35 +1000 Subject: [PATCH 2/4] Add scrolling to settings. --- ev3sim/visual/settings/menu.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/ev3sim/visual/settings/menu.py b/ev3sim/visual/settings/menu.py index 9e9ac437..60b2b2f9 100644 --- a/ev3sim/visual/settings/menu.py +++ b/ev3sim/visual/settings/menu.py @@ -1,3 +1,4 @@ +from ev3sim.visual.menus.utils import CustomScroll from ev3sim.settings import SettingsManager from ev3sim.visual.settings.elements import TextEntry import os @@ -21,6 +22,25 @@ def clearEvents(self): self.onCancel = None def generateObjects(self): + # Scrolling container + old_y = getattr(getattr(self, "scrolling_container", None), "cur_y", 0) + self.scrolling_container = CustomScroll( + relative_rect=pygame.Rect(0, 0, *self._size), + manager=self, + object_id=pygame_gui.core.ObjectID("scroll_container"), + ) + self.scrolling_container.num_elems = 1 + self.scrolling_container.elems_size = 1 + self.scrolling_container.span_elems = 1 + scroll_height = self._size[1] - 90 + scrolling_size = (self._size[0], scroll_height) + # Setting dimensions and positions on a UIScrollingContainer seems buggy. This works. + self.scrolling_container.set_dimensions(scrolling_size) + self.scrolling_container.set_position(scrolling_size) + self.scrolling_container.cur_y = old_y + self.scrolling_container.set_scroll(old_y) + self._all_objs.append(self.scrolling_container) + yPadding = 20 yOffset = 0 index = 0 @@ -30,6 +50,7 @@ def generateObjects(self): relative_rect=pygame.Rect(0, 0, *self._size), starting_layer_height=-1, manager=self, + container=self.scrolling_container, object_id=pygame_gui.core.ObjectID(f"{index}-bg", "settings-background"), ) self._all_objs.append(container) @@ -49,6 +70,10 @@ def generateObjects(self): yOffset += group["height"](self._size) yOffset += yPadding + self.scrolling_container.elems_size = yOffset + self.scrolling_container.span_elems = min(1, scroll_height / yOffset) + self.scrolling_container.set_scrollable_area_dimensions((self._size[0], yOffset)) + self.bg = pygame_gui.elements.UIPanel( relative_rect=pygame.Rect(0, 0, *self._size), starting_layer_height=-2, @@ -58,7 +83,7 @@ def generateObjects(self): self._all_objs.append(self.bg) container = pygame_gui.elements.UIPanel( - relative_rect=pygame.Rect(20, yOffset, self._size[0] - 40, 80), + relative_rect=pygame.Rect(20, min(yOffset, self._size[1] - 80), self._size[0] - 40, 80), starting_layer_height=-1, manager=self, object_id=pygame_gui.core.ObjectID(f"{index}-bg", "settings-background"), @@ -66,7 +91,7 @@ def generateObjects(self): self._all_objs.append(container) self.save = pygame_gui.elements.UIButton( - relative_rect=pygame.Rect(self._size[0] - 300, yOffset + 10, 120, 60), + relative_rect=pygame.Rect(self._size[0] - 300, min(yOffset + 10, self._size[1] - 70), 120, 60), manager=self, object_id=pygame_gui.core.ObjectID("save-changes", "action_button"), text="Create" if self.creating else "Save", @@ -75,7 +100,7 @@ def generateObjects(self): self._all_objs.append(self.save) self.cancel = pygame_gui.elements.UIButton( - relative_rect=pygame.Rect(self._size[0] - 160, yOffset + 10, 120, 60), + relative_rect=pygame.Rect(self._size[0] - 160, min(yOffset + 10, self._size[1] - 70), 120, 60), manager=self, object_id=pygame_gui.core.ObjectID("cancel-changes", "action_button"), text="Cancel", From 4edff65887e438eb2160d207b8e5843db4e4377d Mon Sep 17 00:00:00 2001 From: glipR <37640160+glipR@users.noreply.github.com> Date: Fri, 9 Apr 2021 11:03:25 +1000 Subject: [PATCH 3/4] Ensure nested settings screens don't use the same object. --- ev3sim/visual/manager.py | 2 ++ ev3sim/visual/settings/main_settings.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ev3sim/visual/manager.py b/ev3sim/visual/manager.py index 24b6f6ad..7b54e874 100644 --- a/ev3sim/visual/manager.py +++ b/ev3sim/visual/manager.py @@ -19,6 +19,7 @@ class ScreenObjectManager: SCREEN_SIM = "SIMULATOR" SCREEN_BOTS = "BOT_SELECT" SCREEN_SETTINGS = "SETTINGS" + SCREEN_RANDOM_SETTINGS = "RANDOM_SETTINGS" SCREEN_WORKSPACE = "WORKSPACE" SCREEN_UPDATE = "UPDATE" SCREEN_BOT_EDIT = "BOT_EDIT" @@ -106,6 +107,7 @@ def initScreens(self): from ev3sim.visual.settings.menu import SettingsMenu self.screens[self.SCREEN_SETTINGS] = SettingsMenu((self.SCREEN_WIDTH, self.SCREEN_HEIGHT)) + self.screens[self.SCREEN_RANDOM_SETTINGS] = SettingsMenu((self.SCREEN_WIDTH, self.SCREEN_HEIGHT)) # Rescue edit screen from ev3sim.visual.menus.rescue_edit import RescueMapEditMenu diff --git a/ev3sim/visual/settings/main_settings.py b/ev3sim/visual/settings/main_settings.py index 2fe4604a..2371f44a 100644 --- a/ev3sim/visual/settings/main_settings.py +++ b/ev3sim/visual/settings/main_settings.py @@ -8,11 +8,11 @@ def onClickConfigEditor(filename): from ev3sim.visual.manager import ScreenObjectManager ScreenObjectManager.instance.pushScreen( - ScreenObjectManager.SCREEN_SETTINGS, + ScreenObjectManager.SCREEN_RANDOM_SETTINGS, file=find_abs("user_config.yaml", config_locations()), settings=randomisation_settings, ) - ScreenObjectManager.instance.screens[ScreenObjectManager.SCREEN_SETTINGS].clearEvents() + ScreenObjectManager.instance.screens[ScreenObjectManager.SCREEN_RANDOM_SETTINGS].clearEvents() main_settings = [ From f086c3c7affcca5f7822651a2645c97aa045026b Mon Sep 17 00:00:00 2001 From: glipR <37640160+glipR@users.noreply.github.com> Date: Fri, 9 Apr 2021 11:08:20 +1000 Subject: [PATCH 4/4] Reset scroll height for settings. --- ev3sim/visual/settings/menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ev3sim/visual/settings/menu.py b/ev3sim/visual/settings/menu.py index 60b2b2f9..9021dae6 100644 --- a/ev3sim/visual/settings/menu.py +++ b/ev3sim/visual/settings/menu.py @@ -23,7 +23,7 @@ def clearEvents(self): def generateObjects(self): # Scrolling container - old_y = getattr(getattr(self, "scrolling_container", None), "cur_y", 0) + old_y = 0 self.scrolling_container = CustomScroll( relative_rect=pygame.Rect(0, 0, *self._size), manager=self,