From c66da8740161538b721e477f5b4f5eb3bd8c9171 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Tue, 18 Nov 2025 13:20:18 -0600 Subject: [PATCH] Migrate from legacy config to new AppConfig singleton system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove dual config system and migrate all code to use appConfigInstance. **Phase 1: Update VR Controller Code** - snapAll.ts: Replace getAppConfig() with appConfigInstance.current - Use `rotateSnap > 0` instead of rotationSnapEnabled flag - Use `locationSnap > 0` instead of locationSnapEnabled flag - Remove parseFloat() calls (values already numbers) - groundMeshObserver.ts: Direct property replacement - flyModeEnabled → flyMode - snapTurnSnap → turnSnap (remove parseFloat) - customPhysics.ts: Add enabled checks and update - Add `> 0` checks (was applying unconditionally) - Use locationSnap and rotateSnap directly **Phase 2: Remove Legacy Config Bridge** - vrConfigPanel.ts: Remove syncLegacyConfig() method and all calls - configModal.tsx: Remove legacy localStorage 'config' writes **Phase 3: Cleanup** - appConfig.ts: Remove legacy code (ConfigType, getAppConfig(), setAppConfig()) - Remove unused log import **Benefits:** - Eliminates dual config system confusion - Fixes precision error from string "0" values - Single source of truth via appConfigInstance - Reactive updates via Observable pattern - Cleaner, simpler codebase 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- package.json | 2 +- src/controllers/functions/snapAll.ts | 16 ++++----- src/menus/vrConfigPanel.ts | 28 ---------------- src/react/pages/configModal.tsx | 14 -------- src/util/appConfig.ts | 42 ++---------------------- src/util/customPhysics.ts | 33 ++++++++++++------- src/util/functions/groundMeshObserver.ts | 8 ++--- 7 files changed, 36 insertions(+), 107 deletions(-) diff --git a/package.json b/package.json index a7a6c69..9c44f09 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "immersive", "private": true, - "version": "0.0.8-28", + "version": "0.0.8-30", "type": "module", "license": "MIT", "engines": { diff --git a/src/controllers/functions/snapAll.ts b/src/controllers/functions/snapAll.ts index 27e4961..10adb30 100644 --- a/src/controllers/functions/snapAll.ts +++ b/src/controllers/functions/snapAll.ts @@ -1,23 +1,23 @@ import {TransformNode, Vector3} from "@babylonjs/core"; -import {getAppConfig} from "../../util/appConfig"; +import {appConfigInstance} from "../../util/appConfig"; import {snapRotateVal} from "../../util/functions/snapRotateVal"; import {snapGridVal} from "../../util/functions/snapGridVal"; export function snapAll(node: TransformNode, pickPoint: Vector3) { - const config = getAppConfig(); + const config = appConfigInstance.current; const transform = new TransformNode('temp', node.getScene()); transform.position = pickPoint; node.setParent(transform); - if (config.rotationSnapEnabled) { - node.rotation = snapRotateVal(node.absoluteRotationQuaternion.toEulerAngles(), parseFloat(config.rotationSnap)); + if (config.rotateSnap > 0) { + node.rotation = snapRotateVal(node.absoluteRotationQuaternion.toEulerAngles(), config.rotateSnap); } - if (config.locationSnapEnabled) { - transform.position = snapGridVal(transform.absolutePosition, parseFloat(config.locationSnap)); + if (config.locationSnap > 0) { + transform.position = snapGridVal(transform.absolutePosition, config.locationSnap); } node.setParent(null); - if (config.locationSnapEnabled) { - node.position = snapGridVal(node.absolutePosition, parseFloat(config.locationSnap)); + if (config.locationSnap > 0) { + node.position = snapGridVal(node.absolutePosition, config.locationSnap); } transform.dispose(); diff --git a/src/menus/vrConfigPanel.ts b/src/menus/vrConfigPanel.ts index 850587c..e321c31 100644 --- a/src/menus/vrConfigPanel.ts +++ b/src/menus/vrConfigPanel.ts @@ -335,12 +335,10 @@ export class VRConfigPanel { const lastValue = appConfigInstance.current.locationSnap || 0.1; appConfigInstance.setGridSnap(lastValue > 0 ? lastValue : 0.1); this.updateLocationSnapButtonStates(lastValue); - this.syncLegacyConfig(); } else { // Disable by setting to 0 appConfigInstance.setGridSnap(0); this.updateLocationSnapButtonStates(0); - this.syncLegacyConfig(); } }); @@ -388,7 +386,6 @@ export class VRConfigPanel { if (this._locationSnapEnabled) { appConfigInstance.setGridSnap(snap.value); this.updateLocationSnapButtonStates(snap.value); - this.syncLegacyConfig(); } }); @@ -457,12 +454,10 @@ export class VRConfigPanel { const lastValue = appConfigInstance.current.rotateSnap || 90; appConfigInstance.setRotateSnap(lastValue > 0 ? lastValue : 90); this.updateRotationSnapButtonStates(lastValue); - this.syncLegacyConfig(); } else { // Disable by setting to 0 appConfigInstance.setRotateSnap(0); this.updateRotationSnapButtonStates(0); - this.syncLegacyConfig(); } }); @@ -510,7 +505,6 @@ export class VRConfigPanel { if (this._rotationSnapEnabled) { appConfigInstance.setRotateSnap(snap.value); this.updateRotationSnapButtonStates(snap.value); - this.syncLegacyConfig(); } }); @@ -586,26 +580,4 @@ export class VRConfigPanel { // - Snap turn UI update // - Label rendering mode UI update } - - /** - * Sync changes to legacy config for backward compatibility - * Legacy config is used by snapAll.ts and other older code - */ - private syncLegacyConfig(): void { - const config = appConfigInstance.current; - - const legacyConfig = { - locationSnap: config.locationSnap.toString(), - locationSnapEnabled: config.locationSnap > 0, - rotationSnap: config.rotateSnap.toString(), - rotationSnapEnabled: config.rotateSnap > 0, - snapTurnSnap: config.turnSnap.toString(), - snapTurnSnapEnabled: config.turnSnap > 0, - flyModeEnabled: config.flyMode, - labelRenderingMode: config.labelRenderingMode - }; - - localStorage.setItem('config', JSON.stringify(legacyConfig)); - this._logger.debug('Synced legacy config', legacyConfig); - } } diff --git a/src/react/pages/configModal.tsx b/src/react/pages/configModal.tsx index a9e819b..8f2b705 100644 --- a/src/react/pages/configModal.tsx +++ b/src/react/pages/configModal.tsx @@ -59,20 +59,6 @@ export default function ConfigModal({configOpened, closeConfig}) { appConfigInstance.setGridSnap(parseFloat(locationSnap)); appConfigInstance.setRotateSnap(parseFloat(rotationSnap)); appConfigInstance.setTurnSnap(parseFloat(snapTurnSnap)); - - // Also update legacy config for backward compatibility - const legacyConfig = { - locationSnap: locationSnap, - locationSnapEnabled: locationSnapEnabled, - rotationSnap: rotationSnap, - rotationSnapEnabled: rotationSnapEnabled, - snapTurnSnap: snapTurnSnap, - snapTurnSnapEnabled: snapTurnSnapEnabled, - flyModeEnabled: flyModeEnabled, - labelRenderingMode: labelRenderingMode - }; - localStorage.setItem('config', JSON.stringify(legacyConfig)); - }, [locationSnap, locationSnapEnabled, rotationSnap, rotationSnapEnabled, snapTurnSnap, snapTurnSnapEnabled, flyModeEnabled, labelRenderingMode]); return ( diff --git a/src/util/appConfig.ts b/src/util/appConfig.ts index 693a7e9..f768fd4 100644 --- a/src/util/appConfig.ts +++ b/src/util/appConfig.ts @@ -1,6 +1,6 @@ import {Observable} from "@babylonjs/core"; import {AppConfigType, LabelRenderingMode} from "./appConfigType"; -import log from "loglevel"; + export class AppConfig { public readonly onConfigChangedObservable = new Observable(); private _currentConfig: AppConfigType; @@ -88,42 +88,4 @@ export class AppConfig { // Singleton instance for app-wide configuration // Use this instead of creating new AppConfig() instances -export const appConfigInstance = new AppConfig(); - -let defaultConfig: ConfigType = - { - locationSnap: '.1', - locationSnapEnabled: true, - rotationSnap: '90', - rotationSnapEnabled: true, - flyModeEnabled: true, - snapTurnSnap: '45', - snapTurnSnapEnabled: false - } -try { - const newConfig = JSON.parse(localStorage.getItem('config')); - defaultConfig = {...defaultConfig, ...newConfig}; - -} catch (e) { - -} - -export type ConfigType = { - locationSnap: string, - locationSnapEnabled: boolean, - rotationSnap: string, - rotationSnapEnabled: boolean, - flyModeEnabled: boolean, - snapTurnSnap: string, - snapTurnSnapEnabled: boolean -} - -export function getAppConfig(): ConfigType { - return defaultConfig; -} - -export function setAppConfig(config: ConfigType) { - const logger = log.getLogger('setAppConfig'); - logger.debug('setting config', JSON.stringify(config)); - localStorage.setItem('config', JSON.stringify(config)); -} \ No newline at end of file +export const appConfigInstance = new AppConfig(); \ No newline at end of file diff --git a/src/util/customPhysics.ts b/src/util/customPhysics.ts index c2b9207..82a9f31 100644 --- a/src/util/customPhysics.ts +++ b/src/util/customPhysics.ts @@ -3,7 +3,7 @@ import HavokPhysics from "@babylonjs/havok"; import {snapGridVal} from "./functions/snapGridVal"; import {snapRotateVal} from "./functions/snapRotateVal"; import {isDiagramEntity} from "../diagram/functions/isDiagramEntity"; -import {getAppConfig} from "./appConfig"; +import {appConfigInstance} from "./appConfig"; export class CustomPhysics { private readonly scene: Scene; @@ -21,6 +21,7 @@ export class CustomPhysics { scene.collisionsEnabled = true; scene.onAfterPhysicsObservable.add(() => { + const config = appConfigInstance.current; scene.meshes.forEach((mesh) => { if (isDiagramEntity(mesh) && mesh.physicsBody) { const body = mesh.physicsBody; @@ -29,18 +30,26 @@ export class CustomPhysics { if (linearVelocity.length() < .1) { body.disablePreStep = false; - const pos: Vector3 = body.getObjectCenterWorld(); - const val: Vector3 = snapGridVal(pos, - parseFloat(getAppConfig().locationSnap)); - body.transformNode.position.set(val.x, val.y, val.z); - const rot: Quaternion = - Quaternion.FromEulerVector( - snapRotateVal(body.transformNode.rotationQuaternion.toEulerAngles(), - parseFloat(getAppConfig().rotationSnap))) - body.transformNode.rotationQuaternion.set( - rot.x, rot.y, rot.z, rot.w - ); + // Apply location snap if enabled + if (config.locationSnap > 0) { + const pos: Vector3 = body.getObjectCenterWorld(); + const val: Vector3 = snapGridVal(pos, config.locationSnap); + body.transformNode.position.set(val.x, val.y, val.z); + } + + // Apply rotation snap if enabled + if (config.rotateSnap > 0) { + const rot: Quaternion = + Quaternion.FromEulerVector( + snapRotateVal(body.transformNode.rotationQuaternion.toEulerAngles(), + config.rotateSnap)); + + body.transformNode.rotationQuaternion.set( + rot.x, rot.y, rot.z, rot.w + ); + } + scene.onAfterRenderObservable.addOnce(() => { body.disablePreStep = true; }); diff --git a/src/util/functions/groundMeshObserver.ts b/src/util/functions/groundMeshObserver.ts index eae4b5b..327b9b7 100644 --- a/src/util/functions/groundMeshObserver.ts +++ b/src/util/functions/groundMeshObserver.ts @@ -4,7 +4,7 @@ import {WebController} from "../../controllers/webController"; import {Rigplatform} from "../../controllers/rigplatform"; import {DiagramManager} from "../../diagram/diagramManager"; import {Spinner} from "../../objects/spinner"; -import {getAppConfig} from "../appConfig"; +import {appConfigInstance} from "../appConfig"; import {Scene} from "@babylonjs/core"; @@ -114,9 +114,9 @@ export async function groundMeshObserver(ground: AbstractMesh, }); const rig = new Rigplatform(xr, diagramManager); - const config = getAppConfig(); - rig.flyMode = config.flyModeEnabled; - rig.turnSnap = parseFloat(config.snapTurnSnap); + const config = appConfigInstance.current; + rig.flyMode = config.flyMode; + rig.turnSnap = config.turnSnap; const webController = new WebController(ground.getScene(), rig, diagramManager); // Set XR on diagram manager so toolbox can create exit button