From 6cd590e9b78cf424382d969eb772774e206c364e Mon Sep 17 00:00:00 2001 From: Soham Samanta Date: Fri, 12 Dec 2025 15:45:44 -0500 Subject: [PATCH 1/4] new export :) --- WebVis/src/GUI.js | 31 ++++++++++++++++++++----------- WebVis/src/Scenario.js | 10 +++++++++- WebVis/src/utils.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/WebVis/src/GUI.js b/WebVis/src/GUI.js index d355b8f..786b79d 100644 --- a/WebVis/src/GUI.js +++ b/WebVis/src/GUI.js @@ -4,7 +4,7 @@ import { Scenario } from './Scenario.js'; import { gScene, gLights, gRenderer, gModules, gReferenceModule, gModulePositions, gCanvas, gHighlightModule } from './main.js'; import { moduleBrush, pathfinderData, WorkerType, MessageType, ContentType, VisConfigData, ModuleType, getModuleAtPosition } from './utils.js'; import { CameraType } from "./utils.js"; -import { saveConfiguration, downloadConfiguration } from './utils.js'; +import { saveConfiguration, downloadConfiguration, downloadScenario } from './utils.js'; import { Module } from './Module.js'; // Exact filenames of example scenarios in /Scenarios/ @@ -257,17 +257,17 @@ window._pathfinderRun = function() { /* GUI setup */ /* ****************************** */ // GUI elements for general settings -export const gGraphicsGui = new GUI( { title: "Graphics",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ).close(); +export const gGraphicsGui = new GUI( { title: "Graphics",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ).close(); let style_controller; // GUI elements for Visualizer Mode -export const gAnimGui = new GUI( { title: "Animation",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ); -export const gScenGui = new GUI( { title: "Scenario",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ).close(); +export const gAnimGui = new GUI( { title: "Animation",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ); +export const gScenGui = new GUI( { title: "Scenario",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ).close(); // GUI elements for Configurizer Mode -export const gModuleBrushGui = new GUI( { title: "Brush",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ).hide(); +export const gModuleBrushGui = new GUI( { title: "Brush",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ).hide(); let brushColor_selector; -export const gLayerGui = new GUI( { title: "Layer",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ).hide(); -export const gSelectedModuleGui = new GUI( { title: "Selected Module",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ).hide(); +export const gLayerGui = new GUI( { title: "Layer",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ).hide(); +export const gSelectedModuleGui = new GUI( { title: "Selected Module",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ).hide(); export const zSliceController = gLayerGui.add(moduleBrush, 'zSlice', VisConfigData.bounds.z.min - 2, VisConfigData.bounds.z.max + 2, 1).name("Layer").onChange((value) => { if (window._isPainterModeActive) { updateVisibleModules(value); @@ -275,8 +275,9 @@ export const zSliceController = gLayerGui.add(moduleBrush, 'zSlice', VisConfigDa }); // GUI element for Pathfinder and developer options -export const gPathfinderGui = new GUI( { title: "Pathfinder",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ).close(); -export const gModeGui = new GUI( { title: "View/Edit",width: window.innerWidth*.1, container: document.getElementById("controlBar") } ); +export const gPathfinderGui = new GUI( { title: "Pathfinder",width: window.innerWidth*.12, container: document.getElementById("controlBar") } ).close(); +export const gExportGui = new GUI( { title: "Export",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ); +export const gModeGui = new GUI( { title: "View/Edit",width: window.innerWidth*.12, container: document.getElementById("controlBar") } ); // Global variables for module selection let selectedModule = null; const selectedModuleColor = { color: 0x808080 }; @@ -451,18 +452,26 @@ document.addEventListener("DOMContentLoaded", async function () { } } }, 'loadFinal').name("Load Final Config"); - gPathfinderGui.add({ + + // Export Controls + gExportGui.add({ downloadInitial: function() { downloadConfiguration(true); } }, 'downloadInitial').name("Download Initial"); - gPathfinderGui.add({ + gExportGui.add({ downloadFinal: function() { downloadConfiguration(false); } }, 'downloadFinal').name("Download Final"); + gExportGui.add({ + downloadScenario: function() { + downloadScenario(); + } + }, 'downloadScenario').name("Download Scenario"); + const _folder = gScenGui.addFolder("Example Scenarios"); for (let i in EXAMPLE_SCENARIOS) { let ex = EXAMPLE_SCENARIOS[i]; diff --git a/WebVis/src/Scenario.js b/WebVis/src/Scenario.js index f0f0dd3..2f6585a 100644 --- a/WebVis/src/Scenario.js +++ b/WebVis/src/Scenario.js @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import {CameraType, ModuleType, MoveType, VisConfigData} from "./utils.js"; +import {CameraType, ModuleType, MoveType, VisConfigData, pathfinderData} from "./utils.js"; import { Module } from "./Module.js"; import { Move } from "./Move.js"; import { MoveSet } from "./MoveSet.js"; @@ -24,6 +24,10 @@ export class Scenario { // Reset Data VisConfigData.nextModID = 0; VisConfigData.clearBounds(); + + // Store the scenario content for export + pathfinderData.currentScenario = rawString; + console.log("Stored scenario content, length:", rawString ? rawString.length : 0); // remove '\r' characters rawString = rawString.replace(/\r/g, ''); @@ -37,6 +41,10 @@ export class Scenario { let metadataLines = metadataString.split('\n'); let scenarioName = metadataLines[0]; let scenarioDescription = metadataLines[1]; + + // Store the scenario name for export + pathfinderData.currentScenarioName = scenarioName; + let scenarioModuleType; switch (metadataLines[2]) { case 'CUBE': scenarioModuleType = ModuleType.CUBE; break; diff --git a/WebVis/src/utils.js b/WebVis/src/utils.js index 41fa454..e9eec9d 100644 --- a/WebVis/src/utils.js +++ b/WebVis/src/utils.js @@ -85,6 +85,8 @@ export let pathfinderData = { config_i: '{"exists": false}', config_f: '{"exists": false}', scen_out: 'INVALID SCENE', + currentScenario: null, // Stores the currently loaded scenario content + currentScenarioName: null, // Stores the name of the currently loaded scenario settings: { name: "WebPathfinder-Out", description: "Output produced by a valid Pathfinder run.", @@ -261,3 +263,32 @@ export function downloadConfiguration(isInitial = true) { // Clean up URL.revokeObjectURL(url); } + +// Function to download scenario file +export function downloadScenario() { + // Try to export current scenario first, then pathfinder output + let scenarioContent = pathfinderData.currentScenario || pathfinderData.scen_out; + + console.log("Download scenario called - currentScenario:", pathfinderData.currentScenario ? "exists" : "null"); + console.log("Download scenario called - scen_out:", pathfinderData.scen_out); + + if (!scenarioContent || scenarioContent === 'INVALID SCENE') { + console.warn("No valid scenario to download. Please load a scenario first."); + return; + } + + // Use current scenario name if available, otherwise use settings name + const scenarioName = (pathfinderData.currentScenarioName || pathfinderData.settings.name) + ".scen"; + + // Create blob and download link + const blob = new Blob([scenarioContent], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = url; + link.download = scenarioName; + link.click(); + + // Clean up + URL.revokeObjectURL(url); +} From 9b8aa6f492d0b696a77d143da6c03b53f5b4a4bc Mon Sep 17 00:00:00 2001 From: Soham Samanta Date: Mon, 26 Jan 2026 14:10:04 -0500 Subject: [PATCH 2/4] import export --- WebVis/index.html | 9 +- WebVis/src/GUI.js | 237 ++++++++++++++++++++++++++++++++------------ WebVis/src/utils.js | 72 ++++++++++++-- 3 files changed, 246 insertions(+), 72 deletions(-) diff --git a/WebVis/index.html b/WebVis/index.html index cd10937..17466ba 100644 --- a/WebVis/index.html +++ b/WebVis/index.html @@ -33,10 +33,13 @@ } -
+
-
- +
+ + + +
diff --git a/WebVis/src/GUI.js b/WebVis/src/GUI.js index 786b79d..67636d6 100644 --- a/WebVis/src/GUI.js +++ b/WebVis/src/GUI.js @@ -1,10 +1,10 @@ import * as THREE from 'three'; import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; import { Scenario } from './Scenario.js'; -import { gScene, gLights, gRenderer, gModules, gReferenceModule, gModulePositions, gCanvas, gHighlightModule } from './main.js'; +import { gScene, gLights, gRenderer, gModules, gReferenceModule, gModulePositions, gCanvas, gHighlightModule, cancelActiveMove } from './main.js'; import { moduleBrush, pathfinderData, WorkerType, MessageType, ContentType, VisConfigData, ModuleType, getModuleAtPosition } from './utils.js'; import { CameraType } from "./utils.js"; -import { saveConfiguration, downloadConfiguration, downloadScenario } from './utils.js'; +import { saveConfiguration, downloadConfiguration, downloadScenario, downloadCurrentConfiguration, parseConfigurationJSON } from './utils.js'; import { Module } from './Module.js'; // Exact filenames of example scenarios in /Scenarios/ @@ -66,6 +66,88 @@ const SliderType = Object.freeze({ QUADRATIC: 1 }); +/** + * Load a configuration from JSON and display it in the scene + * @param {string} configJSON - The JSON string of the configuration + * @returns {boolean} - True if successful, false otherwise + */ +function loadConfigurationFromJSON(configJSON) { + const configData = parseConfigurationJSON(configJSON); + + if (!configData) { + console.error("Failed to parse configuration JSON"); + return false; + } + + // Clear existing modules + for (let module in gModules) { + gModules[module].destroy(); + } + cancelActiveMove(); + + // Reset Data + VisConfigData.nextModID = 0; + VisConfigData.clearBounds(); + + // Invalidate move sequence + window.gwMoveSetSequence.invalidate(); + + // Create modules from configuration + configData.modules.forEach((moduleData, index) => { + const pos = moduleData.position; + // Handle 2D positions (add z=0) or 3D positions + const position = new THREE.Vector3( + pos[0], + pos[1], + pos.length === 3 ? pos[2] : 0 + ); + + // Parse color - can be array [r,g,b] or hex number + let colorHex; + const colorData = moduleData.properties?.colorProperty?.color; + if (Array.isArray(colorData)) { + // Convert RGB array to hex + const r = colorData[0]; + const g = colorData[1]; + const b = colorData[2]; + colorHex = (r << 16) | (g << 8) | b; + } else if (typeof colorData === 'number') { + colorHex = colorData; + } else { + colorHex = 0xFFFFFF; // Default white + } + + // Create the module + const module = new Module(configData.moduleType, index, position, colorHex, 0.9); + + // Set static property if specified + if (moduleData.static) { + module.markStatic(); + } + }); + + // Position camera + const centroid = VisConfigData.getCentroid(); + const radius = VisConfigData.getRadius(); + + gwUser.camera.position.x = centroid.x; + gwUser.camera.position.y = centroid.y; + gwUser.camera.position.z = centroid.z + radius + 3.0; + gwUser.controls.target.set(centroid.x, centroid.y, centroid.z); + + gwUser.miniCamera.position.x = centroid.x; + gwUser.miniCamera.position.y = centroid.y; + gwUser.miniCamera.position.z = centroid.z + radius + 3.0; + gwUser.miniControls.target.set(centroid.x, centroid.y, centroid.z); + + // Update reference module to match the loaded configuration type + gReferenceModule.swapType(configData.moduleType); + gHighlightModule.swapType(configData.moduleType); + + console.log(`Loaded configuration "${configData.name}" with ${configData.modules.length} modules`); + return true; +} + class GuiGlobalsHelper { constructor(prop, defaultVal, sliderType = SliderType.LINEAR) { this.prop = prop; @@ -276,7 +358,7 @@ export const zSliceController = gLayerGui.add(moduleBrush, 'zSlice', VisConfigDa // GUI element for Pathfinder and developer options export const gPathfinderGui = new GUI( { title: "Pathfinder",width: window.innerWidth*.12, container: document.getElementById("controlBar") } ).close(); -export const gExportGui = new GUI( { title: "Export",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ); +export const gExportGui = new GUI( { title: "Import/Export",width: window.innerWidth*.08, container: document.getElementById("controlBar") } ); export const gModeGui = new GUI( { title: "View/Edit",width: window.innerWidth*.12, container: document.getElementById("controlBar") } ); // Global variables for module selection let selectedModule = null; @@ -393,66 +475,6 @@ document.addEventListener("DOMContentLoaded", async function () { } }, 'saveFinal').name("Save Final Config"); - gPathfinderGui.add({ - loadInitial: function() { - if (window.Worker) { - if (config2ScenWorker != null) { - config2ScenWorker.terminate(); - } - config2ScenWorker = new Worker("src/PathfinderWorker.js"); - config2ScenWorker.postMessage([WorkerType.CONFIG2SCEN, pathfinderData.config_i]); - config2ScenWorker.onmessage = (msg) => { - switch (msg.data[0]) { - case MessageType.ERROR: - console.log("config2Scen task encountered an error."); - config2ScenWorker.terminate(); - break; - case MessageType.RESULT: - config2ScenWorker.terminate(); - new Scenario(msg.data[1]); - break; - case MessageType.DATA: - // Currently unused for config2Scen - console.log(msg.data[1]); - } - } - console.log("Started config2Scen task"); - } else { - console.log("Browser does not support web workers."); - } - } - }, 'loadInitial').name("Load Initial Config"); - - gPathfinderGui.add({ - loadFinal: function() { - if (window.Worker) { - if (config2ScenWorker != null) { - config2ScenWorker.terminate(); - } - config2ScenWorker = new Worker("src/PathfinderWorker.js"); - config2ScenWorker.postMessage([WorkerType.CONFIG2SCEN, pathfinderData.config_f]); - config2ScenWorker.onmessage = (msg) => { - switch (msg.data[0]) { - case MessageType.ERROR: - console.log("config2Scen task encountered an error."); - config2ScenWorker.terminate(); - break; - case MessageType.RESULT: - config2ScenWorker.terminate(); - new Scenario(msg.data[1]); - break; - case MessageType.DATA: - // Currently unused for config2Scen - console.log(msg.data[1]); - } - } - console.log("Started config2Scen task"); - } else { - console.log("Browser does not support web workers."); - } - } - }, 'loadFinal').name("Load Final Config"); - // Export Controls gExportGui.add({ downloadInitial: function() { @@ -466,12 +488,30 @@ document.addEventListener("DOMContentLoaded", async function () { } }, 'downloadFinal').name("Download Final"); + gExportGui.add({ + importInitial: function() { + document.getElementById("initialConfigUploadButton").click(); + } + }, 'importInitial').name("Import Initial"); + + gExportGui.add({ + importFinal: function() { + document.getElementById("finalConfigUploadButton").click(); + } + }, 'importFinal').name("Import Final"); + gExportGui.add({ downloadScenario: function() { downloadScenario(); } }, 'downloadScenario').name("Download Scenario"); + gExportGui.add({ + downloadCurrent: function() { + downloadCurrentConfiguration(); + } + }, 'downloadCurrent').name("Download Current"); + const _folder = gScenGui.addFolder("Example Scenarios"); for (let i in EXAMPLE_SCENARIOS) { let ex = EXAMPLE_SCENARIOS[i]; @@ -488,6 +528,77 @@ document.addEventListener("DOMContentLoaded", async function () { selectedModule.mesh.material.uniforms.diffuse.value.setFromColor(new THREE.Color(value)); } }); + + // Add event listeners for configuration file uploads + const initialConfigUploadElement = document.getElementById("initialConfigUploadButton"); + initialConfigUploadElement.onchange = (e) => { + const file = initialConfigUploadElement.files[0]; + if (!file) return; + const reader = new FileReader(); + reader.onload = (e) => { + const jsonContent = e.target.result; + try { + // Store in pathfinder data for potential pathfinder use + pathfinderData.config_i = jsonContent; + console.log("Initial configuration loaded from file"); + + // Load and display the configuration directly + const success = loadConfigurationFromJSON(jsonContent); + if (success) { + // Enable pathfinder if both configs are loaded + if (!pathfinderData.is_running && JSON.parse(pathfinderData.config_f).exists) { + pathfinder_controller.enable(); + } + } else { + alert("Failed to load configuration. Please check the console for errors."); + } + } catch (error) { + console.error("Invalid JSON file:", error); + alert("Invalid JSON file. Please select a valid configuration file."); + } + } + reader.onerror = (e) => { + console.error("Error reading file:", e.target.error); + } + reader.readAsText(file); + // Reset the input so the same file can be selected again + initialConfigUploadElement.value = ''; + } + + const finalConfigUploadElement = document.getElementById("finalConfigUploadButton"); + finalConfigUploadElement.onchange = (e) => { + const file = finalConfigUploadElement.files[0]; + if (!file) return; + const reader = new FileReader(); + reader.onload = (e) => { + const jsonContent = e.target.result; + try { + // Store in pathfinder data for potential pathfinder use + pathfinderData.config_f = jsonContent; + console.log("Final configuration loaded from file"); + + // Load and display the configuration directly + const success = loadConfigurationFromJSON(jsonContent); + if (success) { + // Enable pathfinder if both configs are loaded + if (!pathfinderData.is_running && JSON.parse(pathfinderData.config_i).exists) { + pathfinder_controller.enable(); + } + } else { + alert("Failed to load configuration. Please check the console for errors."); + } + } catch (error) { + console.error("Invalid JSON file:", error); + alert("Invalid JSON file. Please select a valid configuration file."); + } + } + reader.onerror = (e) => { + console.error("Error reading file:", e.target.error); + } + reader.readAsText(file); + // Reset the input so the same file can be selected again + finalConfigUploadElement.value = ''; + } }); /** diff --git a/WebVis/src/utils.js b/WebVis/src/utils.js index e9eec9d..4617814 100644 --- a/WebVis/src/utils.js +++ b/WebVis/src/utils.js @@ -268,27 +268,87 @@ export function downloadConfiguration(isInitial = true) { export function downloadScenario() { // Try to export current scenario first, then pathfinder output let scenarioContent = pathfinderData.currentScenario || pathfinderData.scen_out; - + console.log("Download scenario called - currentScenario:", pathfinderData.currentScenario ? "exists" : "null"); console.log("Download scenario called - scen_out:", pathfinderData.scen_out); - + if (!scenarioContent || scenarioContent === 'INVALID SCENE') { console.warn("No valid scenario to download. Please load a scenario first."); return; } - + // Use current scenario name if available, otherwise use settings name const scenarioName = (pathfinderData.currentScenarioName || pathfinderData.settings.name) + ".scen"; - + // Create blob and download link const blob = new Blob([scenarioContent], { type: "text/plain" }); const url = URL.createObjectURL(blob); - + const link = document.createElement("a"); link.href = url; link.download = scenarioName; link.click(); - + // Clean up URL.revokeObjectURL(url); } + +// Function to download current configuration state as JSON +export function downloadCurrentConfiguration() { + const config = createPathfinderConfiguration(); + + if (!config || !config.exists) { + console.warn("No configuration to download. Please create a configuration first."); + return; + } + + const configJSON = JSON.stringify(config, null, 2); + const configName = pathfinderData.settings.name + "_current.json"; + + // Create blob and download link + const blob = new Blob([configJSON], { type: "application/json" }); + const url = URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = url; + link.download = configName; + link.click(); + + // Clean up + URL.revokeObjectURL(url); +} + +// Function to parse configuration JSON and return structured data +// This is used by loadConfigurationFromJSON in GUI.js +export function parseConfigurationJSON(configJSON) { + try { + const config = JSON.parse(configJSON); + + if (!config.exists) { + console.warn("Configuration does not exist"); + return null; + } + + // Determine module type + let moduleType; + switch (config.moduleType) { + case 'CUBE': moduleType = ModuleType.CUBE; break; + case 'RHOMBIC_DODECAHEDRON': moduleType = ModuleType.RHOMBIC_DODECAHEDRON; break; + case 'CATOM': moduleType = ModuleType.CATOM; break; + default: + console.log("Unknown module type ", config.moduleType, " -- defaulting to CUBE"); + moduleType = ModuleType.CUBE; + break; + } + + return { + moduleType: moduleType, + modules: config.modules, + name: config.name, + description: config.description + }; + } catch (error) { + console.error("Error parsing configuration:", error); + return null; + } +} From f577191e4a7084eb7de32a89aec0b5c4aa07bf52 Mon Sep 17 00:00:00 2001 From: Soham Samanta Date: Fri, 13 Feb 2026 09:32:04 -0500 Subject: [PATCH 3/4] fixed --- .DS_Store | Bin 0 -> 8196 bytes WebVis/index.html | 8 +-- WebVis/src/GUI.js | 166 ++++++++++++++++++++++------------------------ 3 files changed, 80 insertions(+), 94 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8ac160442b9560e8e7026278daf5c814ad78e86d GIT binary patch literal 8196 zcmeHM&1(}u6o1nsHuzC0UW$n9$&*!!SBq;{Pf`eJX|K&kZBmkMXqp~8w3q4W8lAJK<*D7E-8H@ds?ck1Dz@XAhQ@&4foUX2GTRqH?pTCX3&H-h190Plo&#r zW85=(zL7mGwK)k>dLh$EUGErh3=A_MYxgW&p>=A|wpG6$6c+eg zD-424Gpt}N|Ls@x#rKCVZ`t#;vGeUZ`!;a63R9Zw`=vT{sbYDjb8m(nswlQ=*0ZM5&yLdP7;hX#7{~hHY^Jrb<0#5FP9i7D$cQ@KMGTwBnD|?h zy*i(y$8o>jIOyl?$Td0MqjW8tIm7poeAYlZOC@y|BDc5jX2mmkBEO>e9;453-zn_( zaZh32Nc6$+r?s)-$oPOoXJ}dFN*Am*ad%I#-@v=MX=TmL{KfIwJcf<8pZhV4S)WIc z&cec9lJMySt$?G7j>1Ab|LS~vo@2bw2}(cod1I}?Q6=eEIDECgDKM_1jOJiQ*8jJY zzyCX?90QJlf0zN04Hkm}0u%{B%39mQwTeqNsn^p|nV?hUIHW4aA)`MGaqmG@+W1EH kw8RM7U;hvwPic4lN3~kI)105Q=`5^s*PZ_=*N;>88?*r>u>b%7 literal 0 HcmV?d00001 diff --git a/WebVis/index.html b/WebVis/index.html index 17466ba..2094230 100644 --- a/WebVis/index.html +++ b/WebVis/index.html @@ -35,12 +35,8 @@
-
- - - - -
+ +
diff --git a/WebVis/src/GUI.js b/WebVis/src/GUI.js index 67636d6..e7d969a 100644 --- a/WebVis/src/GUI.js +++ b/WebVis/src/GUI.js @@ -301,10 +301,19 @@ window._pathfinderRun = function() { pathfinder_controller.enable(); pathfinderData.scen_out = msg.data[1]; pathfinderWorker.terminate(); - // TODO: provide option to delay loading found path instead of always instantly loading - new Scenario(pathfinderData.scen_out); - pathfinderProgressBar.style.backgroundColor = "rgba(255, 255, 255, 0.5)"; - pathfinderProgressBar.style.width = "100%"; + + // Check if the result is valid before trying to load it + if (!pathfinderData.scen_out || pathfinderData.scen_out.length === 0 || pathfinderData.scen_out.trim() === '') { + console.error("Pathfinder returned empty result - no path found"); + alert("Pathfinder could not find a path between the initial and final configurations.\n\nPossible reasons:\n1. Configurations are not connected (modules too far apart)\n2. Wrong move set selected for the module type\n3. Configurations have different number of modules\n4. Path requires too many moves (timeout)\n\nCheck the console for more details."); + pathfinderProgressBar.style.backgroundColor = "rgba(255, 0, 0, 0.5)"; + pathfinderProgressBar.style.width = "100%"; + } else { + // Valid result, load the scenario + new Scenario(pathfinderData.scen_out); + pathfinderProgressBar.style.backgroundColor = "rgba(0, 255, 0, 0.5)"; + pathfinderProgressBar.style.width = "100%"; + } pathfinderReverseProgressBar.style.width = "0%"; break; case MessageType.DATA: @@ -454,8 +463,32 @@ document.addEventListener("DOMContentLoaded", async function () { } }); - // Create configuration button controls using object literals - gPathfinderGui.add({ + // Import/Export Controls + gExportGui.add({ + import: function() { + document.getElementById("configUploadButton").click(); + } + }, 'import').name("Import"); + + gExportGui.add({ + downloadCurrent: function() { + downloadCurrentConfiguration(); + } + }, 'downloadCurrent').name("Download Current"); + + gExportGui.add({ + importScenario: function() { + document.getElementById("scenarioUploadButton").click(); + } + }, 'importScenario').name("Import Scenario"); + + gExportGui.add({ + downloadScenario: function() { + downloadScenario(); + } + }, 'downloadScenario').name("Download Scenario"); + + gExportGui.add({ saveInitial: function() { saveConfiguration(true); console.log("Initial configuration saved"); @@ -465,7 +498,7 @@ document.addEventListener("DOMContentLoaded", async function () { } }, 'saveInitial').name("Save Initial Config"); - gPathfinderGui.add({ + gExportGui.add({ saveFinal: function() { saveConfiguration(false); console.log("Final configuration saved"); @@ -475,42 +508,43 @@ document.addEventListener("DOMContentLoaded", async function () { } }, 'saveFinal').name("Save Final Config"); - // Export Controls - gExportGui.add({ - downloadInitial: function() { - downloadConfiguration(true); - } - }, 'downloadInitial').name("Download Initial"); - - gExportGui.add({ - downloadFinal: function() { - downloadConfiguration(false); - } - }, 'downloadFinal').name("Download Final"); - gExportGui.add({ - importInitial: function() { - document.getElementById("initialConfigUploadButton").click(); - } - }, 'importInitial').name("Import Initial"); - - gExportGui.add({ - importFinal: function() { - document.getElementById("finalConfigUploadButton").click(); - } - }, 'importFinal').name("Import Final"); - - gExportGui.add({ - downloadScenario: function() { - downloadScenario(); + viewInitial: function() { + try { + const config = JSON.parse(pathfinderData.config_i); + if (!config.exists) { + alert("No initial configuration saved. Please save an initial configuration first."); + return; + } + const success = loadConfigurationFromJSON(pathfinderData.config_i); + if (!success) { + alert("Failed to load initial configuration. Please check the console for errors."); + } + } catch (error) { + console.error("Error loading initial configuration:", error); + alert("No initial configuration saved or invalid configuration."); + } } - }, 'downloadScenario').name("Download Scenario"); + }, 'viewInitial').name("View Initial Config"); gExportGui.add({ - downloadCurrent: function() { - downloadCurrentConfiguration(); + viewFinal: function() { + try { + const config = JSON.parse(pathfinderData.config_f); + if (!config.exists) { + alert("No final configuration saved. Please save a final configuration first."); + return; + } + const success = loadConfigurationFromJSON(pathfinderData.config_f); + if (!success) { + alert("Failed to load final configuration. Please check the console for errors."); + } + } catch (error) { + console.error("Error loading final configuration:", error); + alert("No final configuration saved or invalid configuration."); + } } - }, 'downloadCurrent').name("Download Current"); + }, 'viewFinal').name("View Final Config"); const _folder = gScenGui.addFolder("Example Scenarios"); for (let i in EXAMPLE_SCENARIOS) { @@ -529,62 +563,18 @@ document.addEventListener("DOMContentLoaded", async function () { } }); - // Add event listeners for configuration file uploads - const initialConfigUploadElement = document.getElementById("initialConfigUploadButton"); - initialConfigUploadElement.onchange = (e) => { - const file = initialConfigUploadElement.files[0]; - if (!file) return; - const reader = new FileReader(); - reader.onload = (e) => { - const jsonContent = e.target.result; - try { - // Store in pathfinder data for potential pathfinder use - pathfinderData.config_i = jsonContent; - console.log("Initial configuration loaded from file"); - - // Load and display the configuration directly - const success = loadConfigurationFromJSON(jsonContent); - if (success) { - // Enable pathfinder if both configs are loaded - if (!pathfinderData.is_running && JSON.parse(pathfinderData.config_f).exists) { - pathfinder_controller.enable(); - } - } else { - alert("Failed to load configuration. Please check the console for errors."); - } - } catch (error) { - console.error("Invalid JSON file:", error); - alert("Invalid JSON file. Please select a valid configuration file."); - } - } - reader.onerror = (e) => { - console.error("Error reading file:", e.target.error); - } - reader.readAsText(file); - // Reset the input so the same file can be selected again - initialConfigUploadElement.value = ''; - } - - const finalConfigUploadElement = document.getElementById("finalConfigUploadButton"); - finalConfigUploadElement.onchange = (e) => { - const file = finalConfigUploadElement.files[0]; + // Add event listener for configuration file upload + const configUploadElement = document.getElementById("configUploadButton"); + configUploadElement.onchange = (e) => { + const file = configUploadElement.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { const jsonContent = e.target.result; try { - // Store in pathfinder data for potential pathfinder use - pathfinderData.config_f = jsonContent; - console.log("Final configuration loaded from file"); - // Load and display the configuration directly const success = loadConfigurationFromJSON(jsonContent); - if (success) { - // Enable pathfinder if both configs are loaded - if (!pathfinderData.is_running && JSON.parse(pathfinderData.config_i).exists) { - pathfinder_controller.enable(); - } - } else { + if (!success) { alert("Failed to load configuration. Please check the console for errors."); } } catch (error) { @@ -597,7 +587,7 @@ document.addEventListener("DOMContentLoaded", async function () { } reader.readAsText(file); // Reset the input so the same file can be selected again - finalConfigUploadElement.value = ''; + configUploadElement.value = ''; } }); From c1c6b83f906788d8054b007ac333625149b55f07 Mon Sep 17 00:00:00 2001 From: Soham Samanta Date: Fri, 13 Feb 2026 16:14:17 -0500 Subject: [PATCH 4/4] copilot --- WebVis/src/GUI.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/WebVis/src/GUI.js b/WebVis/src/GUI.js index e7d969a..a02b353 100644 --- a/WebVis/src/GUI.js +++ b/WebVis/src/GUI.js @@ -89,8 +89,11 @@ function loadConfigurationFromJSON(configJSON) { VisConfigData.nextModID = 0; VisConfigData.clearBounds(); - // Invalidate move sequence - window.gwMoveSetSequence.invalidate(); + // Reset and invalidate move sequence + if (window.gwMoveSetSequence) { + window.gwMoveSetSequence.reset(); + window.gwMoveSetSequence.invalidate(); + } // Create modules from configuration configData.modules.forEach((moduleData, index) => { @@ -130,15 +133,15 @@ function loadConfigurationFromJSON(configJSON) { const centroid = VisConfigData.getCentroid(); const radius = VisConfigData.getRadius(); - gwUser.camera.position.x = centroid.x; - gwUser.camera.position.y = centroid.y; - gwUser.camera.position.z = centroid.z + radius + 3.0; - gwUser.controls.target.set(centroid.x, centroid.y, centroid.z); + window.gwUser.camera.position.x = centroid.x; + window.gwUser.camera.position.y = centroid.y; + window.gwUser.camera.position.z = centroid.z + radius + 3.0; + window.gwUser.controls.target.set(centroid.x, centroid.y, centroid.z); - gwUser.miniCamera.position.x = centroid.x; - gwUser.miniCamera.position.y = centroid.y; - gwUser.miniCamera.position.z = centroid.z + radius + 3.0; - gwUser.miniControls.target.set(centroid.x, centroid.y, centroid.z); + window.gwUser.miniCamera.position.x = centroid.x; + window.gwUser.miniCamera.position.y = centroid.y; + window.gwUser.miniCamera.position.z = centroid.z + radius + 3.0; + window.gwUser.miniControls.target.set(centroid.x, centroid.y, centroid.z); // Update reference module to match the loaded configuration type gReferenceModule.swapType(configData.moduleType); @@ -588,7 +591,7 @@ document.addEventListener("DOMContentLoaded", async function () { reader.readAsText(file); // Reset the input so the same file can be selected again configUploadElement.value = ''; - } + }; }); /**