Refactored some larger classses...added enhanced map capability.
This commit is contained in:
parent
7806760153
commit
7315e3397a
@ -18,6 +18,9 @@
|
||||
<link href="/manifest.webmanifest" rel="manifest"/>
|
||||
<!--<script src='/niceware.js'></script>-->
|
||||
<style>
|
||||
#feed {
|
||||
display: none;
|
||||
}
|
||||
#keyboardHelp {
|
||||
display: none;
|
||||
width: 665px;
|
||||
@ -81,6 +84,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!--<video id="feed" controls="" autoplay="" name="media"><source src="https://listen.broadcastify.com/1drb2xhywkg8nvz.mp3?nc=49099&xan=xtf9912b41c" type="audio/mpeg"></video> -->
|
||||
<div class="scene">
|
||||
<canvas id="gameCanvas"></canvas>
|
||||
</div>
|
||||
|
||||
@ -8,6 +8,7 @@ import {DefaultScene} from "../defaultScene";
|
||||
import {DiagramMenuManager} from "./diagramMenuManager";
|
||||
import {DiagramEventObserverMask} from "./types/diagramEventObserverMask";
|
||||
import {DiagramObject} from "./diagramObject";
|
||||
import {getMe} from "../util/me";
|
||||
|
||||
|
||||
export class DiagramManager {
|
||||
@ -19,8 +20,9 @@ export class DiagramManager {
|
||||
private readonly _diagramMenuManager: DiagramMenuManager;
|
||||
private readonly _scene: Scene;
|
||||
private readonly _diagramObjects: Map<string, DiagramObject> = new Map<string, DiagramObject>();
|
||||
|
||||
private readonly _me: string;
|
||||
constructor() {
|
||||
this._me = getMe();
|
||||
this._scene = DefaultScene.Scene;
|
||||
this._config = new AppConfig();
|
||||
this._controllers = new Controllers();
|
||||
@ -104,6 +106,8 @@ export class DiagramManager {
|
||||
}
|
||||
if (diagramObject) {
|
||||
this._diagramObjects.set(event.entity.id, diagramObject);
|
||||
} else {
|
||||
this._logger.error('failed to create diagram object for ', event.entity);
|
||||
}
|
||||
break;
|
||||
case DiagramEventType.REMOVE:
|
||||
|
||||
@ -11,6 +11,7 @@ import {AppConfig} from "../util/appConfig";
|
||||
import {DiagramEventObserverMask} from "./types/diagramEventObserverMask";
|
||||
import {ConnectionPreview} from "../menus/connectionPreview";
|
||||
import {ScaleMenu2} from "../menus/ScaleMenu2";
|
||||
import {CameraMenu} from "../menus/cameraMenu";
|
||||
|
||||
|
||||
export class DiagramMenuManager {
|
||||
@ -20,11 +21,14 @@ export class DiagramMenuManager {
|
||||
private readonly _notifier: Observable<DiagramEvent>;
|
||||
private readonly _inputTextView: InputTextView;
|
||||
private readonly _scene: Scene;
|
||||
private _cameraMenu: CameraMenu;
|
||||
private logger = log.getLogger('DiagramMenuManager');
|
||||
private _connectionPreview: ConnectionPreview;
|
||||
|
||||
constructor(notifier: Observable<DiagramEvent>, controllers: Controllers, config: AppConfig) {
|
||||
this._scene = DefaultScene.Scene;
|
||||
|
||||
|
||||
this._notifier = notifier;
|
||||
this._inputTextView = new InputTextView(controllers);
|
||||
this.configMenu = new ConfigMenu(config);
|
||||
@ -34,12 +38,7 @@ export class DiagramMenuManager {
|
||||
});
|
||||
this.toolbox = new Toolbox();
|
||||
this.scaleMenu = new ScaleMenu2(this._notifier);
|
||||
/*this.scaleMenu.onScaleChangeObservable.add((mesh: AbstractMesh) => {
|
||||
this.notifyAll({type: DiagramEventType.MODIFY, entity: {id: mesh.id, scale: mesh.scaling}});
|
||||
const position = mesh.absolutePosition.clone();
|
||||
position.y = mesh.getBoundingInfo().boundingBox.maximumWorld.y + .1;
|
||||
this.scaleMenu.changePosition(position);
|
||||
});*/
|
||||
|
||||
controllers.controllerObservable.add((event: ControllerEvent) => {
|
||||
if (event.type == ControllerEventType.B_BUTTON) {
|
||||
if (event.value > .8) {
|
||||
|
||||
@ -7,8 +7,7 @@ import {
|
||||
MeshBuilder,
|
||||
Scene,
|
||||
StandardMaterial,
|
||||
Texture,
|
||||
Vector3
|
||||
Texture
|
||||
} from "@babylonjs/core";
|
||||
import log from "loglevel";
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
@ -154,10 +153,6 @@ function mapMetadata(entity: DiagramEntity, newMesh: AbstractMesh, scene: Scene)
|
||||
return newMesh;
|
||||
}
|
||||
|
||||
function xyztovec(xyz: { x, y, z }): Vector3 {
|
||||
return new Vector3(xyz.x, xyz.y, xyz.z);
|
||||
}
|
||||
|
||||
|
||||
export function buildMissingMaterial(name: string, scene: Scene, color: string): StandardMaterial {
|
||||
const existingMaterial = scene.getMaterialById(name);
|
||||
|
||||
@ -55,4 +55,16 @@ export type DiagramEntity = {
|
||||
parent?: string;
|
||||
diagramlistingid?: string;
|
||||
friendly?: string;
|
||||
rightHand?: {
|
||||
position: { x: number, y: number, z: number },
|
||||
rotation: { x: number, y: number, z: number }
|
||||
},
|
||||
leftHand?: {
|
||||
position: { x: number, y: number, z: number },
|
||||
rotation: { x: number, y: number, z: number }
|
||||
},
|
||||
head?: {
|
||||
position: { x: number, y: number, z: number },
|
||||
rotation: { x: number, y: number, z: number }
|
||||
}
|
||||
}
|
||||
27
src/integration/functions/checkDb.ts
Normal file
27
src/integration/functions/checkDb.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import axios from "axios";
|
||||
import log from "loglevel";
|
||||
|
||||
export async function checkDb(localName: string, remoteDbName: string) {
|
||||
const logger = log.getLogger('checkDb');
|
||||
const dbs = await axios.get(import.meta.env.VITE_SYNCDB_ENDPOINT + 'list');
|
||||
logger.debug(dbs.data);
|
||||
if (dbs.data.indexOf(remoteDbName) == -1) {
|
||||
logger.warn('sync target missing attempting to create');
|
||||
const newdb = await axios.post(import.meta.env.VITE_CREATE_ENDPOINT,
|
||||
{
|
||||
"_id": "org.couchdb.user:" + localName,
|
||||
"name": localName,
|
||||
"password": localName,
|
||||
"roles": ["readers"],
|
||||
"type": "user"
|
||||
}
|
||||
);
|
||||
if (newdb.status == 201) {
|
||||
logger.info('sync target created');
|
||||
} else {
|
||||
logger.warn('sync target not created', newdb);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
22
src/integration/functions/syncDoc.ts
Normal file
22
src/integration/functions/syncDoc.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import log from "loglevel";
|
||||
import {DiagramEntity} from "../../diagram/types/diagramEntity";
|
||||
import {Observable} from "@babylonjs/core";
|
||||
|
||||
export function syncDoc(info: any, onDBRemoveObservable: Observable<DiagramEntity>, onDBUpdateObservable: Observable<DiagramEntity>) {
|
||||
const logger = log.getLogger('syncDoc');
|
||||
logger.debug(info);
|
||||
if (info.direction == 'pull') {
|
||||
const docs = info.change.docs;
|
||||
for (const doc of docs) {
|
||||
logger.debug(doc);
|
||||
if (doc._deleted) {
|
||||
logger.debug('Delete', doc);
|
||||
onDBRemoveObservable.notifyObservers({id: doc._id, template: doc.template}, 1);
|
||||
} else {
|
||||
onDBUpdateObservable.notifyObservers(doc, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -7,10 +7,11 @@ import log, {Logger} from "loglevel";
|
||||
import {ascii_to_hex} from "./functions/hexFunctions";
|
||||
import {getPath} from "../util/functions/getPath";
|
||||
import {DiagramEventObserverMask} from "../diagram/types/diagramEventObserverMask";
|
||||
|
||||
import {syncDoc} from "./functions/syncDoc";
|
||||
import {checkDb} from "./functions/checkDb";
|
||||
|
||||
export class PouchdbPersistenceManager {
|
||||
private logger: Logger = log.getLogger('PouchdbPersistenceManager');
|
||||
private _logger: Logger = log.getLogger('PouchdbPersistenceManager');
|
||||
onDBUpdateObservable: Observable<DiagramEntity> = new Observable<DiagramEntity>();
|
||||
onDBRemoveObservable: Observable<DiagramEntity> = new Observable<DiagramEntity>();
|
||||
|
||||
@ -18,12 +19,13 @@ export class PouchdbPersistenceManager {
|
||||
private remote: PouchDB;
|
||||
private user: string;
|
||||
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
public setDiagramManager(diagramManager: DiagramManager) {
|
||||
diagramManager.onDiagramEventObservable.add((evt) => {
|
||||
this.logger.debug(evt);
|
||||
this._logger.debug(evt);
|
||||
switch (evt.type) {
|
||||
case DiagramEventType.REMOVE:
|
||||
this.remove(evt.entity.id);
|
||||
@ -34,23 +36,25 @@ export class PouchdbPersistenceManager {
|
||||
this.upsert(evt.entity);
|
||||
break;
|
||||
default:
|
||||
this.logger.warn('unknown diagram event type', evt);
|
||||
this._logger.warn('unknown diagram event type', evt);
|
||||
}
|
||||
}, DiagramEventObserverMask.TO_DB);
|
||||
|
||||
this.onDBUpdateObservable.add((evt) => {
|
||||
this.logger.debug(evt);
|
||||
if (!evt.friendly) {
|
||||
this._logger.debug(evt);
|
||||
if (evt.id != 'metadata') {
|
||||
diagramManager.onDiagramEventObservable.notifyObservers({
|
||||
type: DiagramEventType.ADD,
|
||||
entity: evt
|
||||
}, DiagramEventObserverMask.FROM_DB);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
this.onDBRemoveObservable.add((entity) => {
|
||||
this.logger.debug(entity);
|
||||
this._logger.debug(entity);
|
||||
diagramManager.onDiagramEventObservable.notifyObservers(
|
||||
{type: DiagramEventType.REMOVE, entity: entity}, DiagramEventObserverMask.FROM_DB);
|
||||
});
|
||||
@ -64,7 +68,7 @@ export class PouchdbPersistenceManager {
|
||||
const doc = await this.db.get(id);
|
||||
this.db.remove(doc);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
this._logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,10 +88,10 @@ export class PouchdbPersistenceManager {
|
||||
const newEntity = {...entity, _id: entity.id};
|
||||
this.db.put(newEntity);
|
||||
} catch (err2) {
|
||||
this.logger.error(err2);
|
||||
this._logger.error(err2);
|
||||
}
|
||||
} else {
|
||||
this.logger.error(err);
|
||||
this._logger.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,22 +109,27 @@ export class PouchdbPersistenceManager {
|
||||
if (doc && doc.friendly) {
|
||||
localStorage.setItem(current, doc.friendly);
|
||||
}
|
||||
if (doc && doc.camera) {
|
||||
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.status == 404) {
|
||||
console.log('no metadata found');
|
||||
this._logger.debug('no metadata found');
|
||||
const friendly = localStorage.getItem(current);
|
||||
if (friendly) {
|
||||
console.log('local friendly name found ', friendly, ' setting metadata');
|
||||
this._logger.debug('local friendly name found ', friendly, ' setting metadata');
|
||||
const newDoc = {_id: 'metadata', friendly: friendly};
|
||||
await this.db.put(newDoc);
|
||||
} else {
|
||||
console.log('no friendly name found');
|
||||
this._logger.debug('no friendly name found');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async initLocal(): Promise<boolean> {
|
||||
try {
|
||||
|
||||
let sync = false;
|
||||
let current = getPath();
|
||||
if (current && current != 'localdb') {
|
||||
@ -136,8 +145,8 @@ export class PouchdbPersistenceManager {
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
this.logger.error('cannot initialize pouchdb for sync');
|
||||
this._logger.error(err);
|
||||
this._logger.error('cannot initialize pouchdb for sync');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -145,13 +154,14 @@ export class PouchdbPersistenceManager {
|
||||
private async sendLocalDataToScene() {
|
||||
const clear = localStorage.getItem('clearLocal');
|
||||
try {
|
||||
|
||||
const all = await this.db.allDocs({include_docs: true});
|
||||
for (const entity of all.rows) {
|
||||
this.logger.debug(entity.doc);
|
||||
this._logger.debug(entity.doc);
|
||||
if (clear) {
|
||||
this.remove(entity.id);
|
||||
} else {
|
||||
this.onDBUpdateObservable.notifyObservers(entity.doc, 1);
|
||||
this.onDBUpdateObservable.notifyObservers(entity.doc, DiagramEventObserverMask.FROM_DB);
|
||||
}
|
||||
|
||||
}
|
||||
@ -159,29 +169,7 @@ export class PouchdbPersistenceManager {
|
||||
localStorage.removeItem('clearLocal');
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sync() {
|
||||
|
||||
}
|
||||
|
||||
private syncDoc(info) {
|
||||
this.logger.debug(info);
|
||||
if (info.direction == 'pull') {
|
||||
const docs = info.change.docs;
|
||||
for (const doc of docs) {
|
||||
this.logger.debug(doc);
|
||||
if (doc._deleted) {
|
||||
this.logger.debug('Delete', doc);
|
||||
this.onDBRemoveObservable.notifyObservers({id: doc._id, template: doc.template}, 1);
|
||||
} else {
|
||||
this.onDBUpdateObservable.notifyObservers(doc, 1);
|
||||
}
|
||||
|
||||
}
|
||||
this._logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,32 +179,17 @@ export class PouchdbPersistenceManager {
|
||||
const remoteDbName = 'userdb-' + userHex;
|
||||
const remoteUserName = localName;
|
||||
const password = localName;
|
||||
const dbs = await axios.get(import.meta.env.VITE_SYNCDB_ENDPOINT + 'list');
|
||||
this.logger.debug(dbs.data);
|
||||
if (dbs.data.indexOf(remoteDbName) == -1) {
|
||||
this.logger.warn('sync target missing attempting to create');
|
||||
const newdb = await axios.post(import.meta.env.VITE_CREATE_ENDPOINT,
|
||||
{
|
||||
"_id": "org.couchdb.user:" + localName,
|
||||
"name": localName,
|
||||
"password": localName,
|
||||
"roles": ["readers"],
|
||||
"type": "user"
|
||||
}
|
||||
);
|
||||
if (newdb.status == 201) {
|
||||
this.logger.info('sync target created');
|
||||
} else {
|
||||
this.logger.warn('sync target not created', newdb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await checkDb(localName, remoteDbName) == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userEndpoint: string = import.meta.env.VITE_USER_ENDPOINT
|
||||
this.logger.debug(userEndpoint);
|
||||
this.logger.debug(remoteDbName);
|
||||
this._logger.debug(userEndpoint);
|
||||
this._logger.debug(remoteDbName);
|
||||
const target = await axios.get(userEndpoint);
|
||||
if (target.status != 200) {
|
||||
this.logger.warn(target.statusText);
|
||||
this._logger.warn(target.statusText);
|
||||
return;
|
||||
}
|
||||
if (target.data && target.data.userCtx) {
|
||||
@ -224,36 +197,36 @@ export class PouchdbPersistenceManager {
|
||||
const buildTarget = await axios.post(userEndpoint,
|
||||
{username: remoteUserName, password: password});
|
||||
if (buildTarget.status != 200) {
|
||||
this.logger.info(buildTarget.statusText);
|
||||
this._logger.info(buildTarget.statusText);
|
||||
return;
|
||||
} else {
|
||||
this.user = buildTarget.data.userCtx;
|
||||
this.logger.debug(this.user);
|
||||
this._logger.debug(this.user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const remoteEndpoint: string = import.meta.env.VITE_SYNCDB_ENDPOINT;
|
||||
this.logger.debug(remoteEndpoint + remoteDbName);
|
||||
this._logger.debug(remoteEndpoint + remoteDbName);
|
||||
this.remote = new PouchDB(remoteEndpoint + remoteDbName,
|
||||
{auth: {username: remoteUserName, password: password}, skip_setup: true});
|
||||
const dbInfo = await this.remote.info();
|
||||
this.logger.debug(dbInfo);
|
||||
this._logger.debug(dbInfo);
|
||||
this.db.sync(this.remote, {live: true, retry: true})
|
||||
.on('change', (info) => {
|
||||
this.syncDoc(info)
|
||||
syncDoc(info, this.onDBRemoveObservable, this.onDBUpdateObservable);
|
||||
})
|
||||
.on('active', (info) => {
|
||||
this.logger.debug('sync active', info)
|
||||
this._logger.debug('sync active', info)
|
||||
})
|
||||
.on('paused', (info) => {
|
||||
this.logger.debug('sync paused', info)
|
||||
this._logger.debug('sync paused', info)
|
||||
})
|
||||
.on('error', (err) => {
|
||||
this.logger.error('sync error', err)
|
||||
this._logger.error('sync error', err)
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
this._logger.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,12 +12,12 @@ import {
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
import {DefaultScene} from "../defaultScene";
|
||||
import {CameraWindow} from "./cameraWindow";
|
||||
import {CameraWindow} from "../objects/cameraWindow";
|
||||
|
||||
export class CameraIcon {
|
||||
private static _baseMesh: AbstractMesh;
|
||||
private readonly _scene: Scene;
|
||||
private _cam: CameraWindow;
|
||||
private _cams: CameraWindow[] = [];
|
||||
|
||||
constructor(scene: Scene, mapNode: TransformNode, position: Vector3) {
|
||||
this._scene = scene;
|
||||
@ -29,14 +29,20 @@ export class CameraIcon {
|
||||
newInstance.position = position;
|
||||
newInstance.actionManager = new ActionManager(this._scene);
|
||||
newInstance.actionManager.registerAction(new ExecuteCodeAction(ActionManager.OnPickTrigger, () => {
|
||||
if (this._cam) {
|
||||
this._cam.dispose();
|
||||
this._cam = null;
|
||||
if (this._cams && this._cams.length > 0) {
|
||||
for (const cam of this._cams) {
|
||||
cam.dispose();
|
||||
}
|
||||
this._cams = [];
|
||||
} else {
|
||||
this._cam = new CameraWindow(scene, null, 'https://cameras.immersiveidea.com/mjpg/video.mjpg?camera=3');
|
||||
this._cam.mesh.position.x = newInstance.absolutePosition.x;
|
||||
this._cam.mesh.position.z = newInstance.absolutePosition.z;
|
||||
this._cam.mesh.position.y = newInstance.absolutePosition.y + .5;
|
||||
for (let i = 1; i < 7; i++) {
|
||||
const cam = new CameraWindow(scene, null, 'https://cameras.immersiveidea.com/mjpg/video.mjpg?camera=' + i);
|
||||
cam.mesh.position.x = newInstance.absolutePosition.x + (-2 + i);
|
||||
cam.mesh.position.z = newInstance.absolutePosition.z;
|
||||
cam.mesh.position.y = newInstance.absolutePosition.y + .5;
|
||||
this._cams.push(cam);
|
||||
}
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
38
src/maps/functions/tileFunctions.ts
Normal file
38
src/maps/functions/tileFunctions.ts
Normal file
@ -0,0 +1,38 @@
|
||||
export function tile2long(x, z) {
|
||||
return (x / Math.pow(2, z) * 360 - 180);
|
||||
}
|
||||
|
||||
export function tile2lat(y, z) {
|
||||
var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
|
||||
return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
|
||||
}
|
||||
|
||||
const EARTH_CIR_METERS = 40075016.686;
|
||||
const TILE_SIZE = 512;
|
||||
const degreesPerMeter = 360 / EARTH_CIR_METERS;
|
||||
const LIMIT_Y = toDegrees(Math.atan(Math.sinh(Math.PI))) // around 85.0511...
|
||||
|
||||
function toRadians(degrees) {
|
||||
return degrees * Math.PI / 180;
|
||||
}
|
||||
|
||||
function toDegrees(radians) {
|
||||
return (radians / Math.PI) * 180
|
||||
}
|
||||
|
||||
|
||||
export function lonOnTile(lon, zoom) {
|
||||
return ((lon + 180) / 360) * Math.pow(2, zoom)
|
||||
}
|
||||
|
||||
export function latOnTile(lat, zoom) {
|
||||
return (
|
||||
((1 -
|
||||
Math.log(
|
||||
Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180)
|
||||
) /
|
||||
Math.PI) /
|
||||
2) *
|
||||
Math.pow(2, zoom)
|
||||
)
|
||||
}
|
||||
@ -1,6 +1,9 @@
|
||||
import * as mapTilerClient from '@maptiler/client';
|
||||
import {
|
||||
AbstractMesh,
|
||||
ActionEvent,
|
||||
ActionManager,
|
||||
ExecuteCodeAction,
|
||||
MeshBuilder,
|
||||
Observable,
|
||||
Scene,
|
||||
@ -8,9 +11,9 @@ import {
|
||||
Texture,
|
||||
TransformNode,
|
||||
Vector2,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
import {CameraIcon} from "./cameraIcon";
|
||||
import log from "loglevel";
|
||||
import {latOnTile, lonOnTile, tile2lat, tile2long} from "./functions/tileFunctions";
|
||||
|
||||
export type MaptilerMapTile = {
|
||||
lat: number,
|
||||
@ -21,22 +24,29 @@ export type MaptilerMapTile = {
|
||||
y: number,
|
||||
bounds: Vector2[];
|
||||
}
|
||||
type PlotPointType = {
|
||||
position: Vector2;
|
||||
mesh: AbstractMesh;
|
||||
}
|
||||
|
||||
|
||||
export class MaptilerMap {
|
||||
public readonly onReadyObservable = new Observable<MaptilerMapTile>();
|
||||
public readonly onPickObservable: Observable<{ lat: number, lon: number }> = new Observable()
|
||||
private readonly _scene: Scene;
|
||||
private readonly _baseNode: TransformNode;
|
||||
private readonly _key: string;
|
||||
|
||||
private _lat: number;
|
||||
private _lon: number;
|
||||
private _min: Vector2;
|
||||
private _max: Vector2;
|
||||
private _zoom: number;
|
||||
//private _bounds: Vector2[];
|
||||
private readonly _scene: Scene;
|
||||
private _tileXYCount: number = 2;
|
||||
private readonly _baseNode: TransformNode;
|
||||
private readonly _key: string;
|
||||
private _pendingPoints: Array<number> = [];
|
||||
private _points: Vector2[] = [];
|
||||
private readonly _logger = log.getLogger('MaptilerMap');
|
||||
private _points: PlotPointType[] = [];
|
||||
private _actionManager: ActionManager;
|
||||
|
||||
public constructor(key: string, scene: Scene, name: string = 'map-node', tileXYCount: number = 2) {
|
||||
this._scene = scene;
|
||||
@ -45,6 +55,16 @@ export class MaptilerMap {
|
||||
this._baseNode = new TransformNode(name, this._scene);
|
||||
this.onReadyObservable.addOnce(this.buildNodes.bind(this));
|
||||
this.onReadyObservable.addOnce(this.waitForMeshAdded.bind(this));
|
||||
this._actionManager = new ActionManager(this._scene);
|
||||
this._actionManager.registerAction(new ExecuteCodeAction({trigger: ActionManager.OnPickDownTrigger},
|
||||
(evt: ActionEvent) => {
|
||||
const coordinates = evt.additionalData.getTextureCoordinates();
|
||||
const tile = evt.meshUnderPointer.metadata.mapTile;
|
||||
const lat = tile2lat(tile.y + (1 - coordinates.y), this._zoom);
|
||||
const lon = tile2long(tile.x + coordinates.x, this._zoom);
|
||||
this.onPickObservable.notifyObservers({lat: lat, lon: lon});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _startFallback: number = 10;
|
||||
@ -80,7 +100,7 @@ export class MaptilerMap {
|
||||
bounds: []
|
||||
});
|
||||
} else {
|
||||
console.error(JSON.stringify(result));
|
||||
this._logger.error(JSON.stringify(result));
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,8 +119,8 @@ export class MaptilerMap {
|
||||
});
|
||||
}
|
||||
|
||||
public async plotPoint(lat: number, lon: number) {
|
||||
const len = this._points.push(new Vector2(lat, lon));
|
||||
public async plotPoint(lat: number, lon: number, mesh: AbstractMesh) {
|
||||
const len = this._points.push({position: new Vector2(lat, lon), mesh: mesh});
|
||||
this._pendingPoints.push(len - 1);
|
||||
}
|
||||
|
||||
@ -113,21 +133,24 @@ export class MaptilerMap {
|
||||
if (this._pendingPoints.length > 0) {
|
||||
this._pendingPoints = this._pendingPoints.filter((item) => {
|
||||
const point = this._points[item];
|
||||
const tileXY = this.getTileXY(point.x, point.y);
|
||||
console.log(tileXY);
|
||||
const tileXY = this.getTileXY(point.position.x, point.position.y);
|
||||
this._logger.log(tileXY);
|
||||
const mesh = this._scene.getMeshByName(`map-${tileXY[0]}-${tileXY[1]}-plane`);
|
||||
const oldPoint = this._scene.getMeshByName(`map-${point.x}-${point.y}-point`);
|
||||
const oldPoint = this._scene.getMeshByName(`map-${point.position.x}-${point.position.y}-point`);
|
||||
if (!mesh) {
|
||||
console.error(`map-${tileXY[0]}-${tileXY[1]}-plane not found`);
|
||||
this._logger.error(`map-${tileXY[0]}-${tileXY[1]}-plane not found`);
|
||||
return true;
|
||||
} else {
|
||||
if (!oldPoint) {
|
||||
const pixelx = lonOnTile(point.y, this._zoom) % 1;
|
||||
const pixely = latOnTile(point.x, this._zoom) % 1;
|
||||
console.log(`pixelx: ${pixelx}, pixely: ${pixely} found`);
|
||||
const pixely = latOnTile(point.position.x, this._zoom) % 1;
|
||||
const pixelx = lonOnTile(point.position.y, this._zoom) % 1;
|
||||
this._logger.log(`pixelx: ${pixelx}, pixely: ${pixely} found`);
|
||||
try {
|
||||
const newIcon = new CameraIcon(this._scene, this._baseNode,
|
||||
new Vector3(mesh.position.x - .5 + pixelx, mesh.position.y + .5 - pixely, mesh.position.z - .05));
|
||||
const pointMesh = point.mesh;
|
||||
pointMesh.parent = this._baseNode;
|
||||
pointMesh.position.x = mesh.position.x - .5 + pixelx;
|
||||
pointMesh.position.y = mesh.position.y + .5 - pixely;
|
||||
pointMesh.position.z = mesh.position.z - .05;
|
||||
return false;
|
||||
} catch (err) {
|
||||
return true;
|
||||
@ -177,22 +200,22 @@ export class MaptilerMap {
|
||||
}
|
||||
|
||||
private buildMapTile(x: number, y: number, url: string, xTile: number, yTile: number): AbstractMesh {
|
||||
const map = MeshBuilder.CreatePlane(`map-${xTile}-${yTile}-plane`, {width: 1, height: 1}, this._scene);
|
||||
const tile = MeshBuilder.CreatePlane(`map-${xTile}-${yTile}-plane`, {width: 1, height: 1}, this._scene);
|
||||
const mapMaterial = new StandardMaterial(`map-${xTile}-${yTile}-material`, this._scene);
|
||||
const mapTexture = new Texture(url, this._scene);
|
||||
const lon = tile2long(xTile, this._zoom);
|
||||
const lat = tile2lat(yTile, this._zoom);
|
||||
if (!this._min || lat < this._min.x || lon < this._min.y) {
|
||||
this._min = new Vector2(lat, lon);
|
||||
console.log(`min: ${lat}, ${lon}`);
|
||||
this._logger.log(`min: ${lat}, ${lon}`);
|
||||
}
|
||||
const maxLat = tile2lat(yTile + 1, this._zoom);
|
||||
const maxLon = tile2long(xTile + 1, this._zoom);
|
||||
if (!this._max || maxLat > this._max.y || maxLon > this._max.y) {
|
||||
this._min = new Vector2(maxLat, maxLon);
|
||||
console.log(`max: ${maxLat}, ${maxLon}`);
|
||||
this._logger.log(`max: ${maxLat}, ${maxLon}`);
|
||||
}
|
||||
map.metadata = {
|
||||
tile.metadata = {
|
||||
mapTile: {x: xTile, y: yTile}, bounds:
|
||||
{
|
||||
topleft:
|
||||
@ -208,49 +231,15 @@ export class MaptilerMap {
|
||||
mapMaterial.emissiveTexture = mapTexture;
|
||||
mapMaterial.disableLighting = true;
|
||||
mapMaterial.backFaceCulling = false;
|
||||
map.material = mapMaterial;
|
||||
map.position.x = x;
|
||||
map.position.y = y;
|
||||
map.isPickable = true;
|
||||
return map;
|
||||
tile.material = mapMaterial;
|
||||
//tile.material.freeze();
|
||||
tile.position.x = x;
|
||||
tile.position.y = y;
|
||||
tile.renderOutline = true;
|
||||
tile.isPickable = true;
|
||||
tile.actionManager = this._actionManager;
|
||||
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
|
||||
function tile2long(x, z) {
|
||||
return (x / Math.pow(2, z) * 360 - 180);
|
||||
}
|
||||
|
||||
function tile2lat(y, z) {
|
||||
var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
|
||||
return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
|
||||
}
|
||||
|
||||
const EARTH_CIR_METERS = 40075016.686;
|
||||
const TILE_SIZE = 512;
|
||||
const degreesPerMeter = 360 / EARTH_CIR_METERS;
|
||||
const LIMIT_Y = toDegrees(Math.atan(Math.sinh(Math.PI))) // around 85.0511...
|
||||
|
||||
function toRadians(degrees) {
|
||||
return degrees * Math.PI / 180;
|
||||
}
|
||||
|
||||
function toDegrees(radians) {
|
||||
return (radians / Math.PI) * 180
|
||||
}
|
||||
|
||||
|
||||
function lonOnTile(lon, zoom) {
|
||||
return ((lon + 180) / 360) * Math.pow(2, zoom)
|
||||
}
|
||||
|
||||
function latOnTile(lat, zoom) {
|
||||
return (
|
||||
((1 -
|
||||
Math.log(
|
||||
Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180)
|
||||
) /
|
||||
Math.PI) /
|
||||
2) *
|
||||
Math.pow(2, zoom)
|
||||
)
|
||||
}
|
||||
@ -2,6 +2,7 @@ import {AbstractMesh, GizmoManager, IAxisScaleGizmo, Observable} from "@babylonj
|
||||
import {DefaultScene} from "../defaultScene";
|
||||
import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity";
|
||||
import {toDiagramEntity} from "../diagram/functions/toDiagramEntity";
|
||||
import {displayDebug} from "../util/displayDebug";
|
||||
|
||||
export class ScaleMenu2 {
|
||||
private readonly _gizmoManager: GizmoManager;
|
||||
@ -28,6 +29,7 @@ export class ScaleMenu2 {
|
||||
if (this.mesh.scaling.z < .01) {
|
||||
this.mesh.scaling.z = .01;
|
||||
}
|
||||
displayDebug([this.mesh.scaling.x.toString(), this.mesh.scaling.y.toString(), this.mesh.scaling.z.toString()])
|
||||
const entity = toDiagramEntity(this.mesh);
|
||||
this._notifier.notifyObservers({type: DiagramEventType.MODIFY, entity: entity});
|
||||
});
|
||||
|
||||
@ -1,102 +1,37 @@
|
||||
import {Color3, DynamicTexture, MeshBuilder, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
|
||||
import {AbstractMesh, Angle, Color3, MeshBuilder, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
|
||||
import log, {Logger} from "loglevel";
|
||||
import {MaptilerMap} from "../objects/maptilerMap";
|
||||
import {MaptilerMap} from "../maps/maptilerMap";
|
||||
|
||||
export class CameraMenu {
|
||||
private readonly scene: Scene;
|
||||
private readonly logger: Logger = log.getLogger('CameraMenu');
|
||||
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
//this.buildMenu(3, new Vector3(-1, 2, 0));
|
||||
//this.buildMenu(4, new Vector3(0,2,0));
|
||||
//this.buildMenu(5, new Vector3(1,2,0));
|
||||
//this.buildMenu(6, new Vector3(2,2,0));
|
||||
//this.buildIcon();
|
||||
//this.loadIcon();
|
||||
private readonly _scene: Scene;
|
||||
private readonly _logger: Logger = log.getLogger('CameraMenu');
|
||||
|
||||
constructor(scene: Scene) {
|
||||
this._scene = scene;
|
||||
this.buildMap();
|
||||
|
||||
}
|
||||
|
||||
private buildMap() {
|
||||
const maptilerMap = new MaptilerMap('YnvhjBiU8oCWP0GXNdHL', this.scene, 'map-node', 3);
|
||||
maptilerMap.node.position.y = 1;
|
||||
const maptilerMap = new MaptilerMap('YnvhjBiU8oCWP0GXNdHL', this._scene, 'map-node', 3);
|
||||
maptilerMap.node.position.y = 3;
|
||||
maptilerMap.node.position.z = -4;
|
||||
maptilerMap.node.rotation.y = Math.PI;
|
||||
maptilerMap.node.rotation.x = Math.PI / 6;
|
||||
maptilerMap.node.rotation.x = Angle.FromDegrees(10).radians()
|
||||
maptilerMap.node.scaling = new Vector3(1, 1, 1);
|
||||
//maptilerMap.setLocation('loves park, il' , 15);
|
||||
maptilerMap.setLocation('rockford, il', 12).then(() => {
|
||||
maptilerMap.plotPoint(42.33181896128866, -88.86844896012006);
|
||||
maptilerMap.setLocation('loves park, il', 16).then(() => {
|
||||
//maptilerMap.plotPoint(42.33181896128866, -88.86844896012006, this.buildPoint());
|
||||
});
|
||||
maptilerMap.onPickObservable.add((evt) => {
|
||||
maptilerMap.plotPoint(evt.lat, evt.lon, this.buildPoint());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//https://maps.geoapify.com/v1/staticmap?style=osm-carto&scaleFactor=2&width=4096&height=4096¢er=lonlat:-89.0940,42.2711&zoom=12.4318&apiKey=d548c5ed24604be6a9dd0d989631f783
|
||||
private buildIcon() {
|
||||
const icon = MeshBuilder.CreatePlane('camera-icon', {width: .1, height: .1}, this.scene);
|
||||
icon.position = new Vector3(0, 3, 0);
|
||||
icon.metadata = {grabbable: true};
|
||||
const material = new StandardMaterial('icon-material', this.scene);
|
||||
|
||||
material.backFaceCulling = false;
|
||||
const texture = new DynamicTexture('icon-texture', {width: 256, height: 256}, this.scene);
|
||||
//const texture = new DynamicTexture('/assets/icons/video.png', this.scene);
|
||||
|
||||
const image = new Image();
|
||||
//image.setAttribute('width', '256');
|
||||
//image.setAttribute('height', '256');
|
||||
//image.width=32;
|
||||
//image.height=32;
|
||||
image.src = '/assets/icons/video.png';
|
||||
image.onload = () => {
|
||||
texture.getContext().drawImage(image, 0, 0);
|
||||
texture.update();
|
||||
}
|
||||
|
||||
material.emissiveColor = new Color3(.1, .1, .8);
|
||||
material.opacityTexture = texture;
|
||||
icon.material = material;
|
||||
material.disableLighting = true;
|
||||
|
||||
//material.diffuseTexture = texture;
|
||||
//material.disableLighting;
|
||||
//material.emissiveColor = new Color3(1, 1, 1);
|
||||
//texture.uScale = 1;
|
||||
//texture.vScale = 1;
|
||||
|
||||
|
||||
}
|
||||
private buildMenu(camnum: number, position: Vector3) {
|
||||
const camerasphere = MeshBuilder.CreatePlane('camera-' + camnum, {width: 1, height: 1}, this.scene);
|
||||
camerasphere.position = position;
|
||||
const material = new StandardMaterial("cameramaterial", this.scene);
|
||||
//material.emissiveColor = new Color3(1, 1, 1);
|
||||
material.backFaceCulling = false;
|
||||
const texture = new DynamicTexture('texture', {width: 1600, height: 1600}, this.scene);
|
||||
material.emissiveTexture = texture;
|
||||
material.disableLighting = true;
|
||||
const img = new Image();
|
||||
|
||||
img.setAttribute('crossorigin', 'anonymous');
|
||||
img.src = 'https://cameras.immersiveidea.com/mjpg/video.mjpg?camera=' + camnum;
|
||||
|
||||
const ctx = texture.getContext();
|
||||
img.onload = () => {
|
||||
|
||||
ctx.drawImage(img, 0, 0);
|
||||
texture.update();
|
||||
window.setInterval((texture, img, ctx) => {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
texture.update();
|
||||
}, 1000, texture, img, ctx);
|
||||
}
|
||||
|
||||
texture.onLoadObservable.add(() => {
|
||||
this.logger.debug('texture loaded');
|
||||
});
|
||||
camerasphere.material = material;
|
||||
this.logger.info('camera built');
|
||||
private buildPoint(): AbstractMesh {
|
||||
const mesh = MeshBuilder.CreateIcoSphere('point', {radius: .02}, this._scene);
|
||||
const material: StandardMaterial = new StandardMaterial('pointMat', this._scene);
|
||||
material.diffuseColor = Color3.Red();
|
||||
mesh.material = material;
|
||||
mesh.isPickable = true;
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
@ -39,6 +39,13 @@ export class ClickMenu {
|
||||
}
|
||||
}, -1, false, this, false);
|
||||
|
||||
this.makeNewButton("Size", "size", scene, x += .11)
|
||||
.onPointerObservable.add((eventData) => {
|
||||
if (isUp(eventData)) {
|
||||
this.onClickMenuObservable.notifyObservers(eventData);
|
||||
}
|
||||
}, -1, false, this, false);
|
||||
|
||||
this.makeNewButton("Close", "close", scene, x += .11)
|
||||
.onPointerObservable.add((eventData) => {
|
||||
if (isUp(eventData)) {
|
||||
@ -47,17 +54,11 @@ export class ClickMenu {
|
||||
}
|
||||
}, -1, false, this, false);
|
||||
|
||||
this.makeNewButton("Size", "size", scene, x += .11)
|
||||
.onPointerObservable.add((eventData) => {
|
||||
if (isUp(eventData)) {
|
||||
this.onClickMenuObservable.notifyObservers(eventData);
|
||||
}
|
||||
}, -1, false, this, false);
|
||||
|
||||
const platform = scene.getMeshByName("platform");
|
||||
this._transformNode.parent = scene.activeCamera;
|
||||
this._transformNode.position.z = .7;
|
||||
this._transformNode.position.y = -.1;
|
||||
this._transformNode.position.y = -.3;
|
||||
this._transformNode.setParent(platform);
|
||||
this._transformNode.rotation.z = 0;
|
||||
}
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
import {AbstractMenu} from "./abstractMenu";
|
||||
import {Color3, MeshBuilder, StandardMaterial, Vector2, WebXRDefaultExperience} from "@babylonjs/core";
|
||||
import {Controllers} from "../controllers/controllers";
|
||||
|
||||
export class DeckMenu extends AbstractMenu {
|
||||
private static instance: DeckMenu;
|
||||
|
||||
public constructor(xr: WebXRDefaultExperience, controllers: Controllers) {
|
||||
super(xr, controllers);
|
||||
this.buildMenu();
|
||||
}
|
||||
|
||||
private feetToMeters(feet: number, inches: number) {
|
||||
return (feet * 12 + inches) * .0254;
|
||||
}
|
||||
|
||||
private buildMenu() {
|
||||
const base = MeshBuilder.CreateBox("base", {
|
||||
width: this.feetToMeters(14, 6),
|
||||
height: this.feetToMeters(0, .75),
|
||||
depth: this.feetToMeters(14, 6)
|
||||
}, this.scene);
|
||||
base.position.y = this.feetToMeters(0, .375);
|
||||
this.buildPost(new Vector2(7, 3), new Vector2(7, 3));
|
||||
this.buildPost(new Vector2(-7, -3), new Vector2(7, 3));
|
||||
this.buildPost(new Vector2(-4, -0), new Vector2(7, 3));
|
||||
this.buildPost(new Vector2(7 - 4, 3), new Vector2(7, 3));
|
||||
this.buildPost(new Vector2(-7, -3), new Vector2(7, 3 - 16));
|
||||
this.buildPost(new Vector2(7, 3), new Vector2(7, 3 - 16));
|
||||
|
||||
}
|
||||
|
||||
private buildPost(x: Vector2, y: Vector2) {
|
||||
const material = new StandardMaterial("material", this.scene);
|
||||
material.diffuseColor = new Color3(.02, .02, .02);
|
||||
const base = MeshBuilder.CreateBox("base", {
|
||||
width: this.feetToMeters(0, 2.5),
|
||||
height: this.feetToMeters(0, 38),
|
||||
depth: this.feetToMeters(0, 2.5)
|
||||
}, this.scene);
|
||||
base.position.y = this.feetToMeters(0, 38 / 2);
|
||||
base.position.x = this.feetToMeters(x.x, x.y);
|
||||
base.position.z = this.feetToMeters(y.x, y.y);
|
||||
base.material = material;
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
import {AbstractMesh, MeshBuilder, WebXRDefaultExperience} from "@babylonjs/core";
|
||||
import {Controllers} from "../controllers/controllers";
|
||||
import {AbstractMenu} from "./abstractMenu";
|
||||
import {AdvancedDynamicTexture, Grid, TextBlock} from "@babylonjs/gui";
|
||||
|
||||
|
||||
export class IntegrationMenu extends AbstractMenu {
|
||||
private plane: AbstractMesh = null;
|
||||
|
||||
constructor(xr: WebXRDefaultExperience, controllers: Controllers) {
|
||||
super(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);
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
import {Scene} from "@babylonjs/core";
|
||||
|
||||
|
||||
export class MainMenu {
|
||||
private readonly scene: Scene;
|
||||
private parent: HTMLElement;
|
||||
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.buildMenu();
|
||||
}
|
||||
|
||||
private buildMenu() {
|
||||
//const button = document.createElement("button");
|
||||
//this.parent.appendChild(button);
|
||||
//this.file = new InputFile();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
10
src/util/me.ts
Normal file
10
src/util/me.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
export function getMe(): string {
|
||||
let me = localStorage.getItem('me');
|
||||
if (!me) {
|
||||
me = 'user' + uuidv4();
|
||||
localStorage.setItem('me', me);
|
||||
}
|
||||
return me;
|
||||
}
|
||||
@ -35,12 +35,12 @@ export class VrApp {
|
||||
if (webGpu) {
|
||||
engine = new WebGPUEngine(canvas);
|
||||
await (engine as WebGPUEngine).initAsync();
|
||||
console.log("WebGPU enabled");
|
||||
|
||||
} else {
|
||||
engine = new Engine(canvas, true);
|
||||
}
|
||||
engine.setHardwareScalingLevel(1 / window.devicePixelRatio);
|
||||
console.log(engine.getCaps().multiview);
|
||||
|
||||
window.onresize = () => {
|
||||
engine.resize();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user