import * as THREE from 'three'; /** * Registry of the innermost local-frame group of each model in the scene. * Measurements are stored in this local frame so they follow the model * when the user repositions/rotates it via the "Posicionar" tool. */ const localGroups = new Map(); export function registerModelLocalGroup(modelId: string, obj: THREE.Object3D) { localGroups.set(modelId, obj); } export function unregisterModelLocalGroup(modelId: string, obj?: THREE.Object3D) { // Only delete if the registered object matches (avoid race between unmount/mount of the same id) const current = localGroups.get(modelId); if (!obj || current === obj) localGroups.delete(modelId); } export function getModelLocalGroup(modelId: string | null | undefined): THREE.Object3D | undefined { if (!modelId) return undefined; return localGroups.get(modelId); } export function getAllModelLocalGroups(): THREE.Object3D[] { return Array.from(localGroups.values()); } export function worldToModelLocal( modelId: string | null | undefined, p: { x: number; y: number; z: number }, ): { x: number; y: number; z: number } | null { const g = getModelLocalGroup(modelId); if (!g) return null; g.updateWorldMatrix(true, false); const v = new THREE.Vector3(p.x, p.y, p.z); g.worldToLocal(v); return { x: v.x, y: v.y, z: v.z }; } export function modelLocalToWorld( modelId: string | null | undefined, p: { x: number; y: number; z: number }, ): { x: number; y: number; z: number } | null { const g = getModelLocalGroup(modelId); if (!g) return null; g.updateWorldMatrix(true, false); const v = new THREE.Vector3(p.x, p.y, p.z); g.localToWorld(v); return { x: v.x, y: v.y, z: v.z }; } export function getModelWorldScaleFactor(modelId: string | null | undefined): number { const g = getModelLocalGroup(modelId); if (!g) return 1; g.updateWorldMatrix(true, false); const s = new THREE.Vector3(); g.getWorldScale(s); const factor = (Math.abs(s.x) + Math.abs(s.y) + Math.abs(s.z)) / 3; return Number.isFinite(factor) && factor > 1e-6 ? factor : 1; }