Fix camera positioning, label z-fighting, and remove dead code
- Fix desktop camera to be directly above platform by resetting local position
- Increase label back offset from 0.001 to 0.005 to prevent z-fighting
- Use refreshBoundingInfo({}) for consistency with codebase
- Remove unused copyToPublic from pouchData.ts
- Remove dead DiagramEntityAdapter and integration/gizmo module
- Remove unused netlify functions and integration utilities
- Clean up unused imports and commented code across multiple files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8c2b7f9c7d
commit
3155cc930f
@ -1,49 +0,0 @@
|
|||||||
import {Handler, HandlerContext, HandlerEvent} from "@netlify/functions";
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
export const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
|
|
||||||
try {
|
|
||||||
switch (event.httpMethod) {
|
|
||||||
case 'POST':
|
|
||||||
const apiKey = event.headers['api-key'];
|
|
||||||
const query = event.body;
|
|
||||||
const response = await axios.post('https://api.newrelic.com/graphql',
|
|
||||||
query,
|
|
||||||
{headers: {'Api-Key': apiKey, 'Content-Type': 'application/json'}});
|
|
||||||
const data = await response.data;
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Access-Control-Allow-Origin': 'https://cameras.immersiveidea.com',
|
|
||||||
'Access-Control-Allow-Credentials': 'true'
|
|
||||||
},
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'OPTIONS':
|
|
||||||
const headers = {
|
|
||||||
'Access-Control-Allow-Origin': 'https://cameras.immersiveidea.com',
|
|
||||||
'Access-Control-Allow-Credentials': 'true',
|
|
||||||
'Access-Control-Allow-Headers': 'content-type, api-key',
|
|
||||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE'
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
statusCode: 204,
|
|
||||||
headers
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
statusCode: 405,
|
|
||||||
body: 'Method Not Allowed'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
body: JSON.stringify(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,216 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const baseurl = 'https://syncdb-service-d3f974de56ef.herokuapp.com/';
|
|
||||||
const auth = 'admin:stM8Lnm@Cuf-tWZHv';
|
|
||||||
const authToken = Buffer.from(auth).toString('base64');
|
|
||||||
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
username: string,
|
|
||||||
password: string,
|
|
||||||
db: string
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkDB(auth: string, db: string) {
|
|
||||||
try {
|
|
||||||
console.log('Checking for DB');
|
|
||||||
const exist = await axios.head(baseurl + db,
|
|
||||||
{headers: {'Authorization': 'Basic ' + auth}});
|
|
||||||
if (exist && exist.status == 200) {
|
|
||||||
console.log("DB Found");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("DB not Found");
|
|
||||||
//console.log(err);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Access {
|
|
||||||
DENIED,
|
|
||||||
MISSING,
|
|
||||||
ALLOWED,
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUserToken(params: Params) {
|
|
||||||
const userAuth = params.username + ':' + params.password;
|
|
||||||
return Buffer.from(userAuth).toString('base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkIfDbExists(params: Params): Promise<Access> {
|
|
||||||
console.log("Checking if DB exists");
|
|
||||||
if (!params.username || !params.password || !params.db) {
|
|
||||||
throw new Error('No share key provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await checkDB(getUserToken(params), params.db)) {
|
|
||||||
return Access.ALLOWED;
|
|
||||||
}
|
|
||||||
if (await checkDB(authToken, params.db)) {
|
|
||||||
return Access.DENIED;
|
|
||||||
}
|
|
||||||
return Access.MISSING;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createDB(params: Params) {
|
|
||||||
console.log("Creating DB");
|
|
||||||
|
|
||||||
const response = await axios.put(
|
|
||||||
baseurl + params.db,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Basic ' + authToken,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log(response.status);
|
|
||||||
console.log(response.data);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createUser(params: Params) {
|
|
||||||
try {
|
|
||||||
console.log("Checking for User");
|
|
||||||
const userResponse = await axios.head(
|
|
||||||
baseurl + '_users/org.couchdb.user:' + params.username,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Basic ' + getUserToken(params),
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (userResponse.status == 200) {
|
|
||||||
console.log("User Found");
|
|
||||||
return userResponse;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("User Missing");
|
|
||||||
}
|
|
||||||
console.log("Creating User");
|
|
||||||
const userResponse = await axios.put(
|
|
||||||
baseurl + '_users/org.couchdb.user:' + params.username,
|
|
||||||
{
|
|
||||||
_id: 'org.couchdb.user:' + params.username,
|
|
||||||
name: params.username,
|
|
||||||
password: params.password, roles: [], type: 'user'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Basic ' + authToken,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return userResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function authorizeUser(params: Params) {
|
|
||||||
console.log("Authorizing User");
|
|
||||||
return await axios.put(
|
|
||||||
baseurl + params.db + '/_security',
|
|
||||||
{admins: {names: [], roles: []}, members: {names: [params.username], roles: []}},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Basic ' + authToken,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async (req: Request): Promise<Response> => {
|
|
||||||
console.log(req.method);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (req.method == 'OPTIONS') {
|
|
||||||
const origin = req.headers.get('Origin');
|
|
||||||
const headers = req.headers.get('Access-Control-Request-Headers');
|
|
||||||
console.log(origin);
|
|
||||||
return new Response(
|
|
||||||
'OK',
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Allow': 'POST',
|
|
||||||
'Max-Age': '30',
|
|
||||||
'Access-Control-Allow-Methods': 'POST',
|
|
||||||
'Access-Control-Allow-Origin': origin ? origin : 'https://cameras.immersiveidea.com',
|
|
||||||
'Access-Control-Allow-Credentials': 'true',
|
|
||||||
'Access-Control-Allow-Headers': headers ? headers : 'Content-Type'
|
|
||||||
},
|
|
||||||
status: 200
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify(err),
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Allow': 'POST',
|
|
||||||
'Max-Age': '30',
|
|
||||||
'Access-Control-Allow-Methods': 'POST',
|
|
||||||
'Access-Control-Allow-Origin': origin ? origin : 'https://cameras.immersiveidea.com',
|
|
||||||
'Access-Control-Allow-Credentials': 'true'
|
|
||||||
},
|
|
||||||
status: 500
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
const params = JSON.parse(await req.text());
|
|
||||||
console.log(params);
|
|
||||||
const createUserResponse = await createUser(params);
|
|
||||||
console.log(createUserResponse.status);
|
|
||||||
if (createUserResponse.status != 201 && createUserResponse.status != 200) {
|
|
||||||
throw new Error('Could not create User');
|
|
||||||
}
|
|
||||||
|
|
||||||
const exists = await checkIfDbExists(params);
|
|
||||||
switch (exists) {
|
|
||||||
case Access.ALLOWED:
|
|
||||||
console.log('Allowed');
|
|
||||||
return new Response('OK', {status: 200});
|
|
||||||
case Access.DENIED:
|
|
||||||
console.log('Denied');
|
|
||||||
return new Response('Denied', {status: 401});
|
|
||||||
case Access.MISSING:
|
|
||||||
console.log('Creating Missing DB');
|
|
||||||
const createDbResponse = await createDB(params);
|
|
||||||
if (createDbResponse.status != 201) {
|
|
||||||
throw new Error('Could not create DB');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const authorizeUserResponse = await authorizeUser(params);
|
|
||||||
if (authorizeUserResponse.status != 200) {
|
|
||||||
throw new Error('could not authorize user');
|
|
||||||
}
|
|
||||||
const origin = req.headers.get('origin');
|
|
||||||
console.log(origin);
|
|
||||||
return new Response(
|
|
||||||
'OK',
|
|
||||||
{
|
|
||||||
headers: [
|
|
||||||
['Content-Type', 'application/json'],
|
|
||||||
['Access-Control-Allow-Origin', origin],
|
|
||||||
['Access-Control-Allow-Credentials', 'true']
|
|
||||||
],
|
|
||||||
status: 200
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
const response = {err: err};
|
|
||||||
return new Response('Error',
|
|
||||||
{status: 500}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import {Handler, HandlerContext, HandlerEvent} from "@netlify/functions";
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
export const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post('https://api.assemblyai.com/v2/realtime/token', // use account token to get a temp user token
|
|
||||||
{expires_in: 3600}, // can set a TTL timer in seconds.
|
|
||||||
{headers: {authorization: process.env.VOICE_TOKEN}});
|
|
||||||
|
|
||||||
const data = await response.data;
|
|
||||||
return {
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
body: JSON.stringify(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "immersive",
|
"name": "immersive",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.8-46",
|
"version": "0.0.8-47",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@ -25,10 +25,12 @@ export function buildRig(xr: WebXRDefaultExperience): Mesh {
|
|||||||
});
|
});
|
||||||
for (const cam of scene.cameras) {
|
for (const cam of scene.cameras) {
|
||||||
cam.parent = cameratransform;
|
cam.parent = cameratransform;
|
||||||
|
cam.position = new Vector3(0, 1.6, 0); // Reset to local position above platform
|
||||||
}
|
}
|
||||||
scene.onActiveCameraChanged.add(() => {
|
scene.onActiveCameraChanged.add(() => {
|
||||||
for (const cam of scene.cameras) {
|
for (const cam of scene.cameras) {
|
||||||
cam.parent = cameratransform;
|
cam.parent = cameratransform;
|
||||||
|
cam.position = new Vector3(0, 1.6, 0); // Reset to local position above platform
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -314,7 +314,7 @@ export class DiagramObject {
|
|||||||
this._label.rotation.y = 0;
|
this._label.rotation.y = 0;
|
||||||
this._labelBack.rotation.y = Math.PI; // Back face still needs to be flipped
|
this._labelBack.rotation.y = Math.PI; // Back face still needs to be flipped
|
||||||
}
|
}
|
||||||
this._labelBack.position.z = 0.001;
|
this._labelBack.position.z = 0.005;
|
||||||
} else {
|
} else {
|
||||||
// Standard object labels - convert world space to parent's local space
|
// Standard object labels - convert world space to parent's local space
|
||||||
// This accounts for mesh scaling, which is not included in boundingBox.maximum
|
// This accounts for mesh scaling, which is not included in boundingBox.maximum
|
||||||
@ -326,7 +326,7 @@ export class DiagramObject {
|
|||||||
temp.dispose();
|
temp.dispose();
|
||||||
this._label.position.y = y + 0.06;
|
this._label.position.y = y + 0.06;
|
||||||
this._labelBack.rotation.y = Math.PI;
|
this._labelBack.rotation.y = Math.PI;
|
||||||
this._labelBack.position.z = 0.001;
|
this._labelBack.position.z = 0.005;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import {
|
|||||||
} from '@babylonjs/core';
|
} from '@babylonjs/core';
|
||||||
|
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
import { HandleState, CORNER_POSITIONS, FACE_POSITIONS} from './enums';
|
import { CORNER_POSITIONS, FACE_POSITIONS} from './enums';
|
||||||
import { ResizeGizmoEvent } from './types';
|
import { ResizeGizmoEvent } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,7 +110,7 @@ export class ResizeGizmo {
|
|||||||
private createHandles(): void {
|
private createHandles(): void {
|
||||||
// Ensure world matrix and bounding info are current
|
// Ensure world matrix and bounding info are current
|
||||||
this._targetMesh.computeWorldMatrix(true);
|
this._targetMesh.computeWorldMatrix(true);
|
||||||
this._targetMesh.refreshBoundingInfo();
|
this._targetMesh.refreshBoundingInfo({});
|
||||||
|
|
||||||
// Get bounding box for positioning
|
// Get bounding box for positioning
|
||||||
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
||||||
@ -489,7 +489,7 @@ export class ResizeGizmo {
|
|||||||
private updateHandleTransforms(): void {
|
private updateHandleTransforms(): void {
|
||||||
// Ensure world matrix and bounding info are current
|
// Ensure world matrix and bounding info are current
|
||||||
this._targetMesh.computeWorldMatrix(true);
|
this._targetMesh.computeWorldMatrix(true);
|
||||||
this._targetMesh.refreshBoundingInfo();
|
this._targetMesh.refreshBoundingInfo({});
|
||||||
|
|
||||||
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
||||||
const boundingBox = targetBoundingInfo.boundingBox;
|
const boundingBox = targetBoundingInfo.boundingBox;
|
||||||
|
|||||||
@ -74,12 +74,12 @@ export class PouchData {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('paused', (info) => {
|
.on('paused', (info) => {
|
||||||
this._logger.debug('[Sync] Paused - up to date');
|
this._logger.debug(`[Sync] Paused - up to date ${info}`);
|
||||||
})
|
})
|
||||||
.on('active', () => {
|
.on('active', () => {
|
||||||
this._logger.debug('[Sync] Active - syncing');
|
this._logger.debug('[Sync] Active - syncing');
|
||||||
})
|
})
|
||||||
.on('error', (err) => {
|
.on('error', (err: any) => {
|
||||||
this._logger.error('[Sync] Error:', err);
|
this._logger.error('[Sync] Error:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -202,52 +202,4 @@ export class PouchData {
|
|||||||
this._logger.warn('CONFLICTS!', doc._conflicts);
|
this._logger.warn('CONFLICTS!', doc._conflicts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy all documents from this database to a new public database.
|
|
||||||
* Used when sharing a local diagram to make it publicly accessible.
|
|
||||||
* @param newDbName - The name for the new public database
|
|
||||||
* @returns The URL path to the new public diagram
|
|
||||||
*/
|
|
||||||
public async copyToPublic(newDbName: string): Promise<string> {
|
|
||||||
this._logger.info(`[Copy] Starting copy to public-${newDbName}`);
|
|
||||||
|
|
||||||
// Create the remote public database
|
|
||||||
const remoteUrl = `${window.location.origin}/pouchdb/public-${newDbName}`;
|
|
||||||
const remoteDb = new PouchDB(remoteUrl);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get all docs from local database
|
|
||||||
const allDocs = await this._db.allDocs({ include_docs: true });
|
|
||||||
this._logger.debug(`[Copy] Found ${allDocs.rows.length} documents to copy`);
|
|
||||||
|
|
||||||
// Copy each document to the remote database
|
|
||||||
for (const row of allDocs.rows) {
|
|
||||||
if (row.doc) {
|
|
||||||
// Remove PouchDB internal fields for clean insert
|
|
||||||
const { _rev, ...docWithoutRev } = row.doc;
|
|
||||||
try {
|
|
||||||
await remoteDb.put(docWithoutRev);
|
|
||||||
} catch (err) {
|
|
||||||
// Document might already exist if this is a retry
|
|
||||||
this._logger.warn(`[Copy] Failed to copy doc ${row.id}:`, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._logger.info(`[Copy] Successfully copied ${allDocs.rows.length} documents`);
|
|
||||||
return `/db/public/${newDbName}`;
|
|
||||||
} catch (err) {
|
|
||||||
this._logger.error('[Copy] Error copying to public:', err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all documents in the database (for export/copy operations)
|
|
||||||
*/
|
|
||||||
public async getAllDocs(): Promise<any[]> {
|
|
||||||
const result = await this._db.allDocs({ include_docs: true });
|
|
||||||
return result.rows.map(row => row.doc).filter(doc => doc && doc.id !== 'metadata');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import log from "loglevel";
|
|
||||||
|
|
||||||
export async function checkDb(localName: string, remoteDbName: string, password: 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": password,
|
|
||||||
"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;
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
export function hex_to_ascii(input) {
|
|
||||||
const hex = input.toString();
|
|
||||||
let output = '';
|
|
||||||
for (let n = 0; n < hex.length; n += 2) {
|
|
||||||
output += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ascii_to_hex(str) {
|
|
||||||
const arr1 = [];
|
|
||||||
for (let n = 0, l = str.length; n < l; n++) {
|
|
||||||
const hex = Number(str.charCodeAt(n)).toString(16);
|
|
||||||
arr1.push(hex);
|
|
||||||
}
|
|
||||||
return arr1.join('');
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import log from "loglevel";
|
|
||||||
import {DiagramEntity, DiagramEntityType} from "../../diagram/types/diagramEntity";
|
|
||||||
import {Observable} from "@babylonjs/core";
|
|
||||||
import {Encryption} from "../encryption";
|
|
||||||
import {DiagramEventObserverMask} from "../../diagram/types/diagramEventObserverMask";
|
|
||||||
|
|
||||||
export async function syncDoc(info: any, onDBRemoveObservable: Observable<DiagramEntity>, onDBUpdateObservable: Observable<DiagramEntity>,
|
|
||||||
encryption: Encryption, key: string) {
|
|
||||||
const logger = log.getLogger('syncDoc');
|
|
||||||
logger.debug(info);
|
|
||||||
if (info.direction == 'pull') {
|
|
||||||
// @ts-ignore
|
|
||||||
const docs = info.change.docs;
|
|
||||||
let salt = null;
|
|
||||||
for (const doc of docs) {
|
|
||||||
if (doc.encrypted) {
|
|
||||||
if (salt != doc.encrypted.salt || (key && !encryption.ready)) {
|
|
||||||
await encryption.setPassword(key, doc.encrypted.salt);
|
|
||||||
salt = doc.encrypted.salt
|
|
||||||
}
|
|
||||||
const decrypted = await encryption.decryptToObject(doc.encrypted.encrypted, doc.encrypted.iv);
|
|
||||||
if (decrypted.type == DiagramEntityType.USER) {
|
|
||||||
//onUserObservable.notifyObservers(doc, -1);
|
|
||||||
} else {
|
|
||||||
logger.debug(decrypted);
|
|
||||||
if (doc._deleted) {
|
|
||||||
logger.debug('Delete', doc);
|
|
||||||
onDBRemoveObservable.notifyObservers({
|
|
||||||
id: doc._id,
|
|
||||||
template: decrypted.template,
|
|
||||||
type: doc.type
|
|
||||||
}, DiagramEventObserverMask.FROM_DB);
|
|
||||||
} else {
|
|
||||||
onDBUpdateObservable.notifyObservers(decrypted, DiagramEventObserverMask.FROM_DB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (doc.type == 'user') {
|
|
||||||
//onUserObservable.notifyObservers(doc, -1);
|
|
||||||
} else {
|
|
||||||
logger.debug(doc);
|
|
||||||
if (doc._deleted) {
|
|
||||||
logger.debug('Delete', doc);
|
|
||||||
onDBRemoveObservable.notifyObservers({
|
|
||||||
id: doc._id,
|
|
||||||
template: doc.template,
|
|
||||||
type: doc.type
|
|
||||||
}, DiagramEventObserverMask.FROM_DB);
|
|
||||||
} else {
|
|
||||||
if (doc.template) {
|
|
||||||
onDBUpdateObservable.notifyObservers(doc, DiagramEventObserverMask.FROM_DB);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,166 +0,0 @@
|
|||||||
/**
|
|
||||||
* DiagramEntity Integration Adapter for ResizeGizmo
|
|
||||||
* Bridges ResizeGizmo events to DiagramManager's persistence system
|
|
||||||
*
|
|
||||||
* This adapter lives in the integration layer to keep the ResizeGizmo
|
|
||||||
* system pure and reusable without diagram-specific dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { AbstractMesh } from "@babylonjs/core";
|
|
||||||
import { ResizeGizmoManager } from "../../gizmos/ResizeGizmo";
|
|
||||||
import { ResizeGizmoEvent } from "../../gizmos/ResizeGizmo";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type definitions for DiagramManager integration (loosely coupled)
|
|
||||||
* These match the actual types in the codebase without importing them
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface DiagramEntity {
|
|
||||||
id?: string;
|
|
||||||
template?: string;
|
|
||||||
position?: { x: number; y: number; z: number };
|
|
||||||
rotation?: { x: number; y: number; z: number };
|
|
||||||
scale?: { x: number; y: number; z: number };
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DiagramEventType {
|
|
||||||
MODIFY = "MODIFY"
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DiagramEvent {
|
|
||||||
type: DiagramEventType;
|
|
||||||
entity: DiagramEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DiagramEventObserverMask {
|
|
||||||
TO_DB = 2,
|
|
||||||
ALL = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DiagramEventNotifier {
|
|
||||||
notifyObservers(event: DiagramEvent, mask?: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DiagramManager {
|
|
||||||
onDiagramEventObservable: DiagramEventNotifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converter function type for transforming BabylonJS meshes to DiagramEntities
|
|
||||||
*/
|
|
||||||
export type MeshToEntityConverter = (mesh: AbstractMesh) => DiagramEntity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter that connects ResizeGizmo to DiagramManager for persistence
|
|
||||||
* Uses dependency injection to remain loosely coupled from diagram internals
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```typescript
|
|
||||||
* import { DiagramEntityAdapter } from './integration/gizmo';
|
|
||||||
* import { toDiagramEntity } from './diagram/functions/toDiagramEntity';
|
|
||||||
*
|
|
||||||
* // Create resize gizmo
|
|
||||||
* const gizmo = new ResizeGizmoManager(scene, {
|
|
||||||
* mode: ResizeGizmoMode.ALL
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* // Create adapter with injected converter
|
|
||||||
* const adapter = new DiagramEntityAdapter(
|
|
||||||
* gizmo,
|
|
||||||
* diagramManager,
|
|
||||||
* toDiagramEntity, // Injected dependency
|
|
||||||
* false // Don't persist on drag
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* // Now scale changes will automatically persist to database
|
|
||||||
* gizmo.attachToMesh(myDiagramMesh);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export class DiagramEntityAdapter {
|
|
||||||
private _gizmo: ResizeGizmoManager;
|
|
||||||
private _diagramManager: DiagramManager;
|
|
||||||
private _meshConverter: MeshToEntityConverter;
|
|
||||||
private _persistOnDrag: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create adapter
|
|
||||||
* @param gizmo ResizeGizmoManager instance
|
|
||||||
* @param diagramManager DiagramManager instance (or object with onDiagramEventObservable)
|
|
||||||
* @param meshConverter Function to convert BabylonJS mesh to DiagramEntity (injected dependency)
|
|
||||||
* @param persistOnDrag If true, persist on every drag update (can be expensive). If false, only persist on scale end.
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
gizmo: ResizeGizmoManager,
|
|
||||||
diagramManager: DiagramManager,
|
|
||||||
meshConverter: MeshToEntityConverter,
|
|
||||||
persistOnDrag: boolean = false
|
|
||||||
) {
|
|
||||||
this._gizmo = gizmo;
|
|
||||||
this._diagramManager = diagramManager;
|
|
||||||
this._meshConverter = meshConverter;
|
|
||||||
this._persistOnDrag = persistOnDrag;
|
|
||||||
|
|
||||||
this.setupEventListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup event listeners
|
|
||||||
*/
|
|
||||||
private setupEventListeners(): void {
|
|
||||||
// Persist on scale end (always)
|
|
||||||
this._gizmo.onScaleEnd((event) => {
|
|
||||||
this.persistScaleChange(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Optionally persist on drag
|
|
||||||
if (this._persistOnDrag) {
|
|
||||||
this._gizmo.onScaleDrag((event) => {
|
|
||||||
this.persistScaleChange(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persist scale change to DiagramManager
|
|
||||||
*/
|
|
||||||
private persistScaleChange(event: ResizeGizmoEvent): void {
|
|
||||||
const mesh = event.mesh;
|
|
||||||
|
|
||||||
// Convert mesh to DiagramEntity using injected converter
|
|
||||||
// This properly extracts color from material and all other properties
|
|
||||||
const entity = this._meshConverter(mesh);
|
|
||||||
|
|
||||||
// Notify DiagramManager
|
|
||||||
this._diagramManager.onDiagramEventObservable.notifyObservers(
|
|
||||||
{
|
|
||||||
type: DiagramEventType.MODIFY,
|
|
||||||
entity
|
|
||||||
},
|
|
||||||
DiagramEventObserverMask.TO_DB
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable/disable drag persistence
|
|
||||||
*/
|
|
||||||
setPersistOnDrag(enabled: boolean): void {
|
|
||||||
if (this._persistOnDrag === enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._persistOnDrag = enabled;
|
|
||||||
|
|
||||||
// Re-setup listeners
|
|
||||||
// Note: In a production implementation, you'd want to properly remove/add observers
|
|
||||||
// For now, this is a simplified version
|
|
||||||
console.warn("[DiagramEntityAdapter] Changing persistOnDrag at runtime may cause duplicate events");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get persist on drag setting
|
|
||||||
*/
|
|
||||||
getPersistOnDrag(): boolean {
|
|
||||||
return this._persistOnDrag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* Gizmo Integration Layer
|
|
||||||
* Adapters for integrating gizmo systems with diagram persistence
|
|
||||||
*/
|
|
||||||
|
|
||||||
export { DiagramEntityAdapter, type MeshToEntityConverter } from './DiagramEntityAdapter';
|
|
||||||
@ -84,30 +84,6 @@ export class AnimatedLineTexture {
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a texture from the animation set when disposed
|
|
||||||
* WARNING: Do NOT call this on cached textures! Only for non-cached textures.
|
|
||||||
* Cached textures are shared across multiple connections.
|
|
||||||
* Use ClearCache() to dispose cached textures properly.
|
|
||||||
* @param texture - The texture to stop animating
|
|
||||||
*/
|
|
||||||
public static DisposeTexture(texture: Texture): void {
|
|
||||||
// Safety check: prevent disposing cached textures (they're shared!)
|
|
||||||
for (const [color, cachedTexture] of this._coloredTextureCache.entries()) {
|
|
||||||
if (cachedTexture === texture) {
|
|
||||||
console.error(
|
|
||||||
`AnimatedLineTexture.DisposeTexture: Attempted to dispose cached texture ` +
|
|
||||||
`"${texture.name}" (color: ${color}). This will break texture sharing! ` +
|
|
||||||
`Cached textures should not be disposed individually. Use ClearCache() instead.`
|
|
||||||
);
|
|
||||||
return; // Don't dispose - it's shared across multiple connections
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only dispose non-cached textures
|
|
||||||
this._animatedTextures.delete(texture);
|
|
||||||
texture.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preload textures for common colors to prevent first-render stutter
|
* Preload textures for common colors to prevent first-render stutter
|
||||||
@ -119,27 +95,5 @@ export class AnimatedLineTexture {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the texture cache and dispose all cached textures
|
|
||||||
* Use with caution - only call when no connections are using these textures
|
|
||||||
*/
|
|
||||||
public static ClearCache(): void {
|
|
||||||
this._coloredTextureCache.forEach((texture) => {
|
|
||||||
this._animatedTextures.delete(texture);
|
|
||||||
texture.dispose();
|
|
||||||
});
|
|
||||||
this._coloredTextureCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cache statistics for debugging
|
|
||||||
* @returns Object with cache stats
|
|
||||||
*/
|
|
||||||
public static GetCacheStats(): { cachedColors: number; totalAnimatedTextures: number; colors: string[] } {
|
|
||||||
return {
|
|
||||||
cachedColors: this._coloredTextureCache.size,
|
|
||||||
totalAnimatedTextures: this._animatedTextures.size,
|
|
||||||
colors: Array.from(this._coloredTextureCache.keys())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -149,32 +149,6 @@ export class AppConfig {
|
|||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove handle configuration by ID
|
|
||||||
* @param id Handle ID to remove
|
|
||||||
*/
|
|
||||||
public removeHandleConfig(id: string) {
|
|
||||||
if (!this._currentConfig.handles) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialLength = this._currentConfig.handles.length;
|
|
||||||
this._currentConfig.handles = this._currentConfig.handles.filter(h => h.id !== id);
|
|
||||||
|
|
||||||
if (this._currentConfig.handles.length < initialLength) {
|
|
||||||
this._logger.debug(`Removed handle config for ${id}`);
|
|
||||||
this.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all handle configurations
|
|
||||||
* @returns Array of all HandleConfig objects
|
|
||||||
*/
|
|
||||||
public getAllHandleConfigs(): HandleConfig[] {
|
|
||||||
return this._currentConfig.handles || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private save() {
|
private save() {
|
||||||
localStorage.setItem('appConfig', JSON.stringify(this._currentConfig));
|
localStorage.setItem('appConfig', JSON.stringify(this._currentConfig));
|
||||||
this.onConfigChangedObservable.notifyObservers(this._currentConfig, -1);
|
this.onConfigChangedObservable.notifyObservers(this._currentConfig, -1);
|
||||||
|
|||||||
@ -4,13 +4,11 @@ import {
|
|||||||
Material,
|
Material,
|
||||||
MeshBuilder,
|
MeshBuilder,
|
||||||
Observable,
|
Observable,
|
||||||
PBRMaterial,
|
|
||||||
PhysicsAggregate,
|
PhysicsAggregate,
|
||||||
PhysicsShapeType,
|
PhysicsShapeType,
|
||||||
PointsCloudSystem,
|
PointsCloudSystem,
|
||||||
Scene,
|
Scene,
|
||||||
Sound,
|
Sound,
|
||||||
Texture,
|
|
||||||
TransformNode,
|
TransformNode,
|
||||||
Vector3
|
Vector3
|
||||||
} from "@babylonjs/core";
|
} from "@babylonjs/core";
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
import {HavokPlugin, Quaternion, Scene, Vector3} from "@babylonjs/core";
|
import {HavokPlugin, Scene, Vector3} from "@babylonjs/core";
|
||||||
import HavokPhysics from "@babylonjs/havok";
|
import HavokPhysics from "@babylonjs/havok";
|
||||||
import {snapGridVal} from "./functions/snapGridVal";
|
|
||||||
import {snapRotateVal} from "./functions/snapRotateVal";
|
|
||||||
import {isDiagramEntity} from "../diagram/functions/isDiagramEntity";
|
|
||||||
import {appConfigInstance} from "./appConfig";
|
|
||||||
|
|
||||||
export class CustomPhysics {
|
export class CustomPhysics {
|
||||||
private readonly scene: Scene;
|
private readonly scene: Scene;
|
||||||
|
|
||||||
|
|
||||||
constructor(scene: Scene) {
|
constructor(scene: Scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
}
|
}
|
||||||
@ -17,47 +12,6 @@ export class CustomPhysics {
|
|||||||
const havok = await HavokPhysics();
|
const havok = await HavokPhysics();
|
||||||
const havokPlugin = new HavokPlugin(true, havok);
|
const havokPlugin = new HavokPlugin(true, havok);
|
||||||
const scene = this.scene;
|
const scene = this.scene;
|
||||||
const physicsEnable = scene.enablePhysics(new Vector3(0, -9.8, 0), havokPlugin);
|
scene.enablePhysics(new Vector3(0, -9.8, 0), havokPlugin);
|
||||||
|
|
||||||
scene.collisionsEnabled = true;
|
|
||||||
scene.onAfterPhysicsObservable.add(() => {
|
|
||||||
const config = appConfigInstance.current;
|
|
||||||
scene.meshes.forEach((mesh) => {
|
|
||||||
if (isDiagramEntity(mesh) && mesh.physicsBody) {
|
|
||||||
const body = mesh.physicsBody;
|
|
||||||
const linearVelocity = new Vector3();
|
|
||||||
body.getLinearVelocityToRef(linearVelocity);
|
|
||||||
if (linearVelocity.length() < .1) {
|
|
||||||
|
|
||||||
body.disablePreStep = false;
|
|
||||||
|
|
||||||
// Apply location snap if enabled
|
|
||||||
if (config.locationSnap > 0) {
|
|
||||||
const pos: Vector3 = body.getObjectCenterWorld();
|
|
||||||
const val: Vector3 = snapGridVal(pos, config.locationSnap);
|
|
||||||
body.transformNode.position.set(val.x, val.y, val.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply rotation snap if enabled
|
|
||||||
if (config.rotateSnap > 0) {
|
|
||||||
const rot: Quaternion =
|
|
||||||
Quaternion.FromEulerVector(
|
|
||||||
snapRotateVal(body.transformNode.rotationQuaternion.toEulerAngles(),
|
|
||||||
config.rotateSnap));
|
|
||||||
|
|
||||||
body.transformNode.rotationQuaternion.set(
|
|
||||||
rot.x, rot.y, rot.z, rot.w
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
scene.onAfterRenderObservable.addOnce(() => {
|
|
||||||
body.disablePreStep = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,8 +1,3 @@
|
|||||||
/**
|
|
||||||
* Device detection utilities for VR capability and device type
|
|
||||||
*/
|
|
||||||
import {Scene, WebXRDefaultExperience} from "@babylonjs/core";
|
|
||||||
|
|
||||||
export interface DeviceCapabilities {
|
export interface DeviceCapabilities {
|
||||||
isVRCapable: boolean;
|
isVRCapable: boolean;
|
||||||
isMobileVR: boolean;
|
isMobileVR: boolean;
|
||||||
@ -65,4 +60,3 @@ export async function getDeviceCapabilities(): Promise<DeviceCapabilities> {
|
|||||||
deviceType
|
deviceType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,33 +11,6 @@ export function getPath(): string {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current path is a local database (no sync)
|
|
||||||
* Local paths: /db/local/:db
|
|
||||||
*/
|
|
||||||
export function isLocalPath(): boolean {
|
|
||||||
const path = window.location.pathname.split('/');
|
|
||||||
return path.length >= 3 && path[1] === 'db' && path[2] === 'local';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current path is a public database
|
|
||||||
* Public paths: /db/public/:db
|
|
||||||
*/
|
|
||||||
export function isPublicPath(): boolean {
|
|
||||||
const path = window.location.pathname.split('/');
|
|
||||||
return path.length >= 3 && path[1] === 'db' && path[2] === 'public';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current path is a private database
|
|
||||||
* Private paths: /db/private/:db
|
|
||||||
*/
|
|
||||||
export function isPrivatePath(): boolean {
|
|
||||||
const path = window.location.pathname.split('/');
|
|
||||||
return path.length >= 3 && path[1] === 'db' && path[2] === 'private';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the database type from the current path
|
* Get the database type from the current path
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -157,8 +157,6 @@ function positionComponentsRelativeToCamera(scene: Scene, diagramManager: Diagra
|
|||||||
logger.info('Horizontal left:', horizontalLeft);
|
logger.info('Horizontal left:', horizontalLeft);
|
||||||
logger.info('Platform world position:', platform.getAbsolutePosition());
|
logger.info('Platform world position:', platform.getAbsolutePosition());
|
||||||
|
|
||||||
// Position toolbox: Camera-relative positioning disabled to respect default/saved positions
|
|
||||||
// Handles now use their configured defaults or saved localStorage positions
|
|
||||||
const toolbox = diagramManager.diagramMenuManager.toolbox;
|
const toolbox = diagramManager.diagramMenuManager.toolbox;
|
||||||
if (toolbox && toolbox.handleMesh) {
|
if (toolbox && toolbox.handleMesh) {
|
||||||
logger.info('Toolbox handleMesh using default/saved position:', {
|
logger.info('Toolbox handleMesh using default/saved position:', {
|
||||||
@ -166,64 +164,13 @@ function positionComponentsRelativeToCamera(scene: Scene, diagramManager: Diagra
|
|||||||
absolutePosition: toolbox.handleMesh.getAbsolutePosition().clone(),
|
absolutePosition: toolbox.handleMesh.getAbsolutePosition().clone(),
|
||||||
rotation: toolbox.handleMesh.rotation.clone()
|
rotation: toolbox.handleMesh.rotation.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Camera-relative positioning commented out - handles use their own defaults
|
|
||||||
/*
|
|
||||||
// Position at 45 degrees to the left, 0.45m away, slightly below eye level
|
|
||||||
// NOTE: User faces -Z direction by design, so negate forward offset
|
|
||||||
const forwardOffset = horizontalForward.scale(-0.3);
|
|
||||||
const leftOffset = horizontalLeft.scale(0.35);
|
|
||||||
const toolboxWorldPos = cameraWorldPos.add(forwardOffset).add(leftOffset);
|
|
||||||
toolboxWorldPos.y = cameraWorldPos.y - 0.3; // Below eye level
|
|
||||||
|
|
||||||
logger.info('Calculated toolbox world position:', toolboxWorldPos);
|
|
||||||
logger.info('Forward offset:', forwardOffset);
|
|
||||||
logger.info('Left offset:', leftOffset);
|
|
||||||
|
|
||||||
const toolboxLocalPos = Vector3.TransformCoordinates(toolboxWorldPos, platform.getWorldMatrix().invert());
|
|
||||||
logger.info('Calculated toolbox local position:', toolboxLocalPos);
|
|
||||||
|
|
||||||
toolbox.handleMesh.position = toolboxLocalPos;
|
|
||||||
|
|
||||||
// Orient toolbox to face the user
|
|
||||||
const toolboxToCamera = cameraWorldPos.subtract(toolboxWorldPos).normalize();
|
|
||||||
const toolboxYaw = Math.atan2(toolboxToCamera.x, toolboxToCamera.z);
|
|
||||||
toolbox.handleMesh.rotation.y = toolboxYaw;
|
|
||||||
|
|
||||||
logger.info('Toolbox handleMesh AFTER positioning:', {
|
|
||||||
position: toolbox.handleMesh.position.clone(),
|
|
||||||
absolutePosition: toolbox.handleMesh.getAbsolutePosition().clone(),
|
|
||||||
rotation: toolbox.handleMesh.rotation.clone()
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position input text view: Camera-relative positioning disabled to respect default/saved positions
|
|
||||||
// Handles now use their configured defaults or saved localStorage positions
|
|
||||||
const inputTextView = diagramManager.diagramMenuManager['_inputTextView'];
|
const inputTextView = diagramManager.diagramMenuManager['_inputTextView'];
|
||||||
if (inputTextView && inputTextView.handleMesh) {
|
if (inputTextView && inputTextView.handleMesh) {
|
||||||
logger.info('InputTextView handleMesh using default/saved position:', {
|
logger.info('InputTextView handleMesh using default/saved position:', {
|
||||||
position: inputTextView.handleMesh.position.clone(),
|
position: inputTextView.handleMesh.position.clone(),
|
||||||
absolutePosition: inputTextView.handleMesh.getAbsolutePosition().clone()
|
absolutePosition: inputTextView.handleMesh.getAbsolutePosition().clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Camera-relative positioning commented out - handles use their own defaults
|
|
||||||
/*
|
|
||||||
// NOTE: User faces -Z direction by design, so negate forward offset
|
|
||||||
const inputWorldPos = cameraWorldPos.add(horizontalForward.scale(-0.5));
|
|
||||||
inputWorldPos.y = cameraWorldPos.y - 0.4; // Below eye level
|
|
||||||
|
|
||||||
logger.info('Calculated input world position:', inputWorldPos);
|
|
||||||
|
|
||||||
const inputLocalPos = Vector3.TransformCoordinates(inputWorldPos, platform.getWorldMatrix().invert());
|
|
||||||
logger.info('Calculated input local position:', inputLocalPos);
|
|
||||||
|
|
||||||
inputTextView.handleMesh.position = inputLocalPos;
|
|
||||||
|
|
||||||
logger.info('InputTextView handleMesh AFTER positioning:', {
|
|
||||||
position: inputTextView.handleMesh.position.clone(),
|
|
||||||
absolutePosition: inputTextView.handleMesh.getAbsolutePosition().clone()
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,23 +23,6 @@ export function addSceneInspector() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*import("@babylonjs/core/Debug").then(() => {
|
|
||||||
import("@babylonjs/inspector").then(() => {
|
|
||||||
const web = document.querySelector('#webApp');
|
|
||||||
if (scene.debugLayer.isVisible()) {
|
|
||||||
if (web) {
|
|
||||||
(web as HTMLDivElement).style.display = 'block';
|
|
||||||
}
|
|
||||||
scene.debugLayer.hide();
|
|
||||||
} else {
|
|
||||||
scene.debugLayer.show();
|
|
||||||
if (web) {
|
|
||||||
(web as HTMLDivElement).style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import {Color3, DynamicTexture, HemisphericLight, PointLight, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
|
import {Color3, DynamicTexture, HemisphericLight, PointLight, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
|
||||||
import {DefaultScene} from "../defaultScene";
|
|
||||||
import {RenderingMode} from "./renderingMode";
|
import {RenderingMode} from "./renderingMode";
|
||||||
|
|
||||||
export class LightmapGenerator {
|
export class LightmapGenerator {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user