Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions WebVis/src/GUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down Expand Up @@ -257,26 +257,27 @@ 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);
}
});

// 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 };
Expand Down Expand Up @@ -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];
Expand Down
10 changes: 9 additions & 1 deletion WebVis/src/Scenario.js
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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);
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug console.log statement should be removed before merging to production. This statement adds unnecessary noise to the console output and can impact performance in production environments.

Suggested change
console.log("Stored scenario content, length:", rawString ? rawString.length : 0);

Copilot uses AI. Check for mistakes.

// remove '\r' characters
rawString = rawString.replace(/\r/g, '');
Comment on lines +28 to 33
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scenario content is stored before it's processed and cleaned (the '\r' characters are removed later). This means the exported scenario will contain '\r' characters if the original file had them, which could cause inconsistencies. Consider storing the scenario content after the rawString.replace operation to ensure the exported file matches the processed format.

Suggested change
// 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, '');
// remove '\r' characters
rawString = rawString.replace(/\r/g, '');
// Store the scenario content for export
pathfinderData.currentScenario = rawString;
console.log("Stored scenario content, length:", rawString ? rawString.length : 0);

Copilot uses AI. Check for mistakes.
Expand All @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions WebVis/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing the entire raw scenario content in memory (pathfinderData.currentScenario) could lead to memory issues for large scenario files. Consider if this is necessary or if you could store just the parsed/cleaned version, or implement a size check before storing.

Suggested change
currentScenario: null, // Stores the currently loaded scenario content
// Stores the currently loaded scenario content (parsed/cleaned version only)
_currentScenario: null,
setCurrentScenario: function(scenarioContent) {
// Only allow storing if under 5MB (5 * 1024 * 1024 bytes)
const MAX_SCENARIO_SIZE = 5 * 1024 * 1024;
if (typeof scenarioContent === "string" && scenarioContent.length > MAX_SCENARIO_SIZE) {
console.warn("Scenario content too large to store in memory. Max allowed is 5MB.");
this._currentScenario = null;
return false;
}
// If scenarioContent is a parsed object, store directly
this._currentScenario = scenarioContent;
return true;
},
get currentScenario() {
return this._currentScenario;
},

Copilot uses AI. Check for mistakes.
currentScenarioName: null, // Stores the name of the currently loaded scenario
settings: {
name: "WebPathfinder-Out",
description: "Output produced by a valid Pathfinder run.",
Expand Down Expand Up @@ -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);
Comment on lines +272 to +273
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug console.log statements should be removed before merging to production. These statements add unnecessary noise to the console output and can impact performance in production environments.

Suggested change
console.log("Download scenario called - currentScenario:", pathfinderData.currentScenario ? "exists" : "null");
console.log("Download scenario called - scen_out:", pathfinderData.scen_out);

Copilot uses AI. Check for mistakes.

if (!scenarioContent || scenarioContent === 'INVALID SCENE') {
console.warn("No valid scenario to download. Please load a scenario first.");
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message uses console.warn but doesn't provide user-facing feedback. Users who click the "Download Scenario" button when no scenario is loaded won't see any visible indication of why nothing happened. Consider adding a visible alert or notification to inform the user.

Suggested change
console.warn("No valid scenario to download. Please load a scenario first.");
console.warn("No valid scenario to download. Please load a scenario first.");
window.alert("No valid scenario to download. Please load a scenario first.");

Copilot uses AI. Check for mistakes.
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);
}