From e30bca5090edf019c0587f72f896620e3a77edeb Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Tue, 23 Apr 2024 09:10:26 -0500 Subject: [PATCH] Refactor digramManager observer mask, updated logger in controllerbase. --- src/controllers/base.ts | 36 ++--- .../functions/motionControllerObserver.ts | 5 +- src/controllers/webController.ts | 4 +- src/diagram/diagramManager.ts | 46 ++---- src/diagram/functions/diagramEventHandler.ts | 1 - src/integration/functions/hexFunctions.ts | 17 ++ src/integration/pouchdbPersistenceManager.ts | 146 ++++++------------ src/menus/clickMenu.ts | 6 +- src/util/functions/getPath.ts | 8 + 9 files changed, 113 insertions(+), 156 deletions(-) create mode 100644 src/integration/functions/hexFunctions.ts create mode 100644 src/util/functions/getPath.ts diff --git a/src/controllers/base.ts b/src/controllers/base.ts index 3737eea..288794b 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -9,7 +9,7 @@ import { WebXRDefaultExperience, WebXRInputSource } from "@babylonjs/core"; -import {DiagramManager} from "../diagram/diagramManager"; +import {DiagramEventObserverMask, DiagramManager} from "../diagram/diagramManager"; import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity"; import log from "loglevel"; import {ControllerEventType, Controllers} from "./controllers"; @@ -30,6 +30,8 @@ import {pointable} from "./functions/pointable"; import {DefaultScene} from "../defaultScene"; const CLICK_TIME = 300; +const logger = log.getLogger('Base'); + export class Base { static stickVector = Vector3.Zero(); protected xrInputSource: WebXRInputSource; @@ -44,7 +46,6 @@ export class Base { private clickStart: number = 0; protected readonly xr: WebXRDefaultExperience; protected readonly diagramManager: DiagramManager; - private logger: log.Logger; private lastPosition: Vector3 = null; protected controllers: Controllers; private clickMenu: ClickMenu; @@ -53,8 +54,6 @@ export class Base { constructor(controller: WebXRInputSource, xr: WebXRDefaultExperience, diagramManager: DiagramManager) { - this.logger = log.getLogger('Base'); - this.logger.setLevel(this.logger.levels.DEBUG); this.xrInputSource = controller; this.controllers = diagramManager.controllers; this.scene = DefaultScene.Scene; @@ -69,15 +68,17 @@ export class Base { }); this.diagramManager = diagramManager; this.scene.onBeforeRenderObservable.add(beforeRenderObserver, -1, false, this); + + //@TODO THis works, but it uses initGrip, not sure if this is the best idea this.xrInputSource.onMotionControllerInitObservable.add(motionControllerObserver, -1, false, this); this.controllers.controllerObserver.add((event) => { - this.logger.debug(event); + logger.debug(event); switch (event.type) { case ControllerEventType.PULSE: if (event.gripId == this?.xrInputSource?.grip?.id) { this.xrInputSource?.motionController?.pulse(.25, 30) .then(() => { - this.logger.debug("pulse done"); + logger.debug("pulse done"); }); } break; @@ -104,7 +105,7 @@ export class Base { } protected initClicker(trigger: WebXRControllerComponent) { - this.logger.debug("initTrigger"); + logger.debug("initTrigger"); trigger.onButtonStateChangedObservable.add(() => { if (trigger.changes.pressed) { if (trigger.pressed) { @@ -112,7 +113,7 @@ export class Base { this.clickStart = Date.now(); window.setTimeout(() => { if (this.clickStart > 0) { - this.logger.debug("grabbing and cloning"); + logger.debug("grabbing and cloning"); this.grab(true); } }, 300, this); @@ -159,7 +160,7 @@ export class Base { } this.previousParentId = mesh?.parent?.id; - this.logger.warn("grabbed " + mesh?.id + " parent " + this.previousParentId); + logger.warn("grabbed " + mesh?.id + " parent " + this.previousParentId); this.previousRotation = mesh?.rotation.clone(); this.previousScaling = mesh?.scaling.clone(); this.previousPosition = mesh?.position.clone(); @@ -174,7 +175,7 @@ export class Base { } this.grabbedMesh = mesh; } else { - this.logger.debug("cloning " + mesh?.id); + logger.debug("cloning " + mesh?.id); const clone = grabAndClone(this.diagramManager, mesh, this.xrInputSource.motionController.rootMesh); clone.newMesh.metadata.grabClone = false; clone.newMesh.metadata.tool = false; @@ -185,7 +186,7 @@ export class Base { type: DiagramEventType.ADD, entity: toDiagramEntity(clone.newMesh) } - this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); + this.diagramManager.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL); } } @@ -232,22 +233,22 @@ export class Base { const body = mesh?.physicsBody; if (body) { body.setMotionType(PhysicsMotionType.DYNAMIC); - this.logger.debug(body.transformNode.absolutePosition); - this.logger.debug(this.lastPosition); + logger.debug(body.transformNode.absolutePosition); + logger.debug(this.lastPosition); if (this.lastPosition) { body.setLinearVelocity(body.transformNode.absolutePosition.subtract(this.lastPosition).scale(20)); //body.setLinearVelocity(this.lastPosition.subtract(body.transformNode.absolutePosition).scale(20)); - this.logger.debug(this.lastPosition.subtract(body.transformNode.absolutePosition).scale(20)); + logger.debug(this.lastPosition.subtract(body.transformNode.absolutePosition).scale(20)); } } - this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); + this.diagramManager.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL); } private click() { let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.xrInputSource.uniqueId); if (pointable(mesh)) { - this.logger.debug("click on " + mesh.id); + logger.debug("click on " + mesh.id); if (this.clickMenu && !this.clickMenu.isDisposed) { if (this.clickMenu.isConnecting) { this.clickMenu.connect(mesh); @@ -258,7 +259,7 @@ export class Base { } } else { - this.logger.debug("click on nothing"); + logger.debug("click on nothing"); } @@ -270,7 +271,6 @@ export class Base { if (grip.pressed) { this.grab(); } else { - this.drop(); } } diff --git a/src/controllers/functions/motionControllerObserver.ts b/src/controllers/functions/motionControllerObserver.ts index fce8a80..2115f63 100644 --- a/src/controllers/functions/motionControllerObserver.ts +++ b/src/controllers/functions/motionControllerObserver.ts @@ -1,5 +1,8 @@ +import log from "loglevel"; + +const logger = log.getLogger('motionControllerObserver'); export function motionControllerObserver(init) { - this.logger.debug(init.components); + logger.debug(init.components); if (init.components['xr-standard-squeeze']) { this.initGrip(init.components['xr-standard-squeeze']) } diff --git a/src/controllers/webController.ts b/src/controllers/webController.ts index b39c91c..3273637 100644 --- a/src/controllers/webController.ts +++ b/src/controllers/webController.ts @@ -1,7 +1,7 @@ import {AbstractMesh, KeyboardEventTypes, MeshBuilder, Scene} from "@babylonjs/core"; import {Rigplatform} from "./rigplatform"; import {ControllerEventType, Controllers} from "./controllers"; -import {DiagramManager} from "../diagram/diagramManager"; +import {DiagramEventObserverMask, DiagramManager} from "../diagram/diagramManager"; import {GridMaterial} from "@babylonjs/materials"; import {wheelHandler} from "./functions/wheelHandler"; import log, {Logger} from "loglevel"; @@ -206,7 +206,7 @@ export class WebController { this.diagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.MODIFY, entity: toDiagramEntity(this.mesh) - }, -1); + }, DiagramEventObserverMask.ALL); } this.mesh = null; } diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 98a1a1b..d4048f8 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -1,4 +1,4 @@ -import {AbstractMesh, ActionManager, Color3, InstancedMesh, Mesh, Observable, Scene} from "@babylonjs/core"; +import {AbstractMesh, ActionManager, InstancedMesh, Mesh, Observable, Scene} from "@babylonjs/core"; import {DiagramEvent, DiagramEventType} from "./types/diagramEntity"; import log from "loglevel"; import {Controllers} from "../controllers/controllers"; @@ -16,7 +16,11 @@ import {InputTextView} from "../information/inputTextView"; import {DefaultScene} from "../defaultScene"; import {ScaleMenu} from "../menus/scaleMenu"; - +export enum DiagramEventObserverMask { + ALL = -1, + FROM_DB = 1, + TO_DB = 2, +} export class DiagramManager { public readonly _config: AppConfig; private readonly _controllers: Controllers; @@ -39,10 +43,7 @@ export class DiagramManager { if (mesh) { const entity = toDiagramEntity(mesh); entity.text = evt.text; - this.onDiagramEventObservable.notifyObservers({ - type: DiagramEventType.MODIFY, - entity: entity - }, -1); + this.notifyAll({type: DiagramEventType.MODIFY, entity: entity}); } else { this.logger.error("mesh not found", evt.id); } @@ -51,29 +52,13 @@ export class DiagramManager { this.toolbox = new Toolbox(); this.scaleMenu = new ScaleMenu(); this.scaleMenu.onScaleChangeObservable.add((mesh: AbstractMesh) => { - this.onDiagramEventObservable.notifyObservers({ - type: DiagramEventType.MODIFY, - entity: toDiagramEntity(mesh), - }, -1); - + this.notifyAll({type: DiagramEventType.MODIFY, entity: toDiagramEntity(mesh)}); const position = mesh.absolutePosition.clone(); position.y = mesh.getBoundingInfo().boundingBox.maximumWorld.y + .1; this.scaleMenu.changePosition(position); }); - //this.presentationManager = new PresentationManager(this._scene); this.diagramEntityActionManager = buildEntityActionManager(this._controllers); - - if (this.onDiagramEventObservable.hasObservers()) { - this.logger.warn("onDiagramEventObservable already has Observers, you should be careful"); - } - this.toolbox.colorChangeObservable.add((evt) => { - this.logger.debug(evt); - this.onDiagramEventObservable.notifyObservers({ - type: DiagramEventType.CHANGECOLOR, - oldColor: Color3.FromHexString(evt.oldColor), newColor: Color3.FromHexString(evt.newColor) - }, 2); - }, -1, true, this, false); - this.onDiagramEventObservable.add(this.onDiagramEvent, 1, true, this); + this.onDiagramEventObservable.add(this.onDiagramEvent, DiagramEventObserverMask.FROM_DB, true, this); this.logger.debug("DiagramManager constructed"); this._scene.onMeshRemovedObservable.add((mesh) => { @@ -82,10 +67,7 @@ export class DiagramManager { this._scene.meshes.forEach((m) => { if (m?.metadata?.to == mesh.id || m?.metadata?.from == mesh.id) { this.logger.debug("removing connection", m.id); - this.onDiagramEventObservable.notifyObservers({ - type: DiagramEventType.REMOVE, - entity: toDiagramEntity(m) - }, -1); + this.notifyAll({type: DiagramEventType.REMOVE, entity: toDiagramEntity(m)}); } }); } @@ -93,6 +75,10 @@ export class DiagramManager { }); } + private notifyAll(event: DiagramEvent) { + this.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL); + } + public editText(mesh: AbstractMesh) { this.inputTextView.show(mesh); } @@ -105,10 +91,6 @@ export class DiagramManager { return this._config; } - public get scene(): Scene { - return this._scene; - } - public createCopy(mesh: AbstractMesh, copy: boolean = false): AbstractMesh { let newMesh; if (!mesh.isAnInstance) { diff --git a/src/diagram/functions/diagramEventHandler.ts b/src/diagram/functions/diagramEventHandler.ts index 0d9c8d5..3e17509 100644 --- a/src/diagram/functions/diagramEventHandler.ts +++ b/src/diagram/functions/diagramEventHandler.ts @@ -38,7 +38,6 @@ export function diagramEventHandler(event: DiagramEvent, updateTextNode(mesh, entity.text); } switch (event.type) { - case DiagramEventType.RESET: scene.getNodes().forEach((node) => { if (node?.metadata?.template && !node?.metadata?.tool) { diff --git a/src/integration/functions/hexFunctions.ts b/src/integration/functions/hexFunctions.ts new file mode 100644 index 0000000..37cddbc --- /dev/null +++ b/src/integration/functions/hexFunctions.ts @@ -0,0 +1,17 @@ +export function hex_to_ascii(input) { + var hex = input.toString(); + let output = ''; + for (let n = 0; n < hex.length; n += 2) { + output += String.fromCharCode(parseInt(hex.substr(n, 2), 16)); + } + return output; +} + +export function ascii_to_hex(str) { + const arr1 = []; + for (let n = 0, l = str.length; n < l; n++) { + var hex = Number(str.charCodeAt(n)).toString(16); + arr1.push(hex); + } + return arr1.join(''); +} \ No newline at end of file diff --git a/src/integration/pouchdbPersistenceManager.ts b/src/integration/pouchdbPersistenceManager.ts index c9eb573..d7522fc 100644 --- a/src/integration/pouchdbPersistenceManager.ts +++ b/src/integration/pouchdbPersistenceManager.ts @@ -1,14 +1,16 @@ import PouchDB from 'pouchdb'; import {DiagramEntity, DiagramEventType} from "../diagram/types/diagramEntity"; -import {Color3, Observable} from "@babylonjs/core"; +import {Observable} from "@babylonjs/core"; import axios from "axios"; -import {DiagramManager} from "../diagram/diagramManager"; +import {DiagramEventObserverMask, DiagramManager} from "../diagram/diagramManager"; import log, {Logger} from "loglevel"; +import {ascii_to_hex} from "./functions/hexFunctions"; +import {getPath} from "../util/functions/getPath"; const logger: Logger = log.getLogger('PouchdbPersistenceManager'); export class PouchdbPersistenceManager { - updateObserver: Observable = new Observable(); - removeObserver: Observable = new Observable(); + onDBUpdateObservable: Observable = new Observable(); + onDBRemoveObservable: Observable = new Observable(); private db: PouchDB; private remote: PouchDB; @@ -17,54 +19,38 @@ export class PouchdbPersistenceManager { constructor() { } - public setDiagramManager(diagramManager: DiagramManager) { diagramManager.onDiagramEventObservable.add((evt) => { logger.debug(evt); switch (evt.type) { - case DiagramEventType.CHANGECOLOR: - this.changeColor(evt.oldColor, evt.newColor); - break; - case DiagramEventType.ADD: - this.add(evt.entity); - break; case DiagramEventType.REMOVE: this.remove(evt.entity.id); break; + case DiagramEventType.ADD: case DiagramEventType.MODIFY: case DiagramEventType.DROP: - this.modify(evt.entity); + this.upsert(evt.entity); break; default: logger.warn('unknown diagram event type', evt); } - }, 2); - this.updateObserver.add((evt) => { + }, DiagramEventObserverMask.TO_DB); + + this.onDBUpdateObservable.add((evt) => { logger.debug(evt); diagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.ADD, entity: evt - }, 1); + }, DiagramEventObserverMask.FROM_DB); }); - this.removeObserver.add((entity) => { + + this.onDBRemoveObservable.add((entity) => { logger.debug(entity); diagramManager.onDiagramEventObservable.notifyObservers( - {type: DiagramEventType.REMOVE, entity: entity}, 1); + {type: DiagramEventType.REMOVE, entity: entity}, DiagramEventObserverMask.FROM_DB); }); } - public async add(entity: DiagramEntity) { - if (!entity) { - return; - } - const newEntity = {...entity, _id: entity.id}; - try { - this.db.put(newEntity); - } catch (err) { - logger.error(err); - } - } - public async remove(id: string) { if (!id) { return; @@ -77,78 +63,62 @@ export class PouchdbPersistenceManager { } } - public async modify(entity: DiagramEntity) { + public async upsert(entity: DiagramEntity) { if (!entity) { return; } try { const doc = await this.db.get(entity.id); - const newDoc = {...doc, ...entity}; - this.db.put(newDoc); - + if (doc) { + const newDoc = {...doc, ...entity}; + this.db.put(newDoc); + } } catch (err) { - logger.error(err); + if (err.status == 404) { + try { + const newEntity = {...entity, _id: entity.id}; + this.db.put(newEntity); + } catch (err2) { + logger.error(err2); + } + } else { + logger.error(err); + } } } - public async getNewRelicData(): Promise { - return []; - } - - public async setNewRelicData(data: any[]): Promise { - return data; - } - public async initialize() { + if (!await this.initLocal()) { + return; + } + await this.sendLocalDataToScene(); + } + + private async initLocal(): Promise { try { - let current = this.getPath(); - - if (current) { - this.db = new PouchDB(current); - } else { - current = 'public'; - this.db = new PouchDB(current); - } - + let current = getPath() || 'public'; + this.db = new PouchDB(current); await this.beginSync(current); - + return true; } catch (err) { logger.error(err); logger.error('cannot initialize pouchdb for sync'); - return; + return false; } + } + + private async sendLocalDataToScene() { try { const all = await this.db.allDocs({include_docs: true}); for (const entity of all.rows) { logger.debug(entity.doc); - this.updateObserver.notifyObservers(entity.doc, 1); + this.onDBUpdateObservable.notifyObservers(entity.doc, 1); } } catch (err) { logger.error(err); } - } - private getPath(): string { - const path = window.location.pathname.split('/'); - if (path.length == 3 && path[1]) { - return path[2]; - } else { - return null; - } - } - - - async changeColor(oldColor: Color3, newColor: Color3) { - const all = await this.db.allDocs({include_docs: true}); - for (const entity of all.rows) { - logger.debug(`comparing ${entity.doc.color} to ${oldColor.toHexString()}`); - if (entity.doc.color == oldColor.toHexString()) { - entity.doc.color = newColor.toHexString(); - this.db.put({...entity.doc, _rev: entity.doc._rev}); - } - } - } sync() { @@ -162,20 +132,17 @@ export class PouchdbPersistenceManager { logger.debug(doc); if (doc._deleted) { logger.debug('Delete', doc); - this.removeObserver.notifyObservers({id: doc._id, template: doc.template}, 1); + this.onDBRemoveObservable.notifyObservers({id: doc._id, template: doc.template}, 1); } else { - this.updateObserver.notifyObservers(doc, 1); + this.onDBUpdateObservable.notifyObservers(doc, 1); } } } - } private async beginSync(localName: string) { try { - //const remoteDbName = "db1"; - const userHex = ascii_to_hex(localName); const remoteDbName = 'userdb-' + userHex; const remoteUserName = localName; @@ -185,7 +152,6 @@ export class PouchdbPersistenceManager { if (dbs.data.indexOf(remoteDbName) == -1) { logger.warn('sync target missing attempting to create'); const newdb = await axios.post(import.meta.env.VITE_CREATE_ENDPOINT, - { "_id": "org.couchdb.user:" + localName, "name": localName, @@ -229,7 +195,6 @@ export class PouchdbPersistenceManager { {auth: {username: remoteUserName, password: password}, skip_setup: true}); const dbInfo = await this.remote.info(); logger.debug(dbInfo); - this.db.sync(this.remote, {live: true, retry: true}) .on('change', (info) => { this.syncDoc(info) @@ -249,20 +214,3 @@ export class PouchdbPersistenceManager { } } -function hex_to_ascii(input) { - var hex = input.toString(); - let output = ''; - for (let n = 0; n < hex.length; n += 2) { - output += String.fromCharCode(parseInt(hex.substr(n, 2), 16)); - } - return output; -} - -function ascii_to_hex(str) { - const arr1 = []; - for (let n = 0, l = str.length; n < l; n++) { - var hex = Number(str.charCodeAt(n)).toString(16); - arr1.push(hex); - } - return arr1.join(''); -} \ No newline at end of file diff --git a/src/menus/clickMenu.ts b/src/menus/clickMenu.ts index aac6c23..aa56074 100644 --- a/src/menus/clickMenu.ts +++ b/src/menus/clickMenu.ts @@ -1,7 +1,7 @@ import {AbstractMesh, Scene, TransformNode, Vector3} from "@babylonjs/core"; import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity"; import {toDiagramEntity} from "../diagram/functions/toDiagramEntity"; -import {DiagramManager} from "../diagram/diagramManager"; +import {DiagramEventObserverMask, DiagramManager} from "../diagram/diagramManager"; import {DiagramConnection} from "../diagram/diagramConnection"; import {isDiagramEntity} from "../diagram/functions/isDiagramEntity"; import {HtmlButton} from "babylon-html"; @@ -27,7 +27,7 @@ export class ClickMenu { entity: toDiagramEntity(this._mesh) } - this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); + this.diagramManager.onDiagramEventObservable.notifyObservers(event, DiagramEventObserverMask.ALL); this.dispose(); } }, -1, false, this, false); @@ -88,7 +88,7 @@ export class ClickMenu { this.diagramManager.onDiagramEventObservable.notifyObservers({ type: DiagramEventType.ADD, entity: toDiagramEntity(this.connection.mesh) - }, -1); + }, DiagramEventObserverMask.ALL); this.connection = null; this.dispose(); } diff --git a/src/util/functions/getPath.ts b/src/util/functions/getPath.ts new file mode 100644 index 0000000..d27f5af --- /dev/null +++ b/src/util/functions/getPath.ts @@ -0,0 +1,8 @@ +export function getPath(): string { + const path = window.location.pathname.split('/'); + if (path.length == 3 && path[1]) { + return path[2]; + } else { + return null; + } +} \ No newline at end of file