From ceaf6c4fb5280fd43f48ad1af1ccc97029162800 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Thu, 17 Aug 2023 09:15:51 -0500 Subject: [PATCH] Committed new Relic Starting point. Committed new Relic Starting point. --- package-lock.json | 23 +++ package.json | 1 + src/app.ts | 3 + src/controllers/right.ts | 3 - src/integration/iPersistenceManager.ts | 4 + src/integration/indexdbPersistenceManager.ts | 14 +- src/integration/newRelic/newRelicData.ts | 189 ++++++++++++++++++- src/menus/baseMenu.ts | 15 ++ src/menus/configMenu.ts | 10 +- src/menus/editMenu.ts | 139 ++++++++------ src/menus/integrationMenu.ts | 35 ++++ src/util/appConfig.ts | 26 ++- src/util/appConfigType.ts | 5 +- 13 files changed, 393 insertions(+), 74 deletions(-) create mode 100644 src/menus/baseMenu.ts create mode 100644 src/menus/integrationMenu.ts diff --git a/package-lock.json b/package-lock.json index 1ab8696..f69728e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "earcut": "^2.2.4", "loglevel": "^1.8.1", "mxgraph": "^4.2.2", + "niceware": "^4.0.0", "p2p-data-channel": "^1.10.7", "query-string": "^8.1.0", "recordrtc": "^5.6.2", @@ -1248,6 +1249,11 @@ "node": ">=8" } }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2890,6 +2896,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/niceware": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/niceware/-/niceware-4.0.0.tgz", + "integrity": "sha512-7uFcxHr5zwNZO4NEMwxK0N+GbqVMGp2YLZ8x/0dx/nxiT0qCt1zL3hZfqawQoq4XiP2OWa665Xid0g/LyoMfQA==", + "dependencies": { + "binary-search": "^1.3.6", + "randombytes": "^2.0.6" + } + }, "node_modules/node-fetch": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", @@ -3268,6 +3283,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", diff --git a/package.json b/package.json index ec0230e..abdad3d 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "earcut": "^2.2.4", "loglevel": "^1.8.1", "mxgraph": "^4.2.2", + "niceware": "^4.0.0", "p2p-data-channel": "^1.10.7", "query-string": "^8.1.0", "recordrtc": "^5.6.2", diff --git a/src/app.ts b/src/app.ts index 7f3068a..8640e63 100644 --- a/src/app.ts +++ b/src/app.ts @@ -96,12 +96,15 @@ export class App { const diagramManager = new DiagramManager(scene, xr.baseExperience, controllers); const rig = new Rigplatform(scene, xr, diagramManager, controllers); const toolbox = new Toolbox(scene, xr.baseExperience, diagramManager, controllers); + //const dioManager = new DrawioManager(scene, diagramManager); import ('./integration/indexdbPersistenceManager').then((module) => { const persistenceManager = new module.IndexdbPersistenceManager("diagram"); diagramManager.setPersistenceManager(persistenceManager); AppConfig.config.setPersistenceManager(persistenceManager); persistenceManager.initialize(); + //const newRelicData = new NewRelicData(persistenceManager, scene); + }); }); diff --git a/src/controllers/right.ts b/src/controllers/right.ts index b77b763..ec468b8 100644 --- a/src/controllers/right.ts +++ b/src/controllers/right.ts @@ -3,7 +3,6 @@ import {Scene, Vector3, WebXRControllerComponent, WebXRDefaultExperience, WebXRI import {Controllers} from "./controllers"; import log from "loglevel"; import {DiagramManager} from "../diagram/diagramManager"; -import {NewRelicData} from "../integration/newRelic/newRelicData"; export class Right extends Base { constructor(controller: @@ -22,8 +21,6 @@ export class Right extends Base { if (bbutton) { bbutton.onButtonStateChangedObservable.add((button) => { if (button.pressed) { - const newRelic = new NewRelicData(); - newRelic.getNewRelicData(); this.controllers.controllerObserver.notifyObservers({type: 'b-button', value: button.value}); } }); diff --git a/src/integration/iPersistenceManager.ts b/src/integration/iPersistenceManager.ts index e2ab8d1..b8511ed 100644 --- a/src/integration/iPersistenceManager.ts +++ b/src/integration/iPersistenceManager.ts @@ -27,6 +27,10 @@ export interface IPersistenceManager { addDiagram(diagram: DiagramListing); + getNewRelicData(): Promise; + + setNewRelicData(data: any): Promise; + removeDiagram(diagram: DiagramListing); add(mesh: AbstractMesh); diff --git a/src/integration/indexdbPersistenceManager.ts b/src/integration/indexdbPersistenceManager.ts index 629a35c..6a84075 100644 --- a/src/integration/indexdbPersistenceManager.ts +++ b/src/integration/indexdbPersistenceManager.ts @@ -16,10 +16,11 @@ export class IndexdbPersistenceManager implements IPersistenceManager { constructor(name: string) { this.db = new Dexie(name); - const version = 3; + const version = 5; this.db.version(version).stores({config: "id,gridSnap,rotateSnap,createSnap"}); this.db.version(version).stores({entities: "id,diagramlistingid,position,rotation,last_seen,template,text,scale,color"}); this.db.version(version).stores({diagramlisting: "id,name,description,sharekey"}); + this.db.version(version).stores({newRelicData: "id,priority, incidentId"}); this.logger.debug("IndexdbPersistenceManager constructed"); } @@ -83,6 +84,17 @@ export class IndexdbPersistenceManager implements IPersistenceManager { this.diagramListingObserver.notifyObservers(event); } + public async setNewRelicData(data: any[]) { + this.db["newRelicData"].clear(); + data.forEach((d) => { + this.db["newRelicData"].add(d); + }); + } + + public async getNewRelicData(): Promise { + return this.db["newRelicData"].toArray(); + } + public async initialize() { this.logger.info('initialize', this.db['entities'].length); const configs = await this.db['config'].toArray(); diff --git a/src/integration/newRelic/newRelicData.ts b/src/integration/newRelic/newRelicData.ts index c0b03b3..cbc8b75 100644 --- a/src/integration/newRelic/newRelicData.ts +++ b/src/integration/newRelic/newRelicData.ts @@ -1,18 +1,199 @@ +import {IPersistenceManager} from "../iPersistenceManager"; +import { + AbstractMesh, + Color3, + DynamicTexture, + InstancedMesh, + MeshBuilder, + Scene, + StandardMaterial, + Vector3 +} from "@babylonjs/core"; + export class NewRelicData { - private readonly key: string; - private readonly account: string; + private key: string; + private account: string; + private data: any[]; + private scene: Scene; + private persistenceManager: IPersistenceManager; + private policyLabels: AbstractMesh[] = []; - constructor() { + constructor(persistenceManager: IPersistenceManager, scene: Scene) { + this.persistenceManager = persistenceManager; + this.scene = scene; + this.persistenceManager.getNewRelicData() + .then((data) => { + this.data = data; + this.getNewRelicData().then(() => { + this.drawGraph(); + }); + }); + } + public setCredentials(key: string, account: string) { + this.key = key; + this.account = account; + } + + public async clearData() { + this.data = []; + await this.persistenceManager.setNewRelicData(this.data); } public async getNewRelicData() { - try { + if (this.data && this.data.length > 0) { + console.warn("Already have data, early return"); + return; + } + try { + const res = await fetch('https://deepdiagram.com/.netlify/functions/nerdgraph', { + method: 'POST', + credentials: 'include', + body: '{"query": "{actor { nrql(query: \\"select * from NrAiIncident \\", accounts: ' + this.account + ') { results } } }"}', + headers: {"Api-Key": this.key} + }); + const data = await res.json(); + if (data?.data?.actor?.nrql?.results) { + const newdata = data.data.actor.nrql.results.map((item: any) => { + item.id = item.incidentId; + item.policyName = item.policyName ? item.policyName : "No Policy"; + return item + }); + await this.persistenceManager.setNewRelicData(newdata); + this.data = newdata; + } + console.log(JSON.stringify(data, null, 2)); } catch (err) { console.log(err); } } + public drawGraph() { + this.data.sort((a, b) => { + return parseInt(a.openTime) - parseInt(b.openTime); + }); + + const duration = this.data[this.data.length - 1].openTime - this.data[0].openTime; + console.log(duration); + const interval = 10 / duration; + const first = parseInt(this.data[0].openTime); + const material = new StandardMaterial("material", this.scene); + material.diffuseColor = new Color3(0, 0, .7); + material.alpha = .3; + const baseMesh = MeshBuilder.CreateBox("baseItem", {width: 1, height: 1, depth: 1}, this.scene); + baseMesh.material = material; + + const warningMaterial = new StandardMaterial("warningMaterial", this.scene); + warningMaterial.diffuseColor = new Color3(.7, .7, .2); + warningMaterial.alpha = .5; + const warningMesh = MeshBuilder.CreateBox("warningItem", {width: 1, height: 1, depth: 1}, this.scene); + warningMesh.material = warningMaterial; + + const criticalMaterial = new StandardMaterial("criticalMaterial", this.scene); + criticalMaterial.diffuseColor = new Color3(.9, .2, .2); + criticalMaterial.alpha = .7; + const criticalMesh = MeshBuilder.CreateBox("criticalItem", {width: 1, height: 1, depth: 1}, this.scene); + criticalMesh.material = criticalMaterial; + + + const policies: Map = new Map(); + this.data.forEach((item) => { + const policy = item.policyName ? item.policyName : "No Policy"; + let x = 0; + let y: number = 0; + if (policies.has(policy)) { + const value = policies.get(policy); + x = value.x; + y = value.y + .105; + policies.set(policy, {x, y}); + } else { + policies.set(policy, {x: policies.size / 10, y: 0}); + x = policies.get(policy).x; + const policyLabel = this.buildText(policy); + policyLabel.scaling = new Vector3(3, 3, 3); + policyLabel.position = new Vector3(x, .4, 0); + policyLabel.rotation.x = Math.PI / 2; + policyLabel.rotation.y = -Math.PI / 2; + + this.policyLabels.push(policyLabel); + } + const start = parseInt(item.openTime) - first; + let end = duration; + if (item.closeTime) { + end = parseInt(item.closeTime) - first; + } + let box: AbstractMesh; + switch (item.priority) { + case "critical": + box = new InstancedMesh(item.id, criticalMesh); + break; + case "warning": + box = new InstancedMesh(item.id, warningMesh); + break; + default: + box = new InstancedMesh(item.id, baseMesh); + } + + box.position = new Vector3(x, y + .5, (start * interval)); + if (item.closeTime) { + box.scaling = new Vector3(.1, .1, (end - start) * interval); + } else { + box.scaling = new Vector3(.1, .1, .01); + } + box.position.z = box.position.z + box.scaling.z / 2; + + const startLabel = this.buildText(new Date(start + first).toLocaleString()); + startLabel.position = box.position.add(new Vector3(.05, .05, 0)); + startLabel.position.z = (start * interval) - .01; + + const endLabel = this.buildText(new Date(end + first).toLocaleString()); + endLabel.position = box.position.add(new Vector3(.05, .05, 0)); + endLabel.position.z = (end * interval) + .01; + + }); + + this.scene.onBeforeRenderObservable.add(() => { + this.policyLabels.forEach((label) => { + label.position.z = this.scene.activeCamera.globalPosition.z; + }); + }); + } + + private buildText(text: string) { + //Set font + const height = 0.03; + 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, this.scene); + 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 + }, this.scene, false); + const mat = new StandardMaterial("mat", this.scene); + 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}, this.scene); + + plane.material = mat; + + return plane; + } } \ No newline at end of file diff --git a/src/menus/baseMenu.ts b/src/menus/baseMenu.ts new file mode 100644 index 0000000..5c4c9a0 --- /dev/null +++ b/src/menus/baseMenu.ts @@ -0,0 +1,15 @@ +import {Scene, WebXRExperienceHelper} from "@babylonjs/core"; +import {Controllers} from "../controllers/controllers"; + +export class BaseMenu { + protected scene: Scene; + protected xr: WebXRExperienceHelper; + protected controllers: Controllers; + + constructor(scene: Scene, xr: WebXRExperienceHelper, controllers: Controllers) { + this.scene = scene; + this.xr = xr; + this.controllers = controllers; + } + +} \ No newline at end of file diff --git a/src/menus/configMenu.ts b/src/menus/configMenu.ts index c9bcd6a..a7ebcc1 100644 --- a/src/menus/configMenu.ts +++ b/src/menus/configMenu.ts @@ -5,19 +5,15 @@ import log from "loglevel"; import {AppConfig} from "../util/appConfig"; import {Controllers} from "../controllers/controllers"; import {DiaSounds} from "../util/diaSounds"; +import {BaseMenu} from "./baseMenu"; -export class ConfigMenu { - private readonly scene: Scene; - private readonly xr: WebXRExperienceHelper; +export class ConfigMenu extends BaseMenu { private configPlane: AbstractMesh = null; - private controllers: Controllers; private yObserver; constructor(scene: Scene, xr: WebXRExperienceHelper, controllers: Controllers) { - this.scene = scene; - this.xr = xr; - this.controllers = controllers; + super(scene, xr, controllers); if (!this.yObserver) { this.controllers.controllerObserver.add((event) => { if (event.type == 'y-button') { diff --git a/src/menus/editMenu.ts b/src/menus/editMenu.ts index 0b0b703..ec1919a 100644 --- a/src/menus/editMenu.ts +++ b/src/menus/editMenu.ts @@ -11,7 +11,7 @@ import { Vector3, WebXRDefaultExperience } from "@babylonjs/core"; -import {Button3D, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui"; +import {Button3D, GUI3DManager, PlanePanel, TextBlock} from "@babylonjs/gui"; import {DiagramManager} from "../diagram/diagramManager"; import {EditMenuState} from "./editMenuState"; import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity"; @@ -23,6 +23,7 @@ import {CameraHelper} from "../util/cameraHelper"; import {TextLabel} from "../diagram/textLabel"; import {DiagramConnection} from "../diagram/diagramConnection"; import {GLTF2Export} from "@babylonjs/serializers"; +import {AppConfig} from "../util/appConfig"; export class EditMenu { private state: EditMenuState = EditMenuState.NONE; @@ -34,60 +35,8 @@ export class EditMenu { private readonly xr: WebXRDefaultExperience; private readonly diagramManager: DiagramManager; private connection: DiagramConnection = null; - private panel: StackPanel3D; - - constructor(scene: Scene, xr: WebXRDefaultExperience, 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; - this.gizmoManager.gizmos.boundingBoxGizmo.rotationSphereSize = .020; - this.gizmoManager.gizmos.boundingBoxGizmo.scaleDragSpeed = 2; - this.gizmoManager.clearGizmoOnEmptyPointerEvent = true; - this.gizmoManager.usePointerToAttachGizmos = false; - this.manager = new GUI3DManager(this.scene); - const panel = new StackPanel3D(); - - this.manager.addControl(panel); - panel.addControl(this.makeButton("Modify", "modify")); - panel.addControl(this.makeButton("Remove", "remove")); - 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("Recolor", "recolor")); - - //panel.addControl(this.makeButton("Add Ring Cameras", "addRingCameras")); - this.manager.controlScaling = .1; - this.scene.onPointerObservable.add((pointerInfo) => { - switch (pointerInfo.type) { - case PointerEventTypes.POINTERPICK: - 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 { - const tool = pickedMesh?.metadata?.tool; - if (tool) { - this.logger.debug("tool type", tool); - this.paintColor = (pickedMesh.material as StandardMaterial).diffuseColor.toHexString(); - this.logger.debug((pickedMesh.material as StandardMaterial).diffuseColor.toHexString()); - this.logger.debug(pickedMesh.id); - } - - } - } - }); - this.panel = panel; - this.isVisible = false; - } + private panel: PlanePanel; + private buttonMaterial: StandardMaterial; private get isVisible(): boolean { return this.panel.isVisible; @@ -128,13 +77,70 @@ export class EditMenu { }); } + constructor(scene: Scene, xr: WebXRDefaultExperience, 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; + this.gizmoManager.gizmos.boundingBoxGizmo.rotationSphereSize = .020; + this.gizmoManager.gizmos.boundingBoxGizmo.scaleDragSpeed = 2; + this.gizmoManager.clearGizmoOnEmptyPointerEvent = true; + this.gizmoManager.usePointerToAttachGizmos = false; + this.manager = new GUI3DManager(this.scene); + const panel = new PlanePanel(); + panel.columns = 4; + this.manager.addControl(panel); + this.buttonMaterial = new StandardMaterial("buttonMaterial", this.scene); + this.buttonMaterial.diffuseColor = Color3.FromHexString("#000000"); + panel.addControl(this.makeButton("Modify", "modify")); + panel.addControl(this.makeButton("Remove", "remove")); + 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("Recolor", "recolor")); + panel.addControl(this.makeButton("New Relic", "newrelic")); + + //panel.addControl(this.makeButton("Add Ring Cameras", "addRingCameras")); + this.manager.controlScaling = .1; + this.scene.onPointerObservable.add((pointerInfo) => { + switch (pointerInfo.type) { + case PointerEventTypes.POINTERPICK: + 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 { + const tool = pickedMesh?.metadata?.tool; + if (tool) { + this.logger.debug("tool type", tool); + this.paintColor = (pickedMesh.material as StandardMaterial).diffuseColor.toHexString(); + this.logger.debug((pickedMesh.material as StandardMaterial).diffuseColor.toHexString()); + this.logger.debug(pickedMesh.id); + } + + } + } + }); + this.panel = panel; + this.isVisible = false; + } + makeButton(name: string, id: string) { const button = new Button3D(name); button.scaling = new Vector3(.1, .1, .1); button.name = id; const text = new TextBlock(name, name); - text.fontSize = "24px"; - text.color = "white"; + text.fontSize = "48px"; + text.color = "#ffffff"; + text.alpha = 1; button.content = text; button.onPointerClickObservable.add(this.handleClick, -1, false, this); return button; @@ -250,6 +256,22 @@ export class EditMenu { } + private showNewRelic() { + const inputTextView = new InputTextView({xr: this.xr, scene: this.scene, text: "New Relic"}); + inputTextView.show(); + inputTextView.onTextObservable.addOnce((value) => { + console.log(value.text); + AppConfig.config.newRelicKey = value.text; + inputTextView.show(); + inputTextView.onTextObservable.addOnce((value) => { + console.log(value.text); + AppConfig.config.newRelicAccount = value.text; + }); + }); + + + } + private handleClick(_info, state) { switch (state.currentTarget.name) { case "modify": @@ -270,6 +292,9 @@ export class EditMenu { case "recolor": this.state = EditMenuState.RECOLORING; break; + case "newrelic": + this.showNewRelic(); + break; case "export": GLTF2Export.GLTFAsync(this.scene, 'diagram.gltf', { shouldExportNode: function (node) { diff --git a/src/menus/integrationMenu.ts b/src/menus/integrationMenu.ts new file mode 100644 index 0000000..5683112 --- /dev/null +++ b/src/menus/integrationMenu.ts @@ -0,0 +1,35 @@ +import {AbstractMesh, MeshBuilder, Scene, WebXRExperienceHelper} from "@babylonjs/core"; +import {Controllers} from "../controllers/controllers"; +import {BaseMenu} from "./baseMenu"; +import {AdvancedDynamicTexture, Grid, TextBlock} from "@babylonjs/gui"; +import {CameraHelper} from "../util/cameraHelper"; + +export class IntegrationMenu extends BaseMenu { + private plane: AbstractMesh = null; + + constructor(scene: Scene, xr: WebXRExperienceHelper, controllers: Controllers) { + super(scene, xr, controllers); + this.buildMenu(); + } + + public toggle() { + this.plane.isVisible = !this.plane.isVisible; + } + + private buildMenu() { + this.plane = MeshBuilder.CreatePlane("plane", {size: 1}, this.scene); + const advancedTexture2 = AdvancedDynamicTexture.CreateForMesh(this.plane, 1024, 1024, false); + + const grid = new Grid("grid"); + advancedTexture2.addControl(grid); + grid.addColumnDefinition(.25); + grid.addColumnDefinition(.75); + const labelText1 = new TextBlock("labelText1", "New Relic Key"); + grid.addControl(labelText1, 0, 0); + const labelText2 = new TextBlock("labelText1", "New Relic Account"); + grid.addControl(labelText2, 1, 0); + CameraHelper.setMenuPosition(this.plane, this.scene); + } + + +} \ No newline at end of file diff --git a/src/util/appConfig.ts b/src/util/appConfig.ts index 35a0ada..731b272 100644 --- a/src/util/appConfig.ts +++ b/src/util/appConfig.ts @@ -16,6 +16,12 @@ export class AppConfig { private _turnSnap = 0; private rotateSnap = 0; private createSnap = 0; + private _newRelicKey: string = null; + + public get newRelicKey(): string { + return this._newRelicKey; + } + _physicsEnabled = false; private readonly defaultGridSnapIndex = 1; private persistenceManager: IPersistenceManager = null; @@ -46,6 +52,22 @@ export class AppConfig { return this.gridSnapArray[this.gridSnap]; } + public set newRelicKey(val: string) { + this._newRelicKey = val; + this.save(); + } + + private _newRelicAccount: string = null; + + public get newRelicAccount(): string { + return this._newRelicAccount; + } + + public set newRelicAccount(val: string) { + this._newRelicAccount = val; + this.save(); + } + public get physicsEnabled(): boolean { return this._physicsEnabled; } @@ -173,7 +195,9 @@ export class AppConfig { rotateSnap: this.currentRotateSnap.value, createSnap: this.currentCreateSnap.value, turnSnap: this.currentTurnSnap.value, - physicsEnabled: this._physicsEnabled + physicsEnabled: this._physicsEnabled, + newRelicKey: this._newRelicKey, + newRelicAccount: this._newRelicAccount }); } diff --git a/src/util/appConfigType.ts b/src/util/appConfigType.ts index 99d24ef..06202a0 100644 --- a/src/util/appConfigType.ts +++ b/src/util/appConfigType.ts @@ -4,5 +4,8 @@ export type AppConfigType = { rotateSnap: number, createSnap: number, turnSnap: number, - physicsEnabled: boolean + physicsEnabled: boolean, + newRelicKey: string, + newRelicAccount: string, + } \ No newline at end of file