Added diagram import from drawio.

This commit is contained in:
Michael Mainguy 2023-08-07 15:47:49 -05:00
parent a7997608c2
commit c62aa59923
11 changed files with 382 additions and 41 deletions

View File

@ -26,6 +26,7 @@
</head>
<body>
<div class="loader">Loading...</div>
<div id="graphContainer"></div>
<script type="module" src="./src/app.ts"></script>
</body>
</html>

45
package-lock.json generated
View File

@ -8,14 +8,17 @@
"name": "immersive",
"version": "0.0.1",
"dependencies": {
"@babylonjs/core": "^6.14.0",
"@babylonjs/gui": "^6.14.0",
"@babylonjs/core": "^6.15.0",
"@babylonjs/gui": "^6.15.0",
"@babylonjs/havok": "1.1.1",
"@babylonjs/inspector": "^6.14.0",
"@babylonjs/inspector": "^6.15.0",
"@babylonjs/serializers": "^6.15.0",
"@typed-mxgraph/typed-mxgraph": "^1.0.8",
"dexie": "^3.2.4",
"dexie-observable": "^4.0.1-beta.13",
"earcut": "^2.2.4",
"loglevel": "^1.8.1",
"mxgraph": "^4.2.2",
"p2p-data-channel": "^1.10.7",
"query-string": "^8.1.0",
"ring-client-api": "11.7.7",
@ -41,14 +44,14 @@
}
},
"node_modules/@babylonjs/core": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.14.0.tgz",
"integrity": "sha512-ciIfWMMtV5jsnqxqTn+v/CS65yji6CXTP2drmvLlzk+k+IZjE8RfkpMqZgZozN/KNkOmIVn2Li7qRMjg4ZUGlw=="
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.15.0.tgz",
"integrity": "sha512-eoeJ3RB6xYC63ZuGGuceF7rrVA1OFIlO3cvBvAxsoBIzxTSLxyhmG4wwktEvCAePxZ7W7CDYNtA061edGWhxUw=="
},
"node_modules/@babylonjs/gui": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.14.0.tgz",
"integrity": "sha512-rkqPEVDaeza4agyd5xSLSjnWgI1spqcNI/kmNke4gREmweEcZZFxNxjGQd5m/JQMbx7qrj9jtEp8COZ7wNSiWw==",
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.15.0.tgz",
"integrity": "sha512-WkR/r8wLbKyzUuXUgcWVaTZO5drxQ8YjkstDRz/ZrhfOTIEFDpidd7onDhaQ9wmekQEpFHcKJejJ1/MLXYLwAg==",
"peerDependencies": {
"@babylonjs/core": "^6.0.0"
}
@ -74,9 +77,9 @@
}
},
"node_modules/@babylonjs/inspector": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.14.0.tgz",
"integrity": "sha512-8wntNbVb9Yhj0q8Ll4kFGDDJPHk7IeosqVwXTPToTfDYC67pwkVQFXFViEXr1R0HrG0l1FmZzPVLY6szbFSwrw==",
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.15.0.tgz",
"integrity": "sha512-wN1jVC+gxQDcLL/dyuIMbedPZpxTHNW9gto82jvgyjXLIUKOWiQUVJS7moaLhTM27BMWBrHMfG+K4RnsnKEW9A==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-regular-svg-icons": "^6.0.0",
@ -113,10 +116,9 @@
}
},
"node_modules/@babylonjs/serializers": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-6.8.0.tgz",
"integrity": "sha512-/deOpTX3Lnll3PM9Um7l+e33mVPe65BgAOlgskrcH6XmT5BaltedYwQktTyblrZs8nQEs90elPpdOb0bsoY4HA==",
"peer": true,
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-6.15.0.tgz",
"integrity": "sha512-qUKqMe18wlEQpyh5cAa/u/B4xMZ2tKh5K1CurnN9TXaM6i8wImRL4mmXqlwy/Tq18zUGmfmPyU1p1qPuD1ytcg==",
"peerDependencies": {
"@babylonjs/core": "^6.0.0",
"babylonjs-gltf2interface": "^6.0.0"
@ -901,6 +903,11 @@
"node": ">=10"
}
},
"node_modules/@typed-mxgraph/typed-mxgraph": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@typed-mxgraph/typed-mxgraph/-/typed-mxgraph-1.0.8.tgz",
"integrity": "sha512-rzTbmD/XofRq0YZMY/BU9cjbCTw9q8rpIvWRhQO0DcgCx3+rpHTsVOk3pfuhcnUigUYNFkljmDkRuVjbl7zZoQ=="
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
@ -2826,6 +2833,12 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
"node_modules/mxgraph": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/mxgraph/-/mxgraph-4.2.2.tgz",
"integrity": "sha512-FrJc5AxzXSqiQNF+8CyJk6VxuKO4UVPgw32FZuFZ3X9W+JqOAQBTokZhh0ZkEqGpEOyp3z778ssmBTvdrTAdqw==",
"deprecated": "Package no longer supported. Use at your own risk"
},
"node_modules/nano-time": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",

View File

@ -11,15 +11,18 @@
"havok": "cp ./node_modules/@babylonjs/havok/lib/esm/HavokPhysics.wasm ./node_modules/.vite/deps"
},
"dependencies": {
"@babylonjs/core": "^6.14.0",
"@babylonjs/gui": "^6.14.0",
"@babylonjs/core": "^6.15.0",
"@babylonjs/gui": "^6.15.0",
"@babylonjs/havok": "1.1.1",
"@babylonjs/inspector": "^6.14.0",
"@babylonjs/inspector": "^6.15.0",
"@babylonjs/serializers": "^6.15.0",
"ring-client-api": "11.7.7",
"dexie": "^3.2.4",
"dexie-observable": "^4.0.1-beta.13",
"query-string": "^8.1.0",
"loglevel": "^1.8.1",
"mxgraph": "^4.2.2",
"@typed-mxgraph/typed-mxgraph": "^1.0.8",
"round": "^2.0.1",
"earcut": "^2.2.4",
"uuid": "^9.0.0",

View File

@ -17,18 +17,22 @@ import {PeerjsNetworkConnection} from "./integration/peerjsNetworkConnection";
import {InputTextView} from "./information/inputTextView";
import {GamepadManager} from "./controllers/gamepadManager";
import {CustomEnvironment} from "./util/customEnvironment";
import {DrawioManager} from "./integration/drawioManager";
export class App {
//preTasks = [havokModule];
constructor() {
const config = AppConfig.config;
const logger = log.getLogger('App');
log.setLevel('info');
log.getLogger('App').setLevel('info');
log.getLogger('IndexdbPersistenceManager').setLevel('info');
log.getLogger('DiagramManager').setLevel('info');
log.disableAll();
log.setDefaultLevel('info');
log.getLogger('DiagramConnection').setLevel('debug');
log.getLogger('App').setLevel('info');
//log.getLogger('IndexdbPersistenceManager').setLevel('info');
//log.getLogger('DiagramManager').setLevel('info');
//log.getLogger('DiagramConnection').setLevel('debug');
log.getLogger('DrawioManager').setLevel('debug');
const canvas = document.createElement("canvas");
canvas.style.width = "100%";
canvas.style.height = "100%";
@ -41,6 +45,7 @@ export class App {
}
async initialize(canvas) {
const logger = log.getLogger('App');
const engine = new Engine(canvas, true);
const scene = new Scene(engine);
@ -69,6 +74,7 @@ export class App {
camera.radius = 0;
camera.attachControl(canvas, true);
new HemisphericLight("light1", new Vector3(1, 1, 0), scene);
environment.groundMeshObservable.add(async (ground) => {
const xr = await WebXRDefaultExperience.CreateAsync(scene, {
floorMeshes: [ground],
@ -99,6 +105,7 @@ export class App {
const diagramManager = new DiagramManager(scene, xr.baseExperience);
const rig = new Rigplatform(scene, xr, diagramManager);
const toolbox = new Toolbox(scene, xr.baseExperience, diagramManager);
const dioManager = new DrawioManager(scene, diagramManager);
import ('./integration/indexdbPersistenceManager').then((module) => {
const persistenceManager = new module.IndexdbPersistenceManager("diagram");
diagramManager.setPersistenceManager(persistenceManager);

View File

@ -20,6 +20,7 @@ import {EditMenu} from "../menus/editMenu";
import {Controllers} from "./controllers";
import log from "loglevel";
import {DiagramManager} from "../diagram/diagramManager";
import {AppConfig} from "../util/appConfig";
export class Rigplatform {
@ -35,6 +36,7 @@ export class Rigplatform {
private camera: Camera;
private turning: boolean = false;
private velocity: Vector3 = Vector3.Zero();
private turnVelocity: number = 0;
private logger = log.getLogger('Rigplatform');
private readonly diagramManager: DiagramManager;
@ -92,12 +94,13 @@ export class Rigplatform {
this.velocity.y = (val * this.velocityArray[this.velocityIndex])*-1;
}
public turn(val: number) {
const snap = true;
if (snap) {
const snap = AppConfig.config.currentTurnSnap.value;
if (snap > 0) {
if (!this.turning) {
if (Math.abs(val) > .1) {
this.turning = true;
this.yRotation += Angle.FromDegrees(Math.sign(val) * 22.5).radians();
this.yRotation += Angle.FromDegrees(Math.sign(val) * snap).radians();
}
} else {
if (Math.abs(val) < .1) {
@ -106,9 +109,9 @@ export class Rigplatform {
}
} else {
if (Math.abs(val) > .1) {
this.body.setAngularVelocity(Vector3.Up().scale(val));
this.turnVelocity = val;
} else {
this.body.setAngularVelocity(Vector3.Zero());
this.turnVelocity = 0;
}
}
}
@ -169,6 +172,7 @@ export class Rigplatform {
}
private fixRotation() {
this.scene.registerBeforeRender(() => {
if (AppConfig?.config?.currentTurnSnap?.value > 0) {
const q = this.rigMesh.rotationQuaternion;
this.body.setAngularVelocity(Vector3.Zero());
if (q) {
@ -176,6 +180,10 @@ export class Rigplatform {
e.y += this.yRotation;
q.copyFrom(Quaternion.FromEulerAngles(0, e.y, 0));
}
} else {
this.body.setAngularVelocity(Vector3.Up().scale(this.turnVelocity));
}
});
}
}

View File

@ -136,6 +136,20 @@ export class DiagramManager {
if (entity) {
mesh = this.scene.getMeshById(entity.id);
}
if (!mesh) {
const toolMesh = this.scene.getMeshById("tool-" + event.entity.template + "-" + event.entity.color);
if (!toolMesh) {
log.debug('no mesh found for ' + event.entity.template + "-" + event.entity.color, 'adding it');
this.onDiagramEventObservable.notifyObservers({
type: DiagramEventType.CHANGECOLOR,
entity: event.entity
});
}
mesh = MeshConverter.fromDiagramEntity(event.entity, this.scene);
mesh.actionManager = this.actionManager;
DiagramShapePhysics.applyPhysics(mesh, this.scene, PhysicsMotionType.DYNAMIC);
}
switch (event.type) {
case DiagramEventType.CLEAR:
break;

View File

@ -0,0 +1,235 @@
import log from "loglevel";
import {
AbstractMesh,
Axis,
Color3,
DynamicTexture,
MeshBuilder,
Scene,
Space,
StandardMaterial,
TransformNode,
Vector3
} from "@babylonjs/core";
import {DiagramManager} from "../diagram/diagramManager";
import {DiagramEventType} from "../diagram/diagramEntity";
export class DrawioManager {
private diagramManager: DiagramManager;
private readonly zdepth: Map<string, number> = new Map<string, number>();
private scene: Scene;
private readonly logger = log.getLogger('DrawioManager');
private minY = 0;
private minX = 0;
private maxX = 0;
private maxY = 0;
constructor(scene: Scene, diagramManager: DiagramManager) {
this.scene = scene;
this.diagramManager = diagramManager;
this.getGraph();
}
public static updateTextNode(mesh: AbstractMesh, text: string): AbstractMesh {
//Set font
const height = 0.1;
const font_size = 24;
const font = "bold " + font_size + "px Arial";
//Set height for dynamic texture
const DTHeight = 1.5 * font_size; //or set as wished
//Calc Ratio
const ratio = height / DTHeight;
//Use a temporary dynamic texture to calculate the length of the text on the dynamic texture canvas
const temp = new DynamicTexture("DynamicTexture", 32, mesh.getScene());
const tmpctx = temp.getContext();
tmpctx.font = font;
const DTWidth = tmpctx.measureText(text).width + 8;
//Calculate width the plane has to be
const planeWidth = DTWidth * ratio;
//Create dynamic texture and write the text
const dynamicTexture = new DynamicTexture("DynamicTexture", {
width: DTWidth,
height: DTHeight
}, mesh.getScene(), false);
const mat = new StandardMaterial("mat", mesh.getScene());
mat.diffuseTexture = dynamicTexture;
dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
//Create plane and set dynamic texture as material
const plane = MeshBuilder.CreatePlane("text", {width: planeWidth, height: height}, mesh.getScene());
plane.material = mat;
//plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
plane.parent = mesh;
plane.position.z = .51;
plane.scaling.y = 1 / mesh.scaling.y;
plane.scaling.x = 1 / mesh.scaling.x;
plane.scaling.z = 1 / mesh.scaling.z;
plane.rotate(Axis.Z, Math.PI, Space.LOCAL);
plane.rotate(Axis.Y, Math.PI, Space.LOCAL);
//plane.rotate(Axis.X, Math.PI, Space.LOCAL);
return plane;
}
private async getGraph() {
this.logger.debug("starting to get graph");
const entities: Array<{ text: string, id: string, geometry: { x: number, y: number, width: number, height: number } }>
= new Array<{ text: string; id: string, geometry: { x: number; y: number; width: number; height: number } }>();
const graph = await fetch('/arch_demo.xml');
this.logger.debug('got graph');
const graphXml = await graph.text();
const doc = new DOMParser().parseFromString(graphXml, 'text/html');
//this.logger.debug(doc);
const firstDiagram = doc.querySelectorAll('diagram')[0];
const mxDiagram = firstDiagram.querySelector('mxGraphModel');
const width = mxDiagram.getAttribute('pageWidth');
const height = mxDiagram.getAttribute('pageHeight');
this.logger.debug('begin parse');
mxDiagram.querySelectorAll('mxCell').forEach((cell) => {
const value = cell.getAttribute('value');
if (!value) {
//this.logger.warn('no value for :' , cell);
} else {
const ent = new DOMParser().parseFromString(value, 'text/html');
const errorNode = ent.querySelector("parsererror");
this.logger.debug(value);
if (errorNode) {
//this.logger.debug(value);
} else {
const text = this.getText(ent, '');
const id = cell.getAttribute('id');
const parent = cell.getAttribute('parent');
if (this.zdepth.has(parent)) {
this.zdepth.set(id, this.zdepth.get(parent) + .2);
} else {
this.zdepth.set(cell.getAttribute('id'), 0);
}
const geo = cell.querySelector('mxGeometry');
const geometry = {
x: geo.getAttribute('x'),
y: geo.getAttribute('y'),
width: geo.getAttribute('width'),
height: geo.getAttribute('height'),
}
entities.push({text: text, id: id, geometry: this.fixMinMax(geometry)});
if (text) {
this.logger.debug('Text' + text);
this.logger.debug('Geometry' + JSON.stringify(geometry));
}
}
}
});
this.logger.debug('done parsing');
this.logger.debug('MinX' + this.minX);
this.logger.debug('MinY' + this.minY);
this.logger.debug('MaxX' + this.maxX);
this.logger.debug('MaxY' + this.maxY);
const diagramWidth = this.maxX - this.minX;
const diagramHeight = this.maxY - this.minY;
let scale = 1;
if (diagramHeight > diagramWidth) {
scale = 20 / diagramHeight;
} else {
scale = 20 / diagramWidth;
}
const anchor = new TransformNode('anchor', this.scene);
if (entities.length > 0) {
//const basebox = MeshBuilder.CreateBox("box", {
// height: 1,
// width: 1, depth: 1
//}, this.scene);
//const material: StandardMaterial = new StandardMaterial("mat", this.scene);
//material.diffuseColor = new Color3(0.5, 0.5, 0.5);
//material.alpha = .4;
//basebox.material = material;
entities.forEach((entity) => {
/* const box = new InstancedMesh("box", basebox);
box.scaling.z = .1;
box.scaling.y = entity.geometry.height * scale;
box.scaling.x = entity.geometry.width * scale;
box.position.x = (entity.geometry.x - this.minX) * scale + (entity.geometry.width * scale / 2);
box.position.y = (entity.geometry.y - this.minY) * scale + (entity.geometry.height * scale / 2);
box.position.z = 2 + this.zdepth.get(entity.id); */
this.diagramManager.onDiagramEventObservable.notifyObservers(
{
type: DiagramEventType.ADD,
entity: {
text: entity.text,
id: entity.id,
position: new Vector3((entity.geometry.x - this.minX) * scale + (entity.geometry.width * scale / 2),
(entity.geometry.y - this.minY) * scale + (entity.geometry.height * scale / 2),
2 + this.zdepth.get(entity.id)),
scale: new Vector3(entity.geometry.width * scale, entity.geometry.height * scale, .1),
color: Color3.Blue().toHexString(),
template: '#box-template'
}
}
);
//box.metadata = {text: entity.text};
//box.setParent(anchor);
//DrawioManager.updateTextNode(box, entity.text);
});
anchor.position.y = 20;
anchor.rotation.x = Math.PI;
}
this.logger.debug('Scale' + scale);
}
private fixMinMax(geometry: { x: string; y: string; width: string; height: string; }):
{ x: number, y: number, width: number, height: number } {
let x = 0;
if (geometry.x) {
x = parseFloat(geometry.x);
if (x < this.minX) {
this.minX = x;
}
if (x > this.maxX) {
this.maxX = x;
}
}
let y = 0;
if (geometry.y) {
y = parseFloat(geometry.y);
if (y < this.minY) {
this.minY = y;
}
if (y > this.maxY) {
this.maxY = y;
}
}
return ({x: x, y: y, width: parseFloat(geometry.width), height: parseFloat(geometry.height)});
}
private getText(obj: Node, text: string): string {
if (obj.nodeType == Node.TEXT_NODE) {
if (obj.textContent) {
return obj.textContent.trim();
} else {
return '';
}
} else {
if (obj.childNodes) {
let t = '';
obj.childNodes.forEach((child) => {
t += ' ' + this.getText(child, '');
});
return text.trim() + ' ' + t.trim();
} else {
return '';
}
}
}
}

View File

@ -30,22 +30,24 @@ export class ConfigMenu {
}
DiaSounds.instance.enter.play();
const width = .25;
const height = .55;
const height = .75;
const res = 256;
const heightPixels = Math.round((height / width) * res);
this.configPlane = MeshBuilder
.CreatePlane("gridSizePlane",
{
width: .25,
height: .5
height: .75
}, this.scene);
const configTexture = AdvancedDynamicTexture.CreateForMesh(this.configPlane, res, heightPixels);
configTexture.background = "white";
const selectionPanel = new SelectionPanel("selectionPanel");
configTexture.addControl(selectionPanel)
this.buildGridSizeControl(selectionPanel);
this.buildRotationSnapControl(selectionPanel);
this.buildCreateScaleControl(selectionPanel);
this.buildRotationSnapControl(selectionPanel);
this.buildTurnSnapControl(selectionPanel);
CameraHelper.setMenuPosition(this.configPlane, this.scene);
}
@ -85,11 +87,26 @@ export class ConfigMenu {
return radio;
}
private buildTurnSnapControl(selectionPanel: SelectionPanel): RadioGroup {
const radio = new RadioGroup("Turn Snap");
selectionPanel.addGroup(radio);
for (const [index, snap] of AppConfig.config.turnSnaps().entries()) {
const selected = AppConfig.config.currentTurnSnapIndex == index;
radio.addRadio(snap.label, this.turnVal, selected);
}
return radio;
}
private rotateVal(value) {
AppConfig.config.currentRotateSnapIndex = value;
log.debug("configMenu", "rotate Snap", value);
}
private turnVal(value) {
AppConfig.config.currentTurnSnapIndex = value;
log.debug("configMenu", "turn Snap", value);
}
private gridVal(value) {
AppConfig.config.currentGridSnapIndex = value;
log.debug("configMenu", "grid Snap", value);

View File

@ -18,6 +18,7 @@ import {DiaSounds} from "../util/diaSounds";
import {CameraHelper} from "../util/cameraHelper";
import {TextLabel} from "../diagram/textLabel";
import {DiagramConnection} from "../diagram/diagramConnection";
import {GLTF2Export} from "@babylonjs/serializers";
export class EditMenu {
private state: EditMenuState = EditMenuState.NONE;
@ -44,14 +45,17 @@ export class EditMenu {
this.scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case PointerEventTypes.POINTERPICK:
if (pointerInfo.pickInfo?.pickedMesh?.metadata?.template &&
pointerInfo.pickInfo?.pickedMesh?.parent?.parent?.id != "toolbox") {
const pickedMesh = pointerInfo.pickInfo?.pickedMesh;
if (pickedMesh.metadata?.template &&
pickedMesh?.parent?.parent?.id != "toolbox") {
this.diagramEntityPicked(pointerInfo).then(() => {
this.logger.debug("handled");
}).catch((e) => {
this.logger.error(e);
});
break;
} else {
}
}
});
@ -72,6 +76,7 @@ export class EditMenu {
panel.addControl(this.makeButton("Add Label", "label"));
panel.addControl(this.makeButton("Copy", "copy"));
panel.addControl(this.makeButton("Connect", "connect"));
panel.addControl(this.makeButton("Export", "export"));
//panel.addControl(this.makeButton("Add Ring Cameras", "addRingCameras"));
this.manager.controlScaling = .5;
@ -211,9 +216,23 @@ export class EditMenu {
case "connect":
this.state = EditMenuState.CONNECTING;
break;
case "export":
GLTF2Export.GLTFAsync(this.scene, 'diagram.gltf', {
shouldExportNode: function (node) {
if (node?.metadata?.template) {
return true;
} else {
return false;
}
}
}).then((gltf) => {
gltf.downloadFiles();
});
default:
this.logger.error("Unknown button");
return;
}
this.manager.dispose();
this.manager = null;

View File

@ -13,6 +13,7 @@ export type SnapValue = {
export class AppConfig {
private readonly logger = log.getLogger('AppConfig');
private gridSnap = 1;
private _turnSnap = 0;
private rotateSnap = 0;
private createSnap = 0;
_physicsEnabled = false;
@ -35,6 +36,11 @@ export class AppConfig {
{value: 22.5, label: "22.5 Degrees"},
{value: 45, label: "45 Degrees"},
{value: 90, label: "90 Degrees"}];
private turnSnapArray: 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];
@ -66,10 +72,19 @@ export class AppConfig {
return this.createSnapArray[this.createSnap];
}
public get currentTurnSnap(): SnapValue {
return this.turnSnapArray[this._turnSnap];
}
public get currentGridSnapIndex(): number {
return this.gridSnap;
}
public set currentTurnSnapIndex(val: number) {
this._turnSnap = val;
this.save();
}
public set currentGridSnapIndex(val: number) {
this.gridSnap = val;
this.save();
@ -110,6 +125,10 @@ export class AppConfig {
return this.gridSnapArray;
}
public turnSnaps(): SnapValue[] {
return this.turnSnapArray;
}
public createSnaps(): SnapValue[] {
return this.createSnapArray;
}
@ -153,6 +172,7 @@ export class AppConfig {
gridSnap: this.currentGridSnap.value,
rotateSnap: this.currentRotateSnap.value,
createSnap: this.currentCreateSnap.value,
turnSnap: this.currentTurnSnap.value,
physicsEnabled: this._physicsEnabled
});
}
@ -167,7 +187,10 @@ export class AppConfig {
config.gridSnap != this.currentGridSnap.value ||
config.rotateSnap != this.currentRotateSnap.value) {
this.logger.debug("Config changed", config);
this._turnSnap = this.turnSnapArray.findIndex((snap) => snap.value == config.turnSnap);
if (!this._turnSnap || this._turnSnap == -1) {
this._turnSnap = 0;
}
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);

View File

@ -3,5 +3,6 @@ export type AppConfigType = {
gridSnap: number,
rotateSnap: number,
createSnap: number,
turnSnap: number,
physicsEnabled: boolean
}