diff --git a/src/menus/ScaleMenu2.ts b/src/menus/ScaleMenu2.ts deleted file mode 100644 index 6b1cde1..0000000 --- a/src/menus/ScaleMenu2.ts +++ /dev/null @@ -1,80 +0,0 @@ -import {AbstractMesh, GizmoManager, IAxisScaleGizmo, Observable} from "@babylonjs/core"; -import {DefaultScene} from "../defaultScene"; -import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity"; -import {toDiagramEntity} from "../diagram/functions/toDiagramEntity"; - - -export class ScaleMenu2 { - private readonly _gizmoManager: GizmoManager; - private readonly _notifier: Observable; - - constructor(notifier: Observable) { - this._notifier = notifier; - this._gizmoManager = new GizmoManager(DefaultScene.Scene); - this._gizmoManager.positionGizmoEnabled = false; - this._gizmoManager.rotationGizmoEnabled = false; - this._gizmoManager.scaleGizmoEnabled = true; - this._gizmoManager.boundingBoxGizmoEnabled = false; - this._gizmoManager.usePointerToAttachGizmos = false; - configureGizmo(this._gizmoManager.gizmos.scaleGizmo.yGizmo); - configureGizmo(this._gizmoManager.gizmos.scaleGizmo.xGizmo); - configureGizmo(this._gizmoManager.gizmos.scaleGizmo.zGizmo); - this._gizmoManager.gizmos.scaleGizmo.onDragEndObservable.add(() => { - if (this.mesh.scaling.x < .01) { - this.mesh.scaling.x = .01; - } - if (this.mesh.scaling.y < .01) { - this.mesh.scaling.y = .01; - } - if (this.mesh.scaling.z < .01) { - this.mesh.scaling.z = .01; - } - - const entity = toDiagramEntity(this.mesh); - this._notifier.notifyObservers({type: DiagramEventType.MODIFY, entity: entity}); - }); - - } - - public get mesh() { - return this._gizmoManager.attachedMesh; - } - - public get gizmoManager() { - return this._gizmoManager; - } - - public show(mesh: AbstractMesh) { - if (mesh.metadata.image) { - configureImageScale(this._gizmoManager.gizmos.scaleGizmo.yGizmo, true); - configureImageScale(this._gizmoManager.gizmos.scaleGizmo.xGizmo, true); - configureImageScale(this._gizmoManager.gizmos.scaleGizmo.zGizmo, false); - } - this._gizmoManager.attachToMesh(mesh); - } - - public hide() { - this._gizmoManager.attachToMesh(null); - } - -} - -function configureGizmo(gizmo: IAxisScaleGizmo) { - gizmo.snapDistance = .1; - gizmo.uniformScaling = false; - gizmo.scaleRatio = 3; - gizmo.sensitivity = 3; - - // Disable automatic pointer-based drag, we'll control it manually via squeeze button - // This prevents conflicts with trigger button and enables squeeze-based manipulation - gizmo.dragBehavior.startAndReleaseDragOnPointerEvents = false; -} - -function configureImageScale(gizmo: IAxisScaleGizmo, enabled: boolean) { - gizmo.snapDistance = .1; - gizmo.uniformScaling = true; - gizmo.scaleRatio = 3; - gizmo.sensitivity = 3; - gizmo.isEnabled = enabled; - -} \ No newline at end of file diff --git a/src/menus/abstractMenu.ts b/src/menus/abstractMenu.ts deleted file mode 100644 index 2893dd5..0000000 --- a/src/menus/abstractMenu.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {Scene, WebXRDefaultExperience} from "@babylonjs/core"; -import {Handle} from "../objects/handle"; -import {DefaultScene} from "../defaultScene"; - -export abstract class AbstractMenu { - protected handle: Handle; - protected scene: Scene; - protected xr: WebXRDefaultExperience; - - - protected constructor(xr: WebXRDefaultExperience) { - this.scene = DefaultScene.Scene; - this.xr = xr; - } - - public toggle() { - throw new Error("AbstractMenu.toggle() not implemented"); - } -} \ No newline at end of file diff --git a/src/menus/configMenu.ts b/src/menus/configMenu.ts deleted file mode 100644 index 28cc849..0000000 --- a/src/menus/configMenu.ts +++ /dev/null @@ -1,195 +0,0 @@ -import {AdvancedDynamicTexture, CheckboxGroup, RadioGroup, SelectionPanel, StackPanel} from "@babylonjs/gui"; -import {MeshBuilder, Scene, TransformNode, Vector3} from "@babylonjs/core"; -import {AppConfig} from "../util/appConfig"; -import log from "loglevel"; -import {DefaultScene} from "../defaultScene"; -import {Handle} from "../objects/handle"; - - -export class ConfigMenu { - private logger = log.getLogger('ConfigMenu'); - private config: AppConfig; - private readonly baseTransform: TransformNode; - private gridSnaps: Array<{ label: string, value: number }> = [ - {label: "Off", value: 0}, - {label: "0.01", value: 0.01}, - {label: "0.1", value: 0.1}, - {label: "0.5", value: 0.5}, - {label: "1", value: 1}, - ] - private _handle: Handle; - private rotationSnaps: Array<{ label: string, value: number }> = [ - {label: "Off", value: 0}, - {label: "22.5", value: 22.5}, - {label: "45", value: 45}, - {label: "90", value: 90}, - ] - private readonly _scene: Scene; - - constructor(config: AppConfig) { - this._scene = DefaultScene.Scene; - this.baseTransform = new TransformNode("configMenuBase", this._scene); - - this._handle = new Handle(this.baseTransform, 'Configuration'); - this.config = config; - this.buildMenu(); - - } - - public get handleTransformNode(): TransformNode { - return this._handle.transformNode; - } - - private adjustRadio(radio: RadioGroup | CheckboxGroup) { - radio.groupPanel.height = "512px"; - radio.groupPanel.background = "#cccccc"; - radio.groupPanel.color = "#000000"; - radio.groupPanel.fontSize = "64px"; - radio.groupPanel.children[0].height = "70px"; - radio.groupPanel.paddingLeft = "16px"; - radio.selectors.forEach((panel) => { - panel.children[0].height = "64px"; - panel.children[0].width = "64px"; - panel.children[1].paddingLeft = "32px"; - panel.paddingTop = "16px"; - panel.fontSize = "60px"; - panel.adaptHeightToChildren = true; - }); - } - - private buildCreateScaleControl(selectionPanel: SelectionPanel): RadioGroup { - const radio = new RadioGroup("Create Scale"); - selectionPanel.addGroup(radio); - for (const [index, snap] of this.gridSnaps.entries()) { - const selected = (this.config.current.createSnap == snap.value); - this.logger.debug(selected); - radio.addRadio(snap.label, this.createVal.bind(this), selected); - } - this.adjustRadio(radio); - return radio; - } - - private buildFlyModeControl(selectionPanel: SelectionPanel): CheckboxGroup { - const checkbox = new CheckboxGroup("Fly Mode"); - selectionPanel.addGroup(checkbox); - checkbox.addCheckbox("Fly", this.flyMode.bind(this), this.config.current.flyMode); - this.adjustRadio(checkbox); - return checkbox; - } - - private buildRotationSnapControl(selectionPanel: SelectionPanel): RadioGroup { - const radio = new RadioGroup("Rotation Snap"); - selectionPanel.addGroup(radio); - for (const [index, snap] of this.rotationSnaps.entries()) { - const selected = (this.config.current.rotateSnap == snap.value); - radio.addRadio(snap.label, this.rotateVal.bind(this), selected); - } - this.adjustRadio(radio); - return radio; - } - - private buildGridSizeControl(selectionPanel: SelectionPanel): RadioGroup { - const radio = new RadioGroup("Grid Snap"); - selectionPanel.addGroup(radio); - for (const [index, snap] of this.gridSnaps.entries()) { - const selected = (this.config.current.locationSnap == snap.value); - radio.addRadio(snap.label, this.gridVal.bind(this), selected); - } - this.adjustRadio(radio); - return radio; - } - - private buildTurnSnapControl(selectionPanel: SelectionPanel): RadioGroup { - const radio = new RadioGroup("Turn Snap"); - selectionPanel.addGroup(radio); - for (const [index, snap] of this.rotationSnaps.entries()) { - const selected = (this.config.current.turnSnap == snap.value); - radio.addRadio(snap.label, this.turnVal.bind(this), selected); - } - this.adjustRadio(radio); - return radio; - } - - private createVal(value) { - this.config.setCreateSnap(this.gridSnaps[value].value); - } - - private flyMode(value) { - this.config.setFlyMode(value); - } - - private rotateVal(value) { - this.config.setRotateSnap(this.rotationSnaps[value].value); - } - - private turnVal(value) { - this.config.setTurnSnap(this.rotationSnaps[value].value); - } - - private gridVal(value) { - this.config.setGridSnap(this.gridSnaps[value].value); - } - - private buildMenu() { - const configPlane = MeshBuilder - .CreatePlane("configMenuPlane", - { - width: .6, - height: .3 - }, this._scene); - configPlane.parent = this.baseTransform; - //this.createHandle(this.baseTransform, new Vector3(1, 1.6, .5), new Vector3(Math.PI / 4, Math.PI / 4, 0)); - configPlane.position.y = .2; - const configTexture = AdvancedDynamicTexture.CreateForMesh(configPlane, 2048, 1024); - const columnPanel = new StackPanel('columns'); - columnPanel.isVertical = false; - columnPanel.fontSize = "48px"; - configTexture.addControl(columnPanel); - const selectionPanel1 = new SelectionPanel("selectionPanel1"); - selectionPanel1.width = "500px"; - - columnPanel.addControl(selectionPanel1); - this.buildGridSizeControl(selectionPanel1); - this.buildCreateScaleControl(selectionPanel1); - - const selectionPanel2 = new SelectionPanel("selectionPanel2"); - selectionPanel2.width = "500px"; - - columnPanel.addControl(selectionPanel2); - this.buildRotationSnapControl(selectionPanel2); - this.buildTurnSnapControl(selectionPanel2); - - const selectionPanel3 = new SelectionPanel("selectionPanel3"); - selectionPanel3.width = "768px"; - columnPanel.addControl(selectionPanel3); - this.buildFlyModeControl(selectionPanel3); - const offset = new Vector3(.50, 1.6, .38); - const rotation = new Vector3(.5, .6, 0); - - const platform = this._scene.getMeshById('platform'); - if (platform) { - this._handle.transformNode.parent = platform; - if (!this._handle.idStored) { - this._handle.transformNode.position = offset; - this._handle.transformNode.rotation = rotation; - } - - } else { - const handler = this._scene.onNewMeshAddedObservable.add((mesh) => { - if (mesh && mesh.id == 'platform') { - this._handle.transformNode.parent = mesh; - - if (!this._handle.idStored) { - this._handle.transformNode.position = offset; - this._handle.transformNode.rotation = rotation; - } - - - //this._scene.onNewMeshAddedObservable.remove(handler); - } - }, -1, true, this); - } - this.baseTransform.parent.setEnabled(true); - } - -} \ No newline at end of file diff --git a/src/menus/functions/makeButton.ts b/src/menus/functions/makeButton.ts deleted file mode 100644 index 86d10c3..0000000 --- a/src/menus/functions/makeButton.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Button3D, TextBlock} from "@babylonjs/gui"; -import {Vector3} from "@babylonjs/core"; - -export function makeButton(id: string, name: string): Button3D { - const button = new Button3D(name); - button.scaling = new Vector3(.1, .1, .1); - button.name = id; - const text = new TextBlock(name, name); - text.fontSize = "48px"; - text.color = "#ffffee"; - text.alpha = 1; - button.content = text; - return button; -} \ No newline at end of file diff --git a/src/menus/vrConfigPanel.ts b/src/menus/vrConfigPanel.ts index 07aace1..8a4f39c 100644 --- a/src/menus/vrConfigPanel.ts +++ b/src/menus/vrConfigPanel.ts @@ -79,6 +79,9 @@ export class VRConfigPanel { // Create base transform for the entire panel hierarchy this._baseTransform = new TransformNode("vrConfigPanelBase", this._scene); + // Scale down to match toolbox compact size (makes 2m×1.5m panel → 1.2m×0.9m) + this._baseTransform.scaling = new Vector3(0.6, 0.6, 0.6); + // Create handle for grabbing (Handle will become parent of baseTransform) this._handle = new Handle( this._baseTransform, @@ -182,8 +185,14 @@ export class VRConfigPanel { // Parent to base transform this._panelMesh.parent = this._baseTransform; - // Position slightly forward and up from handle - this._panelMesh.position = new Vector3(0, 0.2, 0); + // Calculate position to place panel bottom just above handle + // Panel is 1.5m tall, so center needs to be at half-height + small gap above handle + const panelHeight = 1.5; + const gapAboveHandle = 0.05; // 5cm gap above handle for spacing + const panelCenterY = (panelHeight / 2) + gapAboveHandle; // 0.75 + 0.05 = 0.8m + + // Position panel so bottom edge sits just above handle, matching toolbox appearance + this._panelMesh.position = new Vector3(0, panelCenterY, 0); // Create material for panel backing const material = new StandardMaterial("vrConfigPanelMaterial", this._scene); diff --git a/src/util/appConfig.ts b/src/util/appConfig.ts index f768fd4..42fab9c 100644 --- a/src/util/appConfig.ts +++ b/src/util/appConfig.ts @@ -55,10 +55,6 @@ export class AppConfig { this.save(); } - public setCreateSnap(value: number) { - this._currentConfig.createSnap = value; - this.save(); - } public setTurnSnap(value: number) { this._currentConfig.turnSnap = value; @@ -70,11 +66,6 @@ export class AppConfig { this.save(); } - public setPhysicsEnabled(physicsEnabled: boolean) { - this._currentConfig.physicsEnabled = physicsEnabled; - this.save(); - } - public setLabelRenderingMode(mode: LabelRenderingMode) { this._currentConfig.labelRenderingMode = mode; this.save(); diff --git a/src/util/appConfigType.ts b/src/util/appConfigType.ts index 0c5718a..325a434 100644 --- a/src/util/appConfigType.ts +++ b/src/util/appConfigType.ts @@ -1,5 +1,12 @@ +import {Quaternion, Vector3} from "@babylonjs/core"; + export type LabelRenderingMode = 'fixed' | 'billboard' | 'dynamic' | 'distance'; +export type MenuConfig = { + position: Vector3, + quarternion: Quaternion, + scale: Vector3 +} export type AppConfigType = { id?: number, currentDiagramId?: string, @@ -13,5 +20,7 @@ export type AppConfigType = { passphrase?: string, flyMode?: boolean, labelRenderingMode?: LabelRenderingMode, - + toolbox?: MenuConfig, + configMenu?: MenuConfig, + keyboard?: MenuConfig } \ No newline at end of file