From 6ec28efe789e84833d18e6fe3ec2dff68b07a0f7 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Thu, 30 May 2024 09:22:32 -0500 Subject: [PATCH] Updated way that connections are previewed. --- src/controllers/base.ts | 8 +- src/diagram/diagramConnection.ts | 205 ------------------------------ src/diagram/diagramManager.ts | 2 - src/diagram/diagramMenuManager.ts | 16 ++- src/menus/clickMenu.ts | 50 +------- src/menus/connectionPreview.ts | 76 +++++++++++ 6 files changed, 98 insertions(+), 259 deletions(-) delete mode 100644 src/diagram/diagramConnection.ts create mode 100644 src/menus/connectionPreview.ts diff --git a/src/controllers/base.ts b/src/controllers/base.ts index c51cae7..b3aee53 100644 --- a/src/controllers/base.ts +++ b/src/controllers/base.ts @@ -211,11 +211,9 @@ export class Base { let mesh = this.xr.pointerSelection.getMeshUnderPointer(this.xrInputSource.uniqueId); if (this.diagramManager.isDiagramObject(mesh)) { this.logger.debug("click on " + mesh.id); - if (this.clickMenu && !this.clickMenu.isDisposed) { - if (this.clickMenu.isConnecting) { - this.clickMenu.connect(mesh); - this.clickMenu = null; - } + if (this.diagramManager.diagramMenuManager.connectionPreview) { + this.diagramManager.diagramMenuManager.connect(mesh); + } else { this.clickMenu = this.diagramManager.diagramMenuManager.createClickMenu(mesh, this.xrInputSource); } diff --git a/src/diagram/diagramConnection.ts b/src/diagram/diagramConnection.ts deleted file mode 100644 index a545846..0000000 --- a/src/diagram/diagramConnection.ts +++ /dev/null @@ -1,205 +0,0 @@ -import {AbstractMesh, MeshBuilder, Scene, StandardMaterial, TransformNode, Vector3} from "@babylonjs/core"; -import {v4 as uuidv4} from 'uuid'; -import log, {Logger} from "loglevel"; -import {buildStandardMaterial} from "../materials/functions/buildStandardMaterial"; - - -export class DiagramConnection { - - private readonly id: string; - private logger: Logger = log.getLogger('DiagramConnection'); - constructor(from: string, to: string, id: string, scene?: Scene, gripTransform?: TransformNode, clickPoint?: Vector3) { - this.logger.debug('buildConnection constructor'); - if (id) { - this.id = id; - } else { - this.id = "connection_" + uuidv4(); - } - - this.scene = scene; - this._to = to; - this._from = from; - - const fromMesh = this.scene.getMeshById(from); - if (fromMesh) { - this.fromAnchor = fromMesh; - } - - const toMesh = this.scene.getMeshById(to); - if (toMesh) { - this.toAnchor = toMesh; - } else { - if (fromMesh) { - const to = new TransformNode(this.id + "_to", this.scene); - to.ignoreNonUniformScaling = true; - to.id = this.id + "_to"; - if (clickPoint) { - to.position = clickPoint.clone(); - } else { - to.position = fromMesh.absolutePosition.clone(); - } - - if (gripTransform) { - to.setParent(gripTransform); - } - - this.toAnchor = to; - } else { - this.logger.info("no fromMesh yet, will build when toMesh is available"); - } - } - this.buildConnection(); - } - - private scene: Scene; - private toAnchor: TransformNode; - private fromAnchor: TransformNode; - private transformNode: TransformNode; - - private _mesh: AbstractMesh; - - public get mesh(): AbstractMesh { - return this._mesh; - } - - private readonly _to: string; - - public get to(): string { - return this?.toAnchor?.id; - } - - public set to(value: string) { - if (!value) { - return; - } - const toAnchor = this.scene.getMeshById(value); - if (this.fromAnchor && toAnchor) { - this.toAnchor.dispose(); - this.toAnchor = toAnchor; - this._mesh.metadata.to = this.to; - this._mesh.metadata.exportable = true; - this._mesh.id = this.id; - this.recalculate(); - this.setPoints(); - } - } - - private readonly _from: string; - - public get from(): string { - return this?.fromAnchor?.id; - } - - private tick: number = 0; - - private recalculate() { - const start = this.fromAnchor?.absolutePosition; - const end = this.toAnchor?.absolutePosition; - if (start && end) { - this.transformNode.position = start.add(end).scale(.5); - this.transformNode.lookAt(end); - this._mesh.rotation.x = Math.PI / 2; - this._mesh.scaling.y = Math.abs(start.subtract(end).length()); - const text = this._mesh.getChildren((node) => { - return node.metadata?.label == true; - }); - if (text && text.length > 0) { - text.forEach((node) => { - const t: AbstractMesh = node as AbstractMesh; - t.scaling.y = 1 / this._mesh.scaling.y; - t.position.x = .05; - t.position.z = .05; - t.position.y = 0; - }); - } - if (this.fromAnchor && (this.fromAnchor as AbstractMesh).material) { - this._mesh.material = (((this.fromAnchor as AbstractMesh).material as StandardMaterial)); - } else { - this._mesh.material = buildStandardMaterial(this.id + "_material", this.scene, "#FFFFFF"); - } - - } - } - - private setPoints() { - - } - - private buildConnection() { - this.logger.debug(`buildConnection from ${this._from} to ${this._to}`); - this._mesh = MeshBuilder.CreateCylinder(this.id + "_connection", {diameter: .025, height: 1}, this.scene); - this.transformNode = new TransformNode(this.id + "_transform", this.scene); - this.transformNode.metadata = {exportable: true}; - this._mesh.setParent(this.transformNode); - this.recalculate(); - this._mesh.id = this.id; - if (!this._mesh.metadata) { - this._mesh.metadata = {template: "#connection-template", from: this._from}; - } else { - this._mesh.metadata.template = "#connection-template"; - this._mesh.metadata.from = this._from; - } - if (this._to) { - this._mesh.metadata.to = this.to; - - } - this._mesh.metadata.exportable = true; - this.setPoints(); - this.scene.onBeforeRenderObservable.add(this.beforeRender, -1, true, this); - this.scene.onNewMeshAddedObservable.add(this.onMeshAdded, -1, true, this); - this.mesh.onDisposeObservable.add(this.removeConnection, -1, true, this); - return; - } - - private beforeRender = () => { - if (this.tick++ % 10 == 0) { - this.logger.trace('recalculating', this.tick); - this.recalculate(); - this.setPoints(); - } - } - private removeConnection = () => { - this.logger.debug("removeConnection"); - this.scene.onBeforeRenderObservable.removeCallback(this.beforeRender); - this._mesh.onDisposeObservable.removeCallback(this.removeConnection); - this.removeObserver(); - if (this.toAnchor) { - this.toAnchor = null; - } - if (this.fromAnchor) { - this.fromAnchor = null; - } - if (this._mesh) { - this._mesh.dispose(); - this._mesh = null; - } - if (this.scene) { - this.scene = null; - } - } - private onMeshAdded = (mesh: AbstractMesh) => { - if (mesh && mesh.id) { - if (!this.toAnchor || !this.fromAnchor) { - if (mesh?.id == this?._to) { - this.logger.debug("Found to anchor"); - this.toAnchor = mesh; - this._mesh.metadata.to = this.to; - } - if (mesh?.id == this?._from) { - this.logger.debug("Found from anchor"); - this.fromAnchor = mesh; - this._mesh.metadata.from = this.from; - } - if (this.toAnchor && this.fromAnchor) { - this.logger.debug(`connection built from ${this._from} to ${this._to}`); - this.removeObserver(); - } - } - } - } - - private removeObserver() { - this.logger.debug("removing observer"); - this.scene.onNewMeshAddedObservable.removeCallback(this.onMeshAdded); - } -} diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 220fc7d..8d0ac99 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -8,7 +8,6 @@ import {DefaultScene} from "../defaultScene"; import {DiagramMenuManager} from "./diagramMenuManager"; import {DiagramEventObserverMask} from "./types/diagramEventObserverMask"; import {DiagramObject} from "../objects/diagramObject"; -import {DiagramConnection} from "./diagramConnection"; export class DiagramManager { @@ -20,7 +19,6 @@ export class DiagramManager { private readonly _diagramMenuManager: DiagramMenuManager; private readonly _scene: Scene; private readonly _diagramObjects: Map = new Map(); - private readonly _diagramConnections: Map = new Map(); constructor() { this._scene = DefaultScene.Scene; diff --git a/src/diagram/diagramMenuManager.ts b/src/diagram/diagramMenuManager.ts index f14a52c..ad3c012 100644 --- a/src/diagram/diagramMenuManager.ts +++ b/src/diagram/diagramMenuManager.ts @@ -10,6 +10,7 @@ import {ClickMenu} from "../menus/clickMenu"; import {ConfigMenu} from "../menus/configMenu"; import {AppConfig} from "../util/appConfig"; import {DiagramEventObserverMask} from "./types/diagramEventObserverMask"; +import {ConnectionPreview} from "../menus/connectionPreview"; export class DiagramMenuManager { @@ -20,6 +21,7 @@ export class DiagramMenuManager { private readonly _inputTextView: InputTextView; private readonly _scene: Scene; private logger = log.getLogger('DiagramMenuManager'); + private _connectionPreview: ConnectionPreview; constructor(notifier: Observable, controllers: Controllers, config: AppConfig) { this._scene = DefaultScene.Scene; @@ -65,12 +67,23 @@ export class DiagramMenuManager { }); } + public get connectionPreview(): ConnectionPreview { + return this._connectionPreview; + } + + public connect(mesh: AbstractMesh) { + if (this._connectionPreview) { + this._connectionPreview.connect(mesh); + this._connectionPreview = null; + } + } + public editText(mesh: AbstractMesh) { this._inputTextView.show(mesh); } public createClickMenu(mesh: AbstractMesh, input: WebXRInputSource): ClickMenu { - const clickMenu = new ClickMenu(mesh, input, this._notifier); + const clickMenu = new ClickMenu(mesh); clickMenu.onClickMenuObservable.add((evt: ActionEvent) => { console.log(evt); switch (evt.source.id) { @@ -81,6 +94,7 @@ export class DiagramMenuManager { this.editText(clickMenu.mesh); break; case "connect": + this._connectionPreview = new ConnectionPreview(clickMenu.mesh.id, input, evt.additionalData.pickedPoint, this._notifier); break; case "size": this.scaleMenu.show(clickMenu.mesh); diff --git a/src/menus/clickMenu.ts b/src/menus/clickMenu.ts index 653a19a..d92657d 100644 --- a/src/menus/clickMenu.ts +++ b/src/menus/clickMenu.ts @@ -1,7 +1,5 @@ -import {AbstractMesh, ActionEvent, Observable, Scene, TransformNode, WebXRInputSource} from "@babylonjs/core"; -import {DiagramEvent, DiagramEventType, DiagramTemplates} from "../diagram/types/diagramEntity"; +import {AbstractMesh, ActionEvent, Observable, Scene, TransformNode} from "@babylonjs/core"; import {HtmlButton} from "babylon-html"; -import {DiagramEventObserverMask} from "../diagram/types/diagramEventObserverMask"; const POINTER_UP = "pointerup"; @@ -9,24 +7,9 @@ export class ClickMenu { private readonly _mesh: AbstractMesh; private readonly transform: TransformNode; public onClickMenuObservable: Observable = new Observable(); - private _diagramEventObservable: Observable; - private connectFromId: string = null; - - private getTransform(input: WebXRInputSource | TransformNode): TransformNode { - if (input == null) return null; - if ('grip' in input) { - return input.grip; - } else { - return input as TransformNode; - } - } - - constructor(mesh: AbstractMesh, input: WebXRInputSource | TransformNode, diagramEventObservable: Observable) { - - const grip: TransformNode = this.getTransform(input); + constructor(mesh: AbstractMesh) { this._mesh = mesh; - this._diagramEventObservable = diagramEventObservable; const scene = mesh.getScene(); this.transform = new TransformNode("transform", scene); let x = -.54 / 2; @@ -51,8 +34,8 @@ export class ClickMenu { this.makeNewButton("Connect", "connect", scene, x += .11) .onPointerObservable.add((eventData) => { if (isUp(eventData)) { - this.connectFromId = this._mesh.id; - //this.createMeshConnection(this._mesh, grip, eventData.additionalData.pickedPoint.clone()); + this.onClickMenuObservable.notifyObservers(eventData); + this.dispose(); } }, -1, false, this, false); @@ -79,31 +62,6 @@ export class ClickMenu { this.transform.rotation.z = 0; } - public get isConnecting() { - return this.connectFromId != null; - } - - public connect(mesh: AbstractMesh) { - if (this.isConnecting) { - if (mesh) { - this._diagramEventObservable.notifyObservers({ - type: DiagramEventType.ADD, - entity: { - from: this.connectFromId, - to: mesh.id, - template: DiagramTemplates.CONNECTION, - color: '#000000' - } - }, DiagramEventObserverMask.ALL); - this.connectFromId = null; - this.dispose(); - } - } - } - public get isDisposed(): boolean { - return this.transform.isDisposed(); - } - public get mesh(): AbstractMesh { return this._mesh; } diff --git a/src/menus/connectionPreview.ts b/src/menus/connectionPreview.ts new file mode 100644 index 0000000..16b32a9 --- /dev/null +++ b/src/menus/connectionPreview.ts @@ -0,0 +1,76 @@ +import { + AbstractMesh, + LinesMesh, + MeshBuilder, + Observable, + Observer, + Scene, + TransformNode, + Vector3, + WebXRInputSource +} from "@babylonjs/core"; +import {DefaultScene} from "../defaultScene"; +import {DiagramEvent, DiagramEventType, DiagramTemplates} from "../diagram/types/diagramEntity"; +import {DiagramEventObserverMask} from "../diagram/types/diagramEventObserverMask"; + +export class ConnectionPreview { + private _fromPoint: Vector3; + private _fromId: string; + private _renderObserver: Observer; + private _line: LinesMesh; + private _parent: TransformNode; + private _transform: TransformNode; + private _options: any; + private _scene: Scene; + private _diagramEventObservable: Observable; + + constructor(fromId: string, input: WebXRInputSource, point: Vector3, diagramEventObservable: Observable) { + this._scene = DefaultScene.Scene; + this._diagramEventObservable = diagramEventObservable; + const fromMesh = this._scene.getMeshById(fromId); + + if (fromMesh) { + this._parent = input.pointer; + this._fromId = fromMesh.id; + this._transform = new TransformNode("transform", this._scene); + this._transform.position = point.clone(); + this._transform.setParent(this._parent); + this._fromPoint = fromMesh.getAbsolutePosition(); + this._options = { + points: [this._fromPoint, this._transform.absolutePosition], + updatable: true, + useAlphaForLines: false, + }; + this._line = MeshBuilder.CreateLines("connectionPreview", this._options, this._scene); + this._options.instance = this._line; + this._renderObserver = this._scene.onBeforeRenderObservable.add(() => { + this._options.points[1] = this._transform.absolutePosition; + this._line = MeshBuilder.CreateLines("connectionPreview", this._options); + }); + } + } + + public dispose() { + this._scene.onBeforeRenderObservable.remove(this._renderObserver); + this._parent = null; + this._transform.dispose(); + this._line.dispose(); + } + + public connect(mesh: AbstractMesh) { + if (mesh) { + this._diagramEventObservable.notifyObservers({ + type: DiagramEventType.ADD, + entity: { + from: this._fromId, + to: mesh.id, + template: DiagramTemplates.CONNECTION, + color: '#000000' + } + }, DiagramEventObserverMask.ALL); + this.dispose(); + } + + } + +} \ No newline at end of file