Refactored diagram manager and loggers.
This commit is contained in:
parent
fc588c5dbe
commit
d87d8bace4
23
src/app.ts
23
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();
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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']);
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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<DiagramEvent> = new Observable();
|
||||
public readonly onDiagramEventObservable: Observable<DiagramEvent> = 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;
|
||||
|
||||
@ -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<DiagramEntity> = new Observable<DiagramEntity>();
|
||||
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 = <any>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 = <any>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);
|
||||
}
|
||||
}
|
||||
@ -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<DiagramEntity>;
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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),
|
||||
});
|
||||
|
||||
@ -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<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
|
||||
return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[];
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user