feat: refactor workflow editor authoring surfaces

This commit is contained in:
2026-04-08 21:44:08 +02:00
parent fe46dabfc5
commit 042f62fe55
25 changed files with 4877 additions and 1823 deletions
@@ -0,0 +1,202 @@
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')
})
})