import * as THREE from 'three'; import type { GeometryCache } from './geometryCache'; import { meshToDto, type MeshDto } from '../backend/dto/mesh'; import { model } from '../model/model'; import { Geometry } from '../backend/geometry/geometry'; import type { Id } from '../types'; export class SceneSync { private scene: THREE.Scene; private meshByFace: Record = {}; // faceId → THREE.Mesh private cache: GeometryCache; private _selectedFaceIds: Id[] = []; private readonly baseMaterial = new THREE.MeshPhongMaterial({ color: 0x4a7fc8, shininess: 40, specular: 0x223344, wireframe: false, }); constructor(scene: THREE.Scene, cache: GeometryCache) { this.scene = scene; this.cache = cache; } public get selectedFaceIds() { return this._selectedFaceIds; } public get meshes(): THREE.Mesh[] { return Object.values(this.meshByFace); } public get items(): Record { return this.meshByFace; } addWholeModel() { for (const id of Object.keys(model.solids)) { const meshes = Geometry .tessellateSolid(id) .map(meshToDto); this.addSolid(meshes[2]); // for (const mesh of meshes) // this.addSolid(mesh); } } // Called when FE scene graph syncs from BE addSolid(dto: MeshDto) { const faceId = dto.faceId; if (this.meshByFace[faceId]) return; const geo = this.cache.getOrCreate(faceId, 0, dto); const mesh = new THREE.Mesh(geo, this.baseMaterial.clone()); mesh.userData.faceId = faceId; mesh.userData.vertexIds = dto.vertexIds; mesh.userData.surfaceId = dto.surfaceId; mesh.userData.solidId = dto.solidId; this.scene.add(mesh); this.meshByFace[faceId] = mesh; } setSelected(faceIds: Id[]) { this._selectedFaceIds = faceIds; for (const [sid, mesh] of Object.entries(this.meshByFace)) { const mat = mesh.material as THREE.MeshPhongMaterial; if (faceIds.includes(sid)) { mat.color.setHex(0xf0a040); mat.emissive.setHex(0x221100); } else { mat.color.setHex(0x4a7fc8); mat.emissive.setHex(0x000000); } } } public disposeMesh(faceId: Id) { const mesh = this.meshByFace[faceId]; if (!mesh) return; this.scene.remove(mesh); mesh.geometry.dispose(); if (Array.isArray(mesh.material)) { for (const mat of mesh.material) mat.dispose(); } else mesh.material.dispose(); this.cache.unset(faceId, 0); delete (this.meshByFace[faceId]); } public dispose() { for (const faceId of Object.keys(this.meshByFace)) this.disposeMesh(faceId); this.baseMaterial.dispose(); } }