From d87d8bace44ae66cf32fec9c48faec4fc523c4e6 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Fri, 28 Jul 2023 09:30:10 -0500 Subject: [PATCH] Refactored diagram manager and loggers. --- src/app.ts | 23 ++-- src/controllers/base.ts | 54 +++----- src/controllers/controllers.ts | 3 +- src/controllers/left.ts | 8 +- src/controllers/right.ts | 6 +- src/controllers/rigplatform.ts | 13 +- src/diagram/diagramManager.ts | 49 +++---- src/diagram/indexdbPersistenceManager.ts | 46 +++++-- src/diagram/persistenceManager.ts | 10 ++ src/information/inputTextView.ts | 2 +- src/menus/configMenu.ts | 52 +++++--- src/menus/editMenu.ts | 13 +- src/toolbox/toolbox.ts | 26 ++-- src/util/appConfig.ts | 158 ++++++++++++++++++++++- 14 files changed, 325 insertions(+), 138 deletions(-) diff --git a/src/app.ts b/src/app.ts index 29afda5..8b0c493 100644 --- a/src/app.ts +++ b/src/app.ts @@ -25,7 +25,8 @@ import {DiagramManager} from "./diagram/diagramManager"; import {Toolbox} from "./toolbox/toolbox"; import {DualshockEventMapper} from "./util/dualshockEventMapper"; import log from "loglevel"; - +import {AppConfig} from "./util/appConfig"; +import {IndexdbPersistenceManager} from "./diagram/indexdbPersistenceManager"; export class App { //preTasks = [havokModule]; @@ -35,6 +36,8 @@ export class App { private rig: Rigplatform; constructor() { + const config = AppConfig.config; + log.setLevel('debug'); const canvas = document.createElement("canvas"); canvas.style.width = "100%"; @@ -56,10 +59,6 @@ export class App { this.scene.dispose(); this.scene = null; } - if (DiagramManager.onDiagramEventObservable) { - DiagramManager.onDiagramEventObservable.clear(); - DiagramManager.onDiagramEventObservable = null; - } const engine = new Engine(canvas, true); const scene = new Scene(engine); @@ -106,10 +105,14 @@ export class App { } }); - + const persistenceManager = new IndexdbPersistenceManager("diagram"); const diagramManager = new DiagramManager(this.scene, this.xr.baseExperience); - this.rig = new Rigplatform(this.scene, this.xr); - const toolbox = new Toolbox(scene, this.xr.baseExperience); + diagramManager.setPersistenceManager(persistenceManager); + AppConfig.config.setPersistenceManager(persistenceManager); + + + this.rig = new Rigplatform(this.scene, this.xr, diagramManager); + const toolbox = new Toolbox(scene, this.xr.baseExperience, diagramManager); this.scene.gamepadManager.onGamepadConnectedObservable.add((gamepad) => { try { @@ -119,7 +122,7 @@ export class App { const buttonEvent = DualshockEventMapper.mapButtonEvent(button, 1); if (buttonEvent.objectName) { window.dispatchEvent(new CustomEvent('pa-button-state-change', { - detail: buttonEvent + detail: buttonEvent } )); } @@ -187,7 +190,7 @@ export class App { } }); log.info('App', 'keydown event listener added, use Ctrl+Shift+Alt+I to toggle debug layer'); - + persistenceManager.initialize(); engine.runRenderLoop(() => { scene.render(); diff --git a/src/controllers/base.ts b/src/controllers/base.ts index 8bcb395..28937f6 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -1,6 +1,5 @@ import { AbstractMesh, - Angle, InstancedMesh, Mesh, Scene, @@ -14,7 +13,6 @@ import {DiagramManager} from "../diagram/diagramManager"; import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity"; import log from "loglevel"; import {AppConfig} from "../util/appConfig"; -import round from "round"; export class Base { @@ -29,12 +27,16 @@ export class Base { protected previousPosition: Vector3 = null; protected readonly xr: WebXRDefaultExperience; + protected readonly diagramManager: DiagramManager; - constructor(controller: WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience) { + constructor(controller: WebXRInputSource, + scene: Scene, + xr: WebXRDefaultExperience, + diagramManager: DiagramManager) { this.controller = controller; this.scene = scene; this.xr = xr; - + this.diagramManager = diagramManager; this.controller.onMotionControllerInitObservable.add((init) => { if (init.components['xr-standard-trigger']) { init.components['xr-standard-trigger'] @@ -66,35 +68,6 @@ export class Base { } - static snapRotation(rotation): Vector3 { - const config = AppConfig.config; - if (config.rotateSnap == 0) { - return rotation; - } - rotation.x = this.CalcToSnap(rotation.x, config.rotateSnap); - rotation.y = this.CalcToSnap(rotation.y, config.rotateSnap); - rotation.z = this.CalcToSnap(rotation.z, config.rotateSnap); - return rotation; - } - - static snapPosition(position): Vector3 { - const config = AppConfig.config; - if (config.gridSnap == 0) { - return position; - } - position.x = round(position.x, config.gridSnap); - position.y = round(position.y, config.gridSnap); - position.z = round(position.z, config.gridSnap); - return position; - } - - static CalcToSnap(val, snap) { - const deg = Angle.FromRadians(val).degrees(); - const snappedDegrees = round(deg, snap); - log.getLogger('Base').debug("deg", val, deg, snappedDegrees, snap); - return Angle.FromDegrees(snappedDegrees).radians(); - } - private initGrip(grip: WebXRControllerComponent) { grip.onButtonStateChangedObservable.add(() => { if (grip.changes.pressed) { @@ -124,10 +97,11 @@ export class Base { mesh && mesh.setParent(this.controller.motionController.rootMesh); this.grabbedMesh = mesh; } else { + const config = AppConfig.config; const newMesh = this.createCopy(mesh); newMesh.position = mesh.absolutePosition.clone(); newMesh.rotation = mesh.absoluteRotationQuaternion.toEulerAngles().clone(); - newMesh.scaling = mesh.absoluteScaling.clone(); + newMesh.scaling = config.createSnapVal; newMesh.material = mesh.material; newMesh.metadata = mesh.metadata; newMesh && newMesh.setParent(this.controller.motionController.rootMesh); @@ -156,15 +130,19 @@ export class Base { return; } } - const snappedRotation = Base.snapRotation(mesh.absoluteRotationQuaternion.toEulerAngles().clone()); - const snappedPosition = Base.snapPosition(mesh.absolutePosition.clone()); + const config = AppConfig.config; + const snappedRotation = config.snapRotateVal(mesh.absoluteRotationQuaternion.toEulerAngles().clone()); + const snappedPosition = config.snapGridVal(mesh.absolutePosition.clone()); if (this.previousParent) { const p = this.scene.getMeshById(this.previousParent); if (p) { mesh && mesh.setParent(this.scene.getMeshById(this.previousParent)); + log.getLogger("Base").warn("Base", "Have not implemented snapping to parent yet"); + //@note: this is not implemented yet } else { mesh && mesh.setParent(null); - + mesh.rotation = snappedRotation; + mesh.position = snappedPosition; } } else { mesh && mesh.setParent(null); @@ -180,7 +158,7 @@ export class Base { this.previousScaling = null; this.previousRotation = null; this.previousPosition = null; - DiagramManager.onDiagramEventObservable.notifyObservers(event); + this.diagramManager.onDiagramEventObservable.notifyObservers(event); } } }); diff --git a/src/controllers/controllers.ts b/src/controllers/controllers.ts index 9d3b455..7af5b08 100644 --- a/src/controllers/controllers.ts +++ b/src/controllers/controllers.ts @@ -1,7 +1,8 @@ import {AbstractMesh, Observable, TransformNode} from "@babylonjs/core"; export type ControllerEventType = { - type: string + type: string, + value?: number } export class Controllers { public static movable: TransformNode | AbstractMesh; diff --git a/src/controllers/left.ts b/src/controllers/left.ts index d886479..f398cd1 100644 --- a/src/controllers/left.ts +++ b/src/controllers/left.ts @@ -3,15 +3,17 @@ import {Base} from "./base"; import {Controllers} from "./controllers"; import log from "loglevel"; import {ConfigMenu} from "../menus/configMenu"; +import {DiagramManager} from "../diagram/diagramManager"; export class Left extends Base { public static instance: Left; public configMenu: ConfigMenu; - constructor(controller: - WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience) { - super(controller, scene, xr); + constructor(controller: + WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager) { + + super(controller, scene, xr, diagramManager); this.configMenu = new ConfigMenu(this.scene, xr.baseExperience); Left.instance = this; this.controller.onMotionControllerInitObservable.add((init) => { diff --git a/src/controllers/right.ts b/src/controllers/right.ts index 898da04..0004f8c 100644 --- a/src/controllers/right.ts +++ b/src/controllers/right.ts @@ -2,13 +2,15 @@ import {Base} from "./base"; import {Scene, Vector3, WebXRControllerComponent, WebXRDefaultExperience, WebXRInputSource} from "@babylonjs/core"; import {Controllers} from "./controllers"; import log from "loglevel"; +import {DiagramManager} from "../diagram/diagramManager"; export class Right extends Base { public static instance: Right; constructor(controller: - WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience) { - super(controller, scene, xr); + WebXRInputSource, scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager + ) { + super(controller, scene, xr, diagramManager); Right.instance = this; this.controller.onMotionControllerInitObservable.add((init) => { this.initTrigger(init.components['xr-standard-trigger']); diff --git a/src/controllers/rigplatform.ts b/src/controllers/rigplatform.ts index d738625..59b778a 100644 --- a/src/controllers/rigplatform.ts +++ b/src/controllers/rigplatform.ts @@ -19,6 +19,7 @@ import {Left} from "./left"; import {EditMenu} from "../menus/editMenu"; import {Controllers} from "./controllers"; import log from "loglevel"; +import {DiagramManager} from "../diagram/diagramManager"; export class Rigplatform { @@ -34,13 +35,15 @@ export class Rigplatform { private camera: Camera; private turning: boolean = false; private velocity: Vector3 = Vector3.Zero(); + private readonly diagramManager: DiagramManager; - constructor(scene: Scene, xr: WebXRDefaultExperience) { + constructor(scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager) { this.scene = scene; + this.diagramManager = diagramManager; Rigplatform.xr = xr; Rigplatform.instance = this; - this.bMenu = new EditMenu(scene, xr.baseExperience); + this.bMenu = new EditMenu(scene, xr.baseExperience, this.diagramManager); this.camera = scene.activeCamera; this.rigMesh = MeshBuilder.CreateBox("platform", {width: 2, height: .02, depth: 2}, scene); @@ -111,11 +114,11 @@ export class Rigplatform { let controller; switch (source.inputSource.handedness) { case "right": - Right.instance = new Right(source, this.scene, Rigplatform.xr); + Right.instance = new Right(source, this.scene, Rigplatform.xr, this.diagramManager); Controllers.controllerObserver.add((event: { type: string, value: number }) => { switch (event.type) { case "increaseVelocity": - if (this.velocityIndex < this.velocityArray.length -1) { + if (this.velocityIndex < this.velocityArray.length - 1) { this.velocityIndex++; } else { this.velocityIndex = 0; @@ -150,7 +153,7 @@ export class Rigplatform { }); break; case "left": - Left.instance = new Left(source, this.scene, Rigplatform.xr); + Left.instance = new Left(source, this.scene, Rigplatform.xr, this.diagramManager); break; } diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index b4c67de..e4fa510 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -1,41 +1,42 @@ import {Observable, Scene, WebXRExperienceHelper} from "@babylonjs/core"; - import {DiagramEntity, DiagramEvent, DiagramEventType} from "./diagramEntity"; import {IPersistenceManager} from "./persistenceManager"; -import {IndexdbPersistenceManager} from "./indexdbPersistenceManager"; import {MeshConverter} from "./meshConverter"; import log from "loglevel"; export class DiagramManager { - private persistenceManager: IPersistenceManager = new IndexdbPersistenceManager("diagram"); - static onDiagramEventObservable: Observable = new Observable(); + public readonly onDiagramEventObservable: Observable = new Observable(); + private readonly logger = log.getLogger('DiagramManager'); + private persistenceManager: IPersistenceManager = null; private readonly scene: Scene; private xr: WebXRExperienceHelper; constructor(scene: Scene, xr: WebXRExperienceHelper) { this.scene = scene; this.xr = xr; - this.persistenceManager.updateObserver.add(this.onRemoteEvent, -1, true, this); - log.getLogger('DiagramManager').debug( "remote event observer added"); - this.persistenceManager.initialize(); - - if (!DiagramManager.onDiagramEventObservable) { - log.getLogger('DiagramManager').debug( "onDiagramEventObservable missing, recreated"); - DiagramManager.onDiagramEventObservable = new Observable(); + if (this.onDiagramEventObservable.hasObservers()) { + this.logger.warn("onDiagramEventObservable already has Observers, you should be careful"); } - if (DiagramManager.onDiagramEventObservable.hasObservers()) { - log.getLogger('DiagramManager').debug("onDiagramEventObservable already has Observers, this shouldn't happen"); - } else { - DiagramManager.onDiagramEventObservable.add(this.onDiagramEvent, -1, true, this); - log.getLogger('DiagramManager').debug( "onDiagramEventObservable Observer added"); - } - log.getLogger('DiagramManager').debug( "DiagramManager constructed"); + this.onDiagramEventObservable.add(this.onDiagramEvent, -1, true, this); + this.logger.debug("DiagramManager constructed"); } + public setPersistenceManager(persistenceManager: IPersistenceManager) { + this.persistenceManager = persistenceManager; + this.persistenceManager.updateObserver.add(this.onRemoteEvent, -1, true, this); + } + + private getPersistenceManager(): IPersistenceManager { + if (!this.persistenceManager) { + this.logger.warn("persistenceManager not set"); + return null; + } + return this.persistenceManager; + } private onRemoteEvent(event: DiagramEntity) { //const mesh = Toolbox.instance.newMesh(ToolType[Object.entries(ToolType).find(e => e[1] == event.template)[0]], event.id); - log.getLogger('DiagramManager').debug(event); + this.logger.debug(event); const mesh = MeshConverter.fromDiagramEntity(event, this.scene); if (event.parent) { mesh.parent = this.scene.getMeshById(event.parent); @@ -43,7 +44,7 @@ export class DiagramManager { } private onDiagramEvent(event: DiagramEvent) { - log.getLogger("DiagramManager").debug(event); + this.logger.debug(event.type); const entity = event.entity; let mesh; if (entity) { @@ -55,19 +56,19 @@ export class DiagramManager { case DiagramEventType.DROPPED: break; case DiagramEventType.DROP: - this.persistenceManager.add(mesh); + this.getPersistenceManager()?.add(mesh) break; case DiagramEventType.ADD: break; case DiagramEventType.MODIFY: - this.persistenceManager.modify(mesh); + this.getPersistenceManager()?.modify(mesh) break; case DiagramEventType.CHANGECOLOR: - this.persistenceManager.changeColor(event.oldColor, event.newColor); + this.getPersistenceManager()?.changeColor(event.oldColor, event.newColor); break; case DiagramEventType.REMOVE: if (mesh) { - this.persistenceManager.remove(mesh); + this.getPersistenceManager()?.remove(mesh) mesh.dispose(); } break; diff --git a/src/diagram/indexdbPersistenceManager.ts b/src/diagram/indexdbPersistenceManager.ts index 7d71347..aab1744 100644 --- a/src/diagram/indexdbPersistenceManager.ts +++ b/src/diagram/indexdbPersistenceManager.ts @@ -6,16 +6,18 @@ import {MeshConverter} from "./meshConverter"; import log from "loglevel"; export class IndexdbPersistenceManager implements IPersistenceManager { + private readonly logger = log.getLogger('IndexdbPersistenceManager'); public updateObserver: Observable = new Observable(); private db: Dexie; constructor(name: string) { this.db = new Dexie(name); this.db.version(1).stores({entities: "id,position,rotation,last_seen,template,text,scale,color"}); - log.debug('IndexdbPersistenceManager', "IndexdbPersistenceManager constructed"); + this.logger.debug("IndexdbPersistenceManager constructed"); + } public add(mesh: AbstractMesh) { if (!mesh) { - log.warn('IndexdbPersistenceManager', "Adding null mesh"); + this.logger.error("Adding null mesh, early return"); return; } const entity = MeshConverter.toDiagramEntity(mesh); @@ -24,36 +26,54 @@ export class IndexdbPersistenceManager implements IPersistenceManager { entity.scale = this.vectoxys(mesh.scaling); this.db["entities"].add(entity); - } - private vectoxys(v: Vector3): {x, y ,z} { - return {x: v.x, y: v.y, z: v.z}; - } - private xyztovec(xyz: {x, y, z}): Vector3 { - return new Vector3(xyz.x, xyz.y, xyz.z); + this.logger.debug('add', mesh, entity); } - public remove(mesh: AbstractMesh) { + public remove(mesh: AbstractMesh) { this.db["entities"].delete(mesh.id); } - public modify(mesh) { + + public getConfig(): any { + this.logger.warn('getConfig not implemented'); + //@todo implement + } + + public setConfig(config: any) { + this.logger.warn('setConfig not implemented, value not persisted', config); + //@todo implement + } + + public modify(mesh) { const entity = MeshConverter.toDiagramEntity(mesh); entity.position = this.vectoxys(mesh.position); entity.rotation = this.vectoxys(mesh.rotation); entity.scale = this.vectoxys(mesh.scaling); this.db["entities"].update(mesh.id, entity); + this.logger.debug('modify', mesh, entity); } + public initialize() { + this.logger.info('initialize', this.db['entities'].length); this.db['entities'].each((e) => { e.position = this.xyztovec(e.position); e.rotation = this.xyztovec(e.rotation); e.scale = this.xyztovec(e.scale); - log.debug('IndexdbPersistenceManager', 'adding', e); + this.logger.debug('adding', e); this.updateObserver.notifyObservers(e); }); - log.warn('IndexdbPersistenceManager', "initialize finished"); + this.logger.info("initialize finished"); } + public changeColor(oldColor, newColor) { - log.debug('IndexdbPersistenceManager', `changeColor ${oldColor.toHexString()} to ${newColor.toHexString()}`); + this.logger.debug(`changeColor ${oldColor.toHexString()} to ${newColor.toHexString()}`); this.db['entities'].where('color').equals(oldColor.toHexString()).modify({color: newColor.toHexString()}); } + + private vectoxys(v: Vector3): { x, y, z } { + return {x: v.x, y: v.y, z: v.z}; + } + + private xyztovec(xyz: { x, y, z }): Vector3 { + return new Vector3(xyz.x, xyz.y, xyz.z); + } } \ No newline at end of file diff --git a/src/diagram/persistenceManager.ts b/src/diagram/persistenceManager.ts index 55f9cbc..6fcfaa4 100644 --- a/src/diagram/persistenceManager.ts +++ b/src/diagram/persistenceManager.ts @@ -1,12 +1,22 @@ import {AbstractMesh, Color3, Observable} from "@babylonjs/core"; import {DiagramEntity} from "./diagramEntity"; +import {AppConfigType} from "../util/appConfig"; export interface IPersistenceManager { add(mesh: AbstractMesh); + remove(mesh: AbstractMesh); + modify(mesh: AbstractMesh); + initialize(); + + getConfig(): AppConfigType; + + setConfig(config: AppConfigType); + changeColor(oldColor: Color3, newColor: Color3) + updateObserver: Observable; } diff --git a/src/information/inputTextView.ts b/src/information/inputTextView.ts index 0c8074d..78c9a7a 100644 --- a/src/information/inputTextView.ts +++ b/src/information/inputTextView.ts @@ -3,7 +3,7 @@ import {AdvancedDynamicTexture, InputText} from "@babylonjs/gui"; export class InputTextView { private mesh: AbstractMesh; - private scene: Scene; + private readonly scene: Scene; private xr: WebXRExperienceHelper; private inputPlane: AbstractMesh; private inputText: InputText; diff --git a/src/menus/configMenu.ts b/src/menus/configMenu.ts index a655563..b5fc124 100644 --- a/src/menus/configMenu.ts +++ b/src/menus/configMenu.ts @@ -38,30 +38,52 @@ export class ConfigMenu { selectionPanel.fontSize = "24px"; selectionPanel.height = "100%"; configTexture.addControl(selectionPanel) - const radio1 = new RadioGroup("Rotation Snap"); - radio1.addRadio("Off", this.rotateVal); - radio1.addRadio("22.5 degrees", this.rotateVal); - radio1.addRadio("45 degrees", this.rotateVal); - radio1.addRadio("90 degrees", this.rotateVal); - selectionPanel.addGroup(radio1); - const radio2 = new RadioGroup("Grid Snap"); - radio2.addRadio("Off", this.gridVal); - radio2.addRadio("1 cm", this.gridVal); - radio2.addRadio("10 cm", this.gridVal); - radio2.addRadio("25 cm", this.gridVal); - selectionPanel.addGroup(radio1); - selectionPanel.addGroup(radio2); + selectionPanel.addGroup(this.buildGridSizeControl()); + selectionPanel.addGroup(this.buildRotationSnapControl()); + selectionPanel.addGroup(this.buildCreateScaleControl()); this.configPlane.position = CameraHelper.getFrontPosition(2, this.scene); this.configPlane.rotation.y = Angle.FromDegrees(180).radians(); } + private createVal(value) { + AppConfig.config.currentCreateSnapIndex = value; + log.debug("configMenu", "create Snap", value); + } + + private buildCreateScaleControl(): RadioGroup { + const radio = new RadioGroup("Create Scale"); + for (const [index, snap] of AppConfig.config.createSnaps().entries()) { + const selected = AppConfig.config.currentCreateSnapIndex == index; + radio.addRadio(snap.label, this.createVal, selected); + } + return radio; + } + + private buildRotationSnapControl(): RadioGroup { + const radio = new RadioGroup("Rotation Snap"); + for (const [index, snap] of AppConfig.config.rotateSnaps().entries()) { + const selected = AppConfig.config.currentRotateSnapIndex == index; + radio.addRadio(snap.label, this.rotateVal, selected); + } + return radio; + } + + private buildGridSizeControl(): RadioGroup { + const radio = new RadioGroup("Grid Snap"); + for (const [index, snap] of AppConfig.config.gridSnaps().entries()) { + const selected = AppConfig.config.currentGridSnapIndex == index; + radio.addRadio(snap.label, this.gridVal, selected); + } + return radio; + } + private rotateVal(value) { - AppConfig.config.rotateSnap = AppConfig.config.rotateSnapArray[value]; + AppConfig.config.currentRotateSnapIndex = value; log.debug("configMenu", "rotate Snap", value); } private gridVal(value) { - AppConfig.config.gridSnap = AppConfig.config.gridSnapArray[value]; + AppConfig.config.currentGridSnapIndex = value; log.debug("configMenu", "grid Snap", value); } diff --git a/src/menus/editMenu.ts b/src/menus/editMenu.ts index 1730e32..1e58025 100644 --- a/src/menus/editMenu.ts +++ b/src/menus/editMenu.ts @@ -24,12 +24,13 @@ export class EditMenu { private textView: InputTextView; private textInput: HTMLElement; private gizmoManager: GizmoManager; - private xr: WebXRExperienceHelper; - - constructor(scene: Scene, xr: WebXRExperienceHelper) { + private readonly xr: WebXRExperienceHelper; + private readonly diagramManager: DiagramManager; + constructor(scene: Scene, xr: WebXRExperienceHelper, diagramManager: DiagramManager) { this.scene = scene; this.xr = xr; + this.diagramManager = diagramManager; this.gizmoManager = new GizmoManager(scene); this.gizmoManager.boundingBoxGizmoEnabled = true; this.gizmoManager.gizmos.boundingBoxGizmo.scaleBoxSize = .020; @@ -71,7 +72,7 @@ export class EditMenu { entity: MeshConverter.toDiagramEntity(pointerInfo.pickInfo.pickedMesh) } - DiagramManager.onDiagramEventObservable.notifyObservers(event); + this.diagramManager.onDiagramEventObservable.notifyObservers(event); break; case BmenuState.MODIFYING: if (pointerInfo.pickInfo.pickedMesh.metadata?.template && @@ -83,7 +84,7 @@ export class EditMenu { this.gizmoManager.attachToMesh(mesh); this.gizmoManager.gizmos.boundingBoxGizmo.onScaleBoxDragObservable.add(() => { - DiagramManager.onDiagramEventObservable.notifyObservers({ + this.diagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.MODIFY, entity: MeshConverter.toDiagramEntity(mesh), } @@ -158,7 +159,7 @@ export class EditMenu { } else { log.getLogger('bmenu').error("mesh has no metadata"); } - DiagramManager.onDiagramEventObservable.notifyObservers({ + this.diagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.MODIFY, entity: MeshConverter.toDiagramEntity(mesh), }); diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 109adea..e99a27c 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -27,27 +27,30 @@ export enum ToolType { } export class Toolbox { - public static getToolTypeFromString(type: string): ToolType { - return ToolType[Object.keys(ToolType).find(() => type)] - } - private index = 0; public static instance: Toolbox; private readonly scene: Scene; private readonly xr: WebXRExperienceHelper; - public readonly node : TransformNode; + public readonly node: TransformNode; + private readonly diagramManager: DiagramManager; private readonly manager: GUI3DManager; private readonly gridsize = 5; private readonly addPanel: StackPanel3D; - constructor (scene:Scene, xr: WebXRExperienceHelper) { + + constructor(scene: Scene, xr: WebXRExperienceHelper, diagramManager: DiagramManager) { this.scene = scene; + this.diagramManager = diagramManager; this.addPanel = new StackPanel3D(); - this.manager = new GUI3DManager(scene); + this.manager = new GUI3DManager(scene); this.manager.addControl(this.addPanel); this.node = new TransformNode("toolbox", this.scene); - const handle = MeshBuilder.CreateCapsule("handle", { radius: .01 , orientation: Vector3.Right(), height: .3}, this.scene); + const handle = MeshBuilder.CreateCapsule("handle", { + radius: .01, + orientation: Vector3.Right(), + height: .3 + }, this.scene); handle.id = "handle"; - const handleMaterial = new StandardMaterial("handle-material", this.scene); + const handleMaterial = new StandardMaterial("handle-material", this.scene); handleMaterial.diffuseColor = Color3.FromHexString("#EEEEFF"); handle.material = handleMaterial; handle.position = CameraHelper.getFrontPosition(2, this.scene); @@ -129,7 +132,7 @@ export class Toolbox { material.name = "material-" + value.toHexString(); mesh.id = "toolbox-color-" + value.toHexString(); mesh.name = "toolbox-color-" + value.toHexString(); - DiagramManager.onDiagramEventObservable.notifyObservers( + this.diagramManager.onDiagramEventObservable.notifyObservers( { type: DiagramEventType.CHANGECOLOR, oldColor: oldColor, @@ -206,9 +209,6 @@ export class Toolbox { return null; } } - public show() { - this.buildToolbox(); - } } function enumKeys(obj: O): K[] { return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[]; diff --git a/src/util/appConfig.ts b/src/util/appConfig.ts index bf22d71..0b6e30a 100644 --- a/src/util/appConfig.ts +++ b/src/util/appConfig.ts @@ -1,10 +1,45 @@ +import {Angle, Vector3} from "@babylonjs/core"; +import round from "round"; +import log from "loglevel"; +import {IPersistenceManager} from "../diagram/persistenceManager"; + +export type SnapValue = { + value: number, + label: string +} +export type AppConfigType = { + gridSnap: number, + rotateSnap: number, + createSnap: number +} + export class AppConfig { - public gridSnap = 0; - public rotateSnap = 0; - public gridSnapArray = - [0, 0.1, 0.5, 1]; - public rotateSnapArray = - [0, 22.5, 45, 90] + private gridSnap = 0; + private rotateSnap = 0; + private createSnap = 0; + private readonly defaultGridSnapIndex = 1; + private persistenceManager: IPersistenceManager = null; + private gridSnapArray: SnapValue[] = + [{value: 0, label: "Off"}, + {value: 0.05, label: "(Default)"}, + {value: 0.01, label: "1 cm"}, + {value: 0.1, label: "10 cm"}, + {value: 0.25, label: "25 cm"}, + {value: .5, label: ".5 m"}] + private createSnapArray: SnapValue[] = + [{value: .1, label: "Default (10 cm)"}, + {value: 0.2, label: "20 cm"}, + {value: 0.5, label: ".5 m"}, + {value: 1, label: "1 m"}]; + private rotateSnapArray: SnapValue[] = + [{value: 0, label: "Off"}, + {value: 22.5, label: "22.5 Degrees"}, + {value: 45, label: "45 Degrees"}, + {value: 90, label: "90 Degrees"}]; + + public get currentGridSnap(): SnapValue { + return this.gridSnapArray[this.gridSnap]; + } private static _config: AppConfig; @@ -13,7 +48,116 @@ export class AppConfig { AppConfig._config = new AppConfig(); } return AppConfig._config; - } + public get currentRotateSnap(): SnapValue { + return this.rotateSnapArray[this.rotateSnap]; + } + + public get currentCreateSnap(): SnapValue { + return this.createSnapArray[this.createSnap]; + } + + public get currentGridSnapIndex(): number { + return this.gridSnap; + } + + public set currentGridSnapIndex(val: number) { + this.gridSnap = val; + this.save(); + } + + public get currentCreateSnapIndex(): number { + return this.createSnap; + } + + public set currentCreateSnapIndex(val: number) { + this.createSnap = val; + if (this.currentGridSnapIndex == this.defaultGridSnapIndex) { + this.currentGridSnap.value = this.currentCreateSnap.value / 2; + log.getLogger('AppConfig').debug("Set grid snap to " + this.currentGridSnap.value); + } + this.save(); + } + + public get currentRotateSnapIndex(): number { + return this.rotateSnap; + } + + public set currentRotateSnapIndex(val: number) { + this.rotateSnap = val; + this.save(); + } + + public get createSnapVal(): Vector3 { + return new Vector3(this.currentCreateSnap.value, this.currentCreateSnap.value, this.currentCreateSnap.value); + } + + public setPersistenceManager(persistenceManager: IPersistenceManager) { + this.persistenceManager = persistenceManager; + this.load(); + } + + public gridSnaps(): SnapValue[] { + return this.gridSnapArray; + } + + public createSnaps(): SnapValue[] { + return this.createSnapArray; + } + + public rotateSnaps(): SnapValue[] { + return this.rotateSnapArray; + } + + public snapGridVal(value: Vector3): Vector3 { + if (this.currentGridSnapIndex == 0) { + return value; + } + const position = value.clone(); + position.x = round(position.x, this.currentGridSnap.value); + position.y = round(position.y, this.currentGridSnap.value); + position.z = round(position.z, this.currentGridSnap.value); + return position; + } + + public snapRotateVal(value: Vector3): Vector3 { + if (this.currentRotateSnapIndex == 0) { + return value; + } + const rotation = new Vector3(); + rotation.x = this.snapAngle(value.x); + rotation.y = this.snapAngle(value.y); + rotation.z = this.snapAngle(value.z); + return rotation; + } + + private save() { + this.persistenceManager.setConfig( + { + gridSnap: this.currentGridSnap.value, + rotateSnap: this.currentRotateSnap.value, + createSnap: this.currentCreateSnap.value + }); + } + + private load() { + const config = this.persistenceManager.getConfig(); + if (config) { + this.rotateSnap = this.rotateSnapArray.findIndex((snap) => snap.value == config.rotateSnap); + this.createSnap = this.createSnapArray.findIndex((snap) => snap.value == config.createSnap); + const gridSnap = this.gridSnapArray.findIndex((snap) => snap.value == config.gridSnap); + if (gridSnap == -1) { + this.gridSnap = this.defaultGridSnapIndex; + this.currentGridSnap.value = config.gridSnap; + } + } + } + + private snapAngle(val: number): number { + const deg = Angle.FromRadians(val).degrees(); + const snappedDegrees = round(deg, this.currentRotateSnap.value); + log.getLogger('AppConfig').debug("deg", val, deg, snappedDegrees, this.currentRotateSnap.value); + return Angle.FromDegrees(snappedDegrees).radians(); + } } \ No newline at end of file