import store from './js/ui_state/store/index.js'; import MorphologiesList from './js/ui_state/components/morphologies_list.js'; import AgentsList from './js/ui_state/components/agents_list.js'; import MainButtons from './js/ui_state/components/main_buttons.js'; import ParkourConfig from './js/ui_state/components/parkour_config.js'; import DrawingMode from "./js/ui_state/components/drawing_mode.js"; import AdvancedOptions from "./js/ui_state/components/advanced_options.js"; import EnvsSet from "./js/ui_state/components/envs_set.js"; import GlobalElements from "./js/ui_state/components/global_elements.js"; import AboutTab from "./js/ui_state/components/about_tab.js"; /** * Opens the given modal. * @param modal {HTMLDivElement} */ window.openModal = (modal) => { modal.style.backgroundColor = 'rgba(0,0,0,0.5)'; modal.style.display = "block"; modal.classList.add('show'); } /** * Closes the given modal and clears the text fields. * @param modal {HTMLDivElement} */ window.closeModal = (modal) => { modal.classList.remove('show'); modal.style.display = 'none'; modal.querySelectorAll('.text-field').forEach((span, index) => { span.value = ""; }); } // Save env modal setup const saveEnvModal = document.querySelector('#saveEnvModal'); saveEnvModal.querySelectorAll('.btn').forEach((span, index) => { span.addEventListener('click', () => { // If the confirm button is clicked if(span.getAttribute('id') == "save-confirm-btn"){ // Gets the name and description values let name = saveEnvModal.querySelector('#env-name').value; if(name == ""){ name = "Custom Environment " + store.state.envsSets.customEnvsSet.length; } let description = saveEnvModal.querySelector('#env-description').value; // Saves the current position of the agents for(let i = 0; i < store.state.agents.length; i++){ store.dispatch('setAgentInitPos', {index: i, init_pos: window.game.env.agents[i].agent_body.reference_head_object.GetPosition().Clone()}); } // Adjusts the zoom and scroll to capture the thumbnail let previous_zoom = window.game.env.zoom; let previous_scroll = [...window.game.env.scroll]; window.game.env.set_zoom(THUMBNAIL_ZOOM); window.game.env.set_scroll(null, THUMBNAIL_SCROLL_X, 0); window.game.env.render(); // Creates the state of the current env let env = { terrain: { ground: [...window.ground], ceiling: [...window.ceiling], parkourConfig: Object.assign({}, store.state.parkourConfig.terrain), creepersConfig: Object.assign({}, store.state.parkourConfig.creepers) }, agents: [...store.state.agents], description: { name: name, text: description }, // Captures the canvas to create the thumbnail of the env image: window.canvas.canvas.toDataURL("image/png").replace("image/png", "image/octet-stream") }; // Adds the env to the custom set store.dispatch('addEnv',{set: 'custom', env: env}); // Sets back the zoom and scroll to the previous values window.game.env.set_zoom(previous_zoom); window.game.env.set_scroll(null, previous_scroll[0], previous_scroll[1]); window.game.env.render(); } closeModal(saveEnvModal); }); }); // Main buttons setup const runButton = document.querySelector("#runButton"); runButton.addEventListener('click', () => { store.dispatch('toggleRun', {}); }); const resetButton = document.querySelector("#resetButton"); resetButton.addEventListener('click', () => { store.dispatch('resetSimulation', {}); }); const saveEnvButton = document.querySelector('#saveEnvButton'); saveEnvButton.addEventListener('click', () => { openModal(saveEnvModal); }); const mainButtonsInstance = new MainButtons(); mainButtonsInstance.render(); // Morphologies list setup const morphologiesListInstance = new MorphologiesList(); morphologiesListInstance.render(); // Agents list setup const agentListInstance = new AgentsList(); agentListInstance.render(); // Environments set setup const envsSetInstance = new EnvsSet(); envsSetInstance.render(); // Parkour config parameters setup const dim1SliderElement = document.querySelector("#dim1Slider") dim1SliderElement.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "dim1", value: parseFloat(dim1SliderElement.value) }); }); const dim2SliderElement = document.querySelector("#dim2Slider") dim2SliderElement.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "dim2", value: parseFloat(dim2SliderElement.value) }); }); const dim3SliderElement = document.querySelector("#dim3Slider") dim3SliderElement.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "dim3", value: parseFloat(dim3SliderElement.value) }); }); const smoothingSliderElement = document.querySelector("#smoothingSlider") smoothingSliderElement.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "smoothing", value: parseFloat(smoothingSliderElement.value) }); }); const waterSliderElement = document.querySelector("#waterSlider") waterSliderElement.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "waterLevel", value: parseFloat(waterSliderElement.value) }); }); const creepersWidthSlider = document.querySelector("#creepersWidthSlider"); creepersWidthSlider.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "width", value: parseFloat(creepersWidthSlider.value) }); }); const creepersHeightSlider = document.querySelector("#creepersHeightSlider"); creepersHeightSlider.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "height", value: parseFloat(creepersHeightSlider.value) }); }); const creepersSpacingSlider = document.querySelector("#creepersSpacingSlider"); creepersSpacingSlider.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "spacing", value: parseFloat(creepersSpacingSlider.value) }); }); const creepersTypeSelect = document.querySelector("#creepersType"); creepersTypeSelect.addEventListener('input', () => { store.dispatch('changeParkourConfig', { name: "type", value: creepersTypeSelect.value }); }); const parkourConfigInstance = new ParkourConfig(); parkourConfigInstance.render(); // Tabs buttons setup const gettingStartedBtn = document.querySelector('#getting-started-btn'); gettingStartedBtn.addEventListener('click', () => { store.dispatch('switchTab', 'getting_started'); }) const parkourCustomTab = document.querySelector('#parkour-custom-btn'); parkourCustomTab.addEventListener('click', () => { // Shows the "Draw Yourself!" subtab when opening the "Parkour Customization" tab if(store.state.activeTab != 'draw_yourself' && store.state.activeTab != 'proc_gen'){ let drawTabBtn = document.querySelector('#draw-tab-btn'); let drawYourselfTab = new bootstrap.Tab(drawTabBtn); drawYourselfTab.show(); store.dispatch('switchTab', 'draw_yourself'); } }); const drawYourselfBtn = document.querySelector('#draw-tab-btn'); drawYourselfBtn.addEventListener('click', () => { store.dispatch('switchTab', 'draw_yourself'); }); const procGenBtn = document.querySelector('#proc-gen-tab-btn'); procGenBtn.addEventListener('click', () => { store.dispatch('switchTab', 'proc_gen'); }); const advancedOptionsBtn = document.querySelector('#advanced-options-btn'); advancedOptionsBtn.addEventListener('click', () => { store.dispatch('switchTab', 'advanced_options'); }); const aboutBtn = document.querySelector('#about-btn'); aboutBtn.addEventListener('click', () => { store.dispatch('switchTab', 'about'); }); // Drawing Mode buttons setup const drawGroundButton = document.querySelector('#drawGroundButton'); drawGroundButton.addEventListener('click', () => { store.dispatch('drawGround', !store.state.drawingModeState.drawing_ground); }); const drawCeilingButton = document.querySelector('#drawCeilingButton'); drawCeilingButton.addEventListener('click', () => { store.dispatch('drawCeiling', !store.state.drawingModeState.drawing_ceiling); }); const eraseButton = document.querySelector('#eraseButton'); eraseButton.addEventListener('click', () => { store.dispatch('erase', !store.state.drawingModeState.erasing); }); const clearButton = document.querySelector('#clearButton'); clearButton.addEventListener('click', () => { store.dispatch('clear', {}); }); const generateTerrainButton = document.querySelector('#generateTerrainButton'); generateTerrainButton.addEventListener('click', () => { store.dispatch('generateTerrain', store.state.drawingModeState.drawing); }); const drawingModeInstance = new DrawingMode(); drawingModeInstance.render(); // Advanced Options setup const drawJointsSwitch = document.querySelector("#drawJointsSwitch"); drawJointsSwitch.addEventListener('input', () => { store.dispatch('toggleSwitch', {name: 'drawJoints', value: drawJointsSwitch.checked} ); }); const drawLidarsSwitch = document.querySelector("#drawLidarsSwitch"); window.draw_lidars = true; drawLidarsSwitch.addEventListener('input', () => { store.dispatch('toggleSwitch', {name: 'drawLidars', value: drawLidarsSwitch.checked}); }); const drawNamesSwitch = document.querySelector("#drawNamesSwitch"); window.draw_names = true; drawNamesSwitch.addEventListener('input', () => { store.dispatch('toggleSwitch', {name: 'drawNames', value: drawNamesSwitch.checked}); }); const drawObservationSwitch = document.querySelector("#drawObservationSwitch"); drawObservationSwitch.addEventListener('input', () => { store.dispatch('toggleSwitch', {name: 'drawObservation', value: drawObservationSwitch.checked}); }); const drawRewardSwitch = document.querySelector("#drawRewardSwitch"); drawRewardSwitch.addEventListener('input', () => { store.dispatch('toggleSwitch', {name: 'drawReward', value: drawRewardSwitch.checked}); }); const circleAssetButton = document.querySelector('#circleAssetButton'); circleAssetButton.addEventListener('click', () => { store.dispatch('drawAsset', {name: 'circle', value: !store.state.advancedOptionsState.assets.circle}); }); const advancedOptionsInstance = new AdvancedOptions(); advancedOptionsInstance.render(); // Language set up const langSelect = document.querySelector("#langSelect"); langSelect.addEventListener('input', () => { store.dispatch('setLanguage', langSelect.value); }); // About tab set up const aboutTabInstance = new AboutTab(); aboutTabInstance.render(); // Global elements set up const globalElementsInstance = new GlobalElements(); globalElementsInstance.render(); /* * Fetches the available morphologies and policies from the JSON file */ fetch('https://huggingface.co/api/models?filter=teach-my-agent-parkour') .then(resp => resp.text().then(body => { return JSON.parse(body); })) .then(models => { let morphologies = {} let promises = [] models.forEach(model => { promises.push( fetch('https://huggingface.co/'+model.id+'/raw/main/ta-config.json') .then(result => result.text().then(body => { let policy = JSON.parse(body); if(!(policy["morphology"] in morphologies)){ morphologies[policy["morphology"]] = { "morphology": policy["morphology"], "seeds": [] } } morphologies[policy["morphology"]]["seeds"].push({ "seed": policy["seed"], "name": policy["name"], "path": 'https://huggingface.co/'+model.id+'/resolve/main' }) })) ); }); Promise.all(promises).then(() => { for (const [key, value] of Object.entries(morphologies)) { store.dispatch('addMorphology', { morphology: value["morphology"], seeds: value["seeds"].map((seed, index) => { seed["idx"] = index; return seed; }) }); } }); }); /* * Fetches the base environments from the JSON file. * The JSON file must contain a list of the names of all the files contained in the 'base_envs_set' folder. */ fetch('./base_envs_set.json') .then(resp => resp.text().then(body => { return JSON.parse(body); })) .then(data => data['filenames'].forEach(filename => { // Fetches each JSON env file and parses them to get the corresponding environment fetch('./base_envs_set/' + filename) .then(resp => resp.text().then(body => { let env = JSON.parse(body); // Adds the environment contained in the JSON file to the base set store.dispatch('addEnv',{set: 'base', env: env}); })) })); /* Utility functions that require access to 'store' */ /** * Wrapper for init_game() with default parameters. */ window.init_default = () => { store.dispatch('init_default', {}); } /** * Indicates whether the drawing is active. * @returns {boolean} */ window.is_drawing = () => { return store.state.drawingModeState.drawing; } /** * Indicates whether the drawing_ground is active. * @returns {boolean} */ window.is_drawing_ground = () => { return store.state.drawingModeState.drawing_ground; } /** * Indicates whether the drawing_ceiling is active. * @returns {boolean} */ window.is_drawing_ceiling = () => { return store.state.drawingModeState.drawing_ceiling; } /** * Indicates whether the erasing is active. * @returns {boolean} */ window.is_erasing = () => { return store.state.drawingModeState.erasing; } /** * Indicates whether the drawing_circle is active. * @returns {boolean} */ window.is_drawing_circle = () => { return store.state.advancedOptionsState.assets.circle; } /** * Generates the terrain from the drawing (true) or vice-versa (false). * @param payload {boolean} */ window.generateTerrain = (payload) => { store.dispatch('generateTerrain', payload); } /** * Re-draws the drawing canvas. */ window.refresh_drawing = () => { store.dispatch('refreshDrawing', {}); } /** * Loads the default environment (Flat Parkour). */ window.loadDefaultEnv = () => { let defaultEnv = store.state.envsSets.baseEnvsSet.find(env => env.description["EN"].name.split(" ")[0] == "Flat"); store.dispatch('loadEnv', defaultEnv != null ? defaultEnv : store.state.envsSets.baseEnvsSet[0]); } /** * Handles mouse clicks outside the canvas. */ window.deselectDrawingButtons = () => { store.dispatch('deselectDrawingButtons', {}); } /** * Handles agent selection according to the given index. * @param index {number} - Index of the agent to select in the list of agents */ window.set_agent_selected = (index) => { store.dispatch('selectAgent', {index: index}); } /** * Handles agent following according to the given index. * @param index {number} - Index of the agent to select in the list of agents */ window.set_agent_followed = (index) => { store.dispatch('followAgent', {index: index}); } /** * Handles agent deletion. * @param agent {Object} - Agent to delete */ window.delete_agent = (agent) => { store.dispatch('deleteAgent', {index: window.game.env.agents.indexOf(agent)}); } /** * Returns the current language. * @return {string} */ window.get_language = () => { return store.state.language; } /** * Sets up and starts language selection with IntroJs. */ window.langIntroSetUp = () => { window.langIntro = introJs(); // Called when langIntro is exited: sets up and starts the guide tour window.langIntro.onexit(function (){ window.introTourSetUp(); }); window.langIntro.setOptions({ exitOnOverlayClick: false, showBullets: false, doneLabel: "Ok", steps: [ { title: "Choose a language:", intro: `` }, ] }); // Starts the language selection intro window.langIntro.start(); // Sets the language on select input const langSelectIntro = document.querySelector('#langSelectIntro'); langSelectIntro.addEventListener('input', () => { store.dispatch('setLanguage', langSelectIntro.value); }); } /** * Sets up guide tour with IntroJs. */ window.introTourSetUp = () => { store.dispatch('setIntroTour', true); // Guide tour and hints manager window.introTour = introJs(); // Called when the tour is exited: enables save env button and show the hints window.introTour.onexit(function (){ store.dispatch('setIntroTour', false); }); // Shows hints when a hint is closed so that they are always available window.introTour.onhintclose(function (){ window.introTour.showHints(); }); let tour_dict = window.lang_dict[store.state.language]['introTour']; // Sets up the intro options window.introTour.setOptions({ hidePrev: true, exitOnOverlayClick: false, nextLabel: tour_dict['nextLabel'], prevLabel: tour_dict['prevLabel'], doneLabel: tour_dict['doneLabel'], // Steps of the guide tour steps: [ { title: tour_dict['welcomeTitle'], intro: tour_dict['welcomeText'], disableInteraction: true, }, { title: tour_dict['viewportTitle'], intro: tour_dict['viewportText'], element: document.querySelector('#canvas_container'), disableInteraction: false, position: "bottom" }, { title: tour_dict['runTitle'], intro: tour_dict['runText'], element: document.querySelector('#canvas-and-main-buttons'), disableInteraction: false }, { title: tour_dict['baseEnvsTitle'], intro: tour_dict['baseEnvsText'], element: document.querySelector('#baseEnvsSet'), disableInteraction: true, }, { title: tour_dict['morphologiesTitle'], intro: tour_dict['morphologiesText'], element: document.querySelector('#agents-selection'), disableInteraction: true, }, { title: tour_dict['agentsListTitle'], intro: tour_dict['agentsListText'], element: document.querySelector('#agents_list_container'), disableInteraction: true, }, { title: tour_dict['customEnvsTitle'], intro: tour_dict['customEnvsText'], element: document.querySelector('#customSetSection'), disableInteraction: true, }, { title: tour_dict['furtherTitle'], intro: tour_dict['furtherText'], element: document.querySelector('#tabs-buttons'), disableInteraction: true, } ], // Hints buttons hintButtonLabel: window.lang_dict[store.state.language]['introHints']['buttonLabel'], hints: [ { hint: window.lang_dict[store.state.language]['introHints']['tips'], element: document.querySelector('#canvas_container'), hintPosition: 'top-right', } ] }); // Starts the guide tour window.introTour.start(); }