import { describe, expect, test } from 'vitest' import * as THREE from 'three' import { buildScenePartRegistry, convertSceneManifestMaterials, remapToPartKeys, resolveObjectPartKey } from '../../components/cad/cadUtils' describe('cadUtils scene manifest conversion', () => { test('uses scene manifest part keys as authoritative viewer material map', () => { const materials = convertSceneManifestMaterials([ { part_key: 'rwdr_b_f_802044_tr4_h122bk', effective_material: 'HARTOMAT_010101_Steel-Bare', }, { part_key: 'o_ring_rg_f_802044_tr4_h122bk_1', effective_material: 'HARTOMAT_050101_Elastomer-Black', }, { part_key: 'ignored', effective_material: null, }, ]) expect(materials).toEqual({ rwdr_b_f_802044_tr4_h122bk: { type: 'library', value: 'HARTOMAT_010101_Steel-Bare', }, o_ring_rg_f_802044_tr4_h122bk_1: { type: 'library', value: 'HARTOMAT_050101_Elastomer-Black', }, }) }) }) describe('cadUtils legacy fallback remapping', () => { test('remaps simple legacy source keys onto part keys when no scene manifest exists', () => { const materials = remapToPartKeys( { 'F-802044-0011_AU_TR1_04_1': { type: 'library', value: 'Steel--Stahl' }, 'RWDR_B_F-802044_TR4_H122BK': { type: 'library', value: 'Steel--Stahl' }, }, { 'F-802044-0011_AU_TR1_04': 'f_802044_0011_au_tr1_04', 'RWDR_B_F-802044_TR4_H122BK': 'rwdr_b_f_802044_tr4_h122bk', }, ) expect(materials).toEqual({ f_802044_0011_au_tr1_04: { type: 'library', value: 'Steel--Stahl' }, rwdr_b_f_802044_tr4_h122bk: { type: 'library', value: 'Steel--Stahl' }, }) }) test('remaps serialized instance names using blender-style fuzzy lookup', () => { const materials = remapToPartKeys( { 'RWDR_B_F-802044_TR4_H122B-69186': { type: 'library', value: 'Steel--Stahl' }, 'RWDR_K_F-802044_TR4_H122B-68272': { type: 'library', value: 'Steel--Stahl' }, 'RWDR_F_F-802044_TR4_H122B-69391': { type: 'library', value: 'Steel--Stahl' }, 'O_RING_RG_F-802044_TR4_H-120220': { type: 'library', value: 'Eslastomer_black--Elastomer_schwarz' }, 'F-802044-3001_IR_TR2-H_A1-25921_AF0': { type: 'library', value: 'Steel--Stahl' }, 'F-802044-0011_AU_TR1_04_1_AF0_1': { type: 'library', value: 'Steel--Stahl' }, }, { 'RWDR_B_F-802044_TR4_H122BK': 'rwdr_b_f_802044_tr4_h122bk', 'RWDR_K_F-802044_TR4_H122BK': 'rwdr_k_f_802044_tr4_h122bk', 'RWDR_F_F-802044_TR4_H122BK': 'rwdr_f_f_802044_tr4_h122bk', 'O_RING_RG_F-802044_TR4_H122BK': 'o_ring_rg_f_802044_tr4_h122bk', 'F-802044-3001_IR_TR2-H_A1_04': 'f_802044_3001_ir_tr2_h_a1_04', 'F-802044-0011_AU_TR1_04_1': 'f_802044_0011_au_tr1_04_1', }, ) expect(materials.rwdr_b_f_802044_tr4_h122bk).toEqual({ type: 'library', value: 'Steel--Stahl', }) expect(materials.rwdr_k_f_802044_tr4_h122bk).toEqual({ type: 'library', value: 'Steel--Stahl', }) expect(materials.rwdr_f_f_802044_tr4_h122bk).toEqual({ type: 'library', value: 'Steel--Stahl', }) expect(materials.o_ring_rg_f_802044_tr4_h122bk).toEqual({ type: 'library', value: 'Eslastomer_black--Elastomer_schwarz', }) expect(materials.f_802044_3001_ir_tr2_h_a1_04).toEqual({ type: 'library', value: 'Steel--Stahl', }) expect(materials.f_802044_0011_au_tr1_04_1).toEqual({ type: 'library', value: 'Steel--Stahl', }) }) test('keeps ambiguous fuzzy matches unresolved', () => { const materials = remapToPartKeys( { 'PART_ALPHA-11111': { type: 'library', value: 'Steel--Stahl' }, 'PART_ALPHA-22222': { type: 'library', value: 'Bronze--Bronze' }, }, { PART_ALPHA: 'part_alpha', }, ) expect(materials.part_alpha).toBeUndefined() }) }) describe('cadUtils scene graph part-key registry', () => { test('inherits instance part keys from ancestor nodes and keeps logical keys from scene metadata', () => { const scene = new THREE.Group() const instanceGroup = new THREE.Group() instanceGroup.name = 'KERO_Z-575693-QP-DRH_ISB_1' instanceGroup.userData.partKey = 'kero_z_575693_qp_drh_isb_1' const mesh = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshStandardMaterial()) mesh.name = 'KERO_Z-575693-QP-DRH_ISB_1_1' instanceGroup.add(mesh) const logicalOnlyNode = new THREE.Group() logicalOnlyNode.name = 'RWDR_SKEL_F-802044_TR4_H122BK' logicalOnlyNode.userData.partKey = 'rwdr_skel_f_802044_tr4_h122bk' scene.add(instanceGroup) scene.add(logicalOnlyNode) const { meshRegistry, logicalPartKeys } = buildScenePartRegistry(scene, { 'F-802044_TR4-H122BK_04': 'f_802044_tr4_h122bk_04', }) expect(meshRegistry).toHaveLength(1) expect(meshRegistry[0].partKey).toBe('kero_z_575693_qp_drh_isb_1') expect(resolveObjectPartKey(mesh, {})).toBe('kero_z_575693_qp_drh_isb_1') expect(logicalPartKeys).toEqual(new Set([ 'kero_z_575693_qp_drh_isb_1', 'rwdr_skel_f_802044_tr4_h122bk', 'f_802044_tr4_h122bk_04', ])) }) test('prefers sibling semantic instance nodes over mesh-local exporter keys when transforms match', () => { const scene = new THREE.Group() const assembly = new THREE.Group() const semanticSibling = new THREE.Group() semanticSibling.name = 'KERO_Z-575693-QP-DRH_ISB_1_AF21' semanticSibling.userData.partKey = 'kero_z_575693_qp_drh_isb_1' semanticSibling.position.set(0.08113920585353, 0.236350432177, 0.2109037401181) semanticSibling.quaternion.set(0.10417282880530283, -0.01738337059295405, -0.1636740589602252, 0.9808448616315497) const mesh = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshStandardMaterial()) mesh.name = 'KERO_Z-575693-QP-DRH_ISB_1_1' mesh.userData.partKey = 'kero_z_575693_qp_drh_isb_1_1' mesh.position.copy(semanticSibling.position) mesh.quaternion.copy(semanticSibling.quaternion) assembly.add(semanticSibling) assembly.add(mesh) scene.add(assembly) const { meshRegistry } = buildScenePartRegistry(scene, {}) expect(meshRegistry).toHaveLength(1) expect(meshRegistry[0].partKey).toBe('kero_z_575693_qp_drh_isb_1') expect(mesh.userData.partKey).toBe('kero_z_575693_qp_drh_isb_1') expect(resolveObjectPartKey(mesh, {})).toBe('kero_z_575693_qp_drh_isb_1') }) test('prefers sibling semantic instance nodes even when transforms do not match', () => { const scene = new THREE.Group() const assembly = new THREE.Group() const semanticSibling = new THREE.Group() semanticSibling.name = 'KERO_Z-575693-QP-DRH_ISB_1_AF21' semanticSibling.userData.partKey = 'kero_z_575693_qp_drh_isb_1' semanticSibling.position.set(0.08113920585353, 0.236350432177, 0.2109037401181) const mesh = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshStandardMaterial()) mesh.name = 'KERO_Z-575693-QP-DRH_ISB_1_1' mesh.userData.partKey = 'kero_z_575693_qp_drh_isb_1_1' mesh.position.set(0.2422435981345, 0.06134441033723, 0.2109037401181) assembly.add(semanticSibling) assembly.add(mesh) scene.add(assembly) const { meshRegistry } = buildScenePartRegistry(scene, {}) expect(meshRegistry).toHaveLength(1) expect(meshRegistry[0].partKey).toBe('kero_z_575693_qp_drh_isb_1') expect(mesh.userData.partKey).toBe('kero_z_575693_qp_drh_isb_1') expect(resolveObjectPartKey(mesh, {})).toBe('kero_z_575693_qp_drh_isb_1') }) })