@operato/scene-storage 10.0.0-beta.44 → 10.0.0-beta.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/dist/crane-3d.d.ts +10 -0
- package/dist/crane-3d.js +34 -5
- package/dist/crane-3d.js.map +1 -1
- package/dist/crane.d.ts +136 -6
- package/dist/crane.js +578 -48
- package/dist/crane.js.map +1 -1
- package/dist/parcel-3d.d.ts +1 -0
- package/dist/parcel-3d.js +18 -1
- package/dist/parcel-3d.js.map +1 -1
- package/dist/rack-grid-3d.js +26 -8
- package/dist/rack-grid-3d.js.map +1 -1
- package/dist/rack-grid.d.ts +103 -10
- package/dist/rack-grid.js +484 -86
- package/dist/rack-grid.js.map +1 -1
- package/dist/storage-rack-3d.js +1 -1
- package/dist/storage-rack-3d.js.map +1 -1
- package/dist/storage-rack.d.ts +40 -6
- package/dist/storage-rack.js +111 -14
- package/dist/storage-rack.js.map +1 -1
- package/package.json +4 -4
- package/src/crane-3d.ts +34 -4
- package/src/crane.ts +625 -57
- package/src/parcel-3d.ts +19 -1
- package/src/rack-grid-3d.ts +31 -8
- package/src/rack-grid.ts +504 -82
- package/src/storage-rack-3d.ts +1 -1
- package/src/storage-rack.ts +111 -14
- package/test/test-coord-alignment.ts +2 -2
- package/test/test-crane-bay-match.ts +130 -0
- package/test/test-crane-binding-resolve.ts +168 -0
- package/test/test-crane-duration.ts +90 -0
- package/test/test-crane-rotation-reach.ts +218 -0
- package/test/test-rack-grid-3d-alignment.ts +235 -0
- package/test/test-rack-grid-3d-attach-real.ts +375 -0
- package/test/test-rack-grid-cell.ts +2 -2
- package/test/test-rack-grid-location.ts +2 -2
- package/test/test-rack-grid-occupied-slots.ts +165 -0
- package/test/test-rack-grid-picking-position.ts +154 -0
- package/test/test-rack-grid-slot-api.ts +483 -0
- package/test/test-slot-ids-enumeration.ts +137 -0
- package/test/things-scene-loader-impl.mjs +37 -0
- package/test/things-scene-loader.mjs +24 -0
- package/translations/en.json +2 -0
- package/translations/ja.json +2 -0
- package/translations/ko.json +2 -0
- package/translations/ms.json +2 -0
- package/translations/zh.json +2 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/storage-rack-3d.js
CHANGED
|
@@ -173,7 +173,7 @@ export class StorageRack3D extends RealObjectGroup {
|
|
|
173
173
|
const cellMap = rack.cellMap;
|
|
174
174
|
if (!cellMap)
|
|
175
175
|
return;
|
|
176
|
-
// rack 의 기하 파라미터 — cellMap /
|
|
176
|
+
// rack 의 기하 파라미터 — cellMap / _ensureSlotAttachObject3d 의 levelHeight
|
|
177
177
|
// 계산과 *반드시* 일치해야 함 (shelfBaseHeight > 0 일 때 stock 시각 위치와
|
|
178
178
|
// crane fork target 이 어긋나는 회귀 차단).
|
|
179
179
|
const rs = this.component.state;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-rack-3d.js","sourceRoot":"","sources":["../src/storage-rack-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Rack 3D — multi-level storage shelf system.\n *\n * LO-POLY but visually unambiguous as a rack. The signature parts:\n *\n * - 4 corner uprights (vertical posts running floor → top)\n * - intermediate uprights between bays (one between each adjacent bay pair)\n * - horizontal beams at each level on both front and back faces (defining\n * the cell decks)\n * - diagonal cross-bracing on the back face (the \"X\" pattern that says\n * this is a load-bearing storage rack, not just a generic frame)\n *\n * No floor / ceiling panels — the rack is open by design (cells are accessed\n * by a picker from the front side).\n *\n * Cargo (pallets, boxes) added as children render at their own z position.\n * The rack itself is purely structural geometry.\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nimport { POST_MATERIAL, BEAM_MATERIAL, BRACE_MATERIAL, SHELF_MATERIAL, STOCK_MATERIAL } from './rack-materials.js'\n\nconst BEAM_COLOR = 0x556070 // shelf material 의 color 와 일치 — 일부 코멘트 참조\n\n// ── Stock visualization 공용 자원 ────────────────────────────────────────────\nconst STOCK_GEOMETRY_CACHE = new Map<string, THREE.BufferGeometry>()\nconst STOCK_DEFAULT_COLOR = '#c8a878' // cardboard 색 (legend 매칭 없을 때)\n\nfunction getStockGeometry(w: number, h: number, d: number): THREE.BufferGeometry {\n const k = `${w.toFixed(1)}-${h.toFixed(1)}-${d.toFixed(1)}`\n let g = STOCK_GEOMETRY_CACHE.get(k)\n if (!g) {\n g = new THREE.BoxGeometry(w, d, h) // X=w, Y=d (vertical), Z=h\n STOCK_GEOMETRY_CACHE.set(k, g)\n }\n return g\n}\n\nexport class StorageRack3D extends RealObjectGroup {\n /** state.data 기반 stock 시각화 InstancedMesh. rebuildStockMesh 가 관리. */\n private _stockMesh?: THREE.InstancedMesh\n /** Horizontal beam 그룹 — hideHorizontalFrame 시 visibility 토글. */\n private _beamGroup?: THREE.Group\n\n /** Public read-only — click 핸들러가 instanceId/record 역참조에 사용. */\n get stockMesh(): THREE.InstancedMesh | undefined {\n return this._stockMesh\n }\n\n /** hideHorizontalFrame 변경 시 RackGrid 의 onchange 가 호출 — 즉시 반영. */\n applyFrameVisibility(): void {\n const hide = !!(this.component.state as any)?.hideHorizontalFrame\n if (this._beamGroup) this._beamGroup.visible = !hide\n }\n\n build() {\n super.build()\n\n const { width, height, depth = 3000 } = this.component.state\n const levels = Math.max(1, Math.floor((this.component.state.levels as number) || 4))\n const bays = Math.max(1, Math.floor((this.component.state.bays as number) || 5))\n const shelfBase = Math.max(0, Math.min(\n (this.component.state.shelfBaseHeight as number) || 0,\n depth * 0.9\n ))\n const shelfZone = depth - shelfBase // 실제 shelf 가 차지하는 Y\n\n const baseY = -depth / 2 // rack 바닥 (3D Y 의 최저)\n const shelfBaseY = baseY + shelfBase // 첫 shelf 의 시작 (= level 1 의 바닥)\n const postW = Math.min(width / bays, height) * 0.06\n // beam 두께 = post 와 비슷 (산업 beam 이 post 보다 약간 두꺼움 — 1.2배)\n const beamH = postW * 1.2\n const braceT = postW * 0.6\n\n // Material 은 module-level singleton (rack-materials.ts) — 인스턴스 별 생성 X.\n\n // ── Uprights (vertical posts at every bay boundary) ──────────────\n // bays + 1 vertical positions; for each, one front post + one back post.\n const postGeos: THREE.BufferGeometry[] = []\n for (let i = 0; i <= bays; i++) {\n const xFrac = i / bays - 0.5\n const x = xFrac * width\n // Front + back posts\n for (const zSign of [-1, 1]) {\n const post = new THREE.BoxGeometry(postW, depth, postW)\n post.translate(x, 0, zSign * (height / 2 - postW / 2))\n postGeos.push(post)\n }\n }\n const postMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(postGeos), POST_MATERIAL)\n postMesh.castShadow = true\n postMesh.receiveShadow = true\n this.object3d.add(postMesh)\n\n // ── Horizontal beams (front + back faces at each level) ──────────\n // shelf zone 안 levels+1 위치 (level 0 = shelfBase, level N = 천장).\n const beamGeos: THREE.BufferGeometry[] = []\n for (let lv = 0; lv <= levels; lv++) {\n const yFrac = lv / levels\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n\n for (const zSign of [-1, 1]) {\n const beam = new THREE.BoxGeometry(width, beamH, beamH)\n beam.translate(0, y, zSign * (height / 2 - beamH / 2))\n beamGeos.push(beam)\n }\n }\n const beamMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(beamGeos), BEAM_MATERIAL)\n beamMesh.castShadow = true\n beamMesh.receiveShadow = true\n // hideHorizontalFrame 즉시 토글 위한 별도 group\n this._beamGroup = new THREE.Group()\n this._beamGroup.add(beamMesh)\n this._beamGroup.visible = !((this.component.state as any)?.hideHorizontalFrame)\n this.object3d.add(this._beamGroup)\n\n // ── Diagonal cross-bracing on the back face (the \"X\" pattern) ────\n // Two diagonals per level — \"/\" and \"\\\" — making an X across each\n // bay-tall cell. Visual signature of a load-bearing rack.\n const braceGeos: THREE.BufferGeometry[] = []\n const cellW = width / bays\n const cellH = shelfZone / levels // cell 높이 (shelf zone 안)\n const braceLen = Math.sqrt(cellW * cellW + cellH * cellH)\n const braceAngle = Math.atan2(cellH, cellW)\n const backZ = height / 2 - postW * 0.6\n\n for (let bay = 0; bay < bays; bay++) {\n // Brace only every other bay to keep things visually open\n if (bay % 2 !== 0) continue\n\n const cellCenterX = (bay - bays / 2 + 0.5) * cellW\n\n for (let lv = 0; lv < levels; lv++) {\n const cellCenterY = shelfBaseY + (lv + 0.5) * cellH\n\n for (const sign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(braceLen, braceT, braceT)\n brace.rotateZ(sign * braceAngle)\n brace.translate(cellCenterX, cellCenterY, backZ)\n braceGeos.push(brace)\n }\n }\n }\n if (braceGeos.length > 0) {\n const braceMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(braceGeos), BRACE_MATERIAL)\n braceMesh.castShadow = true\n this.object3d.add(braceMesh)\n }\n\n // ── Shelf planes (level 별 반투명 무볼륨 판) ────────────────────────────\n // 각 level 의 *바닥 면* 에 plane — cell 위치 시각 인식. carrier 가 그 위\n // 에 놓이는 *지지면*. 반투명.\n //\n // X-Z 넓이를 *frame 안쪽* 으로 줄여 mesh 겹침 자체 제거 (Z-fight 회피).\n // X: 양 옆 corner post 안쪽 (-postW 양쪽)\n // Z: 앞/뒤 beam 안쪽 (-beamH 양쪽)\n const shelfW = Math.max(0, width - 2 * postW)\n const shelfD = Math.max(0, height - 2 * beamH)\n const shelfGeo = new THREE.PlaneGeometry(shelfW, shelfD)\n shelfGeo.rotateX(-Math.PI / 2) // X-Y plane → X-Z plane (= horizontal)\n const shelfMaterial = SHELF_MATERIAL\n for (let lv = 0; lv < levels; lv++) {\n // shelf plane Y = 해당 level 의 *load beam top* 정확 일치 (cell 바닥 면).\n // beam center Y = shelfBaseY + yFrac*shelfZone - beamH/2 + (lv===0 ? beamH : 0)\n // beam top Y = beam center + beamH/2 = shelfBaseY + yFrac*shelfZone + (lv===0 ? beamH : 0)\n const yFrac = lv / levels\n const y = shelfBaseY + yFrac * shelfZone + (lv === 0 ? beamH : 0)\n const shelf = new THREE.Mesh(shelfGeo, shelfMaterial)\n shelf.position.set(0, y, 0)\n shelf.receiveShadow = true\n this.object3d.add(shelf)\n }\n\n // state.data 가 있으면 stock InstancedMesh 도 빌드\n this.rebuildStockMesh()\n }\n\n /**\n * state.data 의 각 record 를 한 InstancedMesh instance 로 렌더링.\n * cellMap 에 있는 cellId 만 instance 부여 — 나머지는 무시.\n * Mover/pickAndPlace 와 무관 — 순수 시각화.\n */\n rebuildStockMesh(): void {\n if (this._stockMesh) {\n this.object3d.remove(this._stockMesh)\n this._stockMesh = undefined\n }\n\n const rack: any = this.component\n const data = rack.state?.data as Array<{ cellId: string;[key: string]: any }> | undefined\n if (!Array.isArray(data) || data.length === 0) return\n\n const cellMap = rack.cellMap\n if (!cellMap) return\n\n // rack 의 기하 파라미터 — cellMap / _ensureCellAttachObject3d 의 levelHeight\n // 계산과 *반드시* 일치해야 함 (shelfBaseHeight > 0 일 때 stock 시각 위치와\n // crane fork target 이 어긋나는 회귀 차단).\n const rs: any = this.component.state\n const rackWidth = (rs?.width as number) || 1000\n const rackDepth = (rs?.depth as number) || 3000\n const rackHeight = (rs?.height as number) || 600\n const bays = Math.max(1, Math.floor(rs?.bays || 5))\n const levels = Math.max(1, Math.floor(rs?.levels || 4))\n const shelfBase = Math.max(0, Math.min((rs?.shelfBaseHeight as number) || 0, rackDepth * 0.9))\n const shelfZone = rackDepth - shelfBase\n const bayWidth = rackWidth / bays\n const levelHeight = shelfZone / levels // ← shelfZone 기준 (cellMap 과 동일)\n const rowDepth = rackHeight\n\n // 한 stock 의 크기 — cell 의 85% × 85% × 70%\n const stockW = bayWidth * 0.85\n const stockH = rowDepth * 0.85\n const stockD = levelHeight * 0.7\n const geo = getStockGeometry(stockW, stockH, stockD)\n\n // 유효한 record 만 추출\n const valid: { cell: any; record: any }[] = []\n for (const r of data) {\n if (!r?.cellId) continue\n const cell = cellMap.findById(r.cellId)\n if (cell) valid.push({ cell, record: r })\n }\n if (valid.length === 0) return\n\n const inst = new THREE.InstancedMesh(geo, STOCK_MATERIAL, valid.length)\n inst.castShadow = false\n inst.receiveShadow = false\n inst.frustumCulled = false\n // things-scene EventManager3D 의 raycast hit-test 가 walk-up 하며 찾는 키:\n // userData.context 에 RealObject 자체 set.\n // 객체 전체 대체가 아니라 속성만 set 해 Three.js 의 기존 userData 필드 보존.\n inst.userData.context = this\n // 클릭 핸들러가 instanceId 로 record 를 역참조할 수 있도록 순서대로 저장.\n inst.userData._records = valid.map(v => v.record)\n\n const m = new THREE.Matrix4()\n const pos = new THREE.Vector3()\n const q = new THREE.Quaternion()\n const s = new THREE.Vector3(1, 1, 1)\n const c = new THREE.Color()\n\n for (let i = 0; i < valid.length; i++) {\n const { cell, record } = valid[i]\n // cell 중심 (rack-center 좌표계)\n const x = cell.localPosition.x + bayWidth / 2 - rackWidth / 2\n const cellCenterY = cell.localPosition.y + levelHeight / 2 - rackDepth / 2\n // stock 바닥 = cell 바닥 → stock 중심 Y = cell 중심 Y - levelHeight/2 + stockD/2\n const y = cellCenterY - levelHeight / 2 + stockD / 2\n const z = cell.localPosition.z + rowDepth / 2 - rackHeight / 2\n\n pos.set(x, y, z)\n m.compose(pos, q, s)\n inst.setMatrixAt(i, m)\n\n // Legend 색상 매핑 — rack 의 resolveLegendColor 호출, 매칭 없으면 default\n const resolved = (rack as any).resolveLegendColor?.(record) ?? STOCK_DEFAULT_COLOR\n c.set(resolved)\n inst.setColorAt(i, c)\n }\n inst.instanceMatrix.needsUpdate = true\n if (inst.instanceColor) inst.instanceColor.needsUpdate = true\n\n // ── CRITICAL: bounding sphere 계산 ────────────────────────────────────────\n // Three.js InstancedMesh.raycast 는 먼저 bounding sphere 로 broad-phase 체크.\n // setMatrixAt 만 호출하고 computeBoundingSphere 를 안 부르면 sphere.radius=0\n // → 어떤 ray 도 안 맞음 → click event 발화 안 됨. raycaster 가 instance 인식\n // 못 하는 것의 *결정적 원인*. needsUpdate 후 명시 호출 필수.\n inst.computeBoundingSphere()\n inst.computeBoundingBox?.()\n\n this.object3d.add(inst)\n this._stockMesh = inst\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'levels' in after ||\n 'bays' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after ||\n 'shelfBaseHeight' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"storage-rack-3d.js","sourceRoot":"","sources":["../src/storage-rack-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Rack 3D — multi-level storage shelf system.\n *\n * LO-POLY but visually unambiguous as a rack. The signature parts:\n *\n * - 4 corner uprights (vertical posts running floor → top)\n * - intermediate uprights between bays (one between each adjacent bay pair)\n * - horizontal beams at each level on both front and back faces (defining\n * the cell decks)\n * - diagonal cross-bracing on the back face (the \"X\" pattern that says\n * this is a load-bearing storage rack, not just a generic frame)\n *\n * No floor / ceiling panels — the rack is open by design (cells are accessed\n * by a picker from the front side).\n *\n * Cargo (pallets, boxes) added as children render at their own z position.\n * The rack itself is purely structural geometry.\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nimport { POST_MATERIAL, BEAM_MATERIAL, BRACE_MATERIAL, SHELF_MATERIAL, STOCK_MATERIAL } from './rack-materials.js'\n\nconst BEAM_COLOR = 0x556070 // shelf material 의 color 와 일치 — 일부 코멘트 참조\n\n// ── Stock visualization 공용 자원 ────────────────────────────────────────────\nconst STOCK_GEOMETRY_CACHE = new Map<string, THREE.BufferGeometry>()\nconst STOCK_DEFAULT_COLOR = '#c8a878' // cardboard 색 (legend 매칭 없을 때)\n\nfunction getStockGeometry(w: number, h: number, d: number): THREE.BufferGeometry {\n const k = `${w.toFixed(1)}-${h.toFixed(1)}-${d.toFixed(1)}`\n let g = STOCK_GEOMETRY_CACHE.get(k)\n if (!g) {\n g = new THREE.BoxGeometry(w, d, h) // X=w, Y=d (vertical), Z=h\n STOCK_GEOMETRY_CACHE.set(k, g)\n }\n return g\n}\n\nexport class StorageRack3D extends RealObjectGroup {\n /** state.data 기반 stock 시각화 InstancedMesh. rebuildStockMesh 가 관리. */\n private _stockMesh?: THREE.InstancedMesh\n /** Horizontal beam 그룹 — hideHorizontalFrame 시 visibility 토글. */\n private _beamGroup?: THREE.Group\n\n /** Public read-only — click 핸들러가 instanceId/record 역참조에 사용. */\n get stockMesh(): THREE.InstancedMesh | undefined {\n return this._stockMesh\n }\n\n /** hideHorizontalFrame 변경 시 RackGrid 의 onchange 가 호출 — 즉시 반영. */\n applyFrameVisibility(): void {\n const hide = !!(this.component.state as any)?.hideHorizontalFrame\n if (this._beamGroup) this._beamGroup.visible = !hide\n }\n\n build() {\n super.build()\n\n const { width, height, depth = 3000 } = this.component.state\n const levels = Math.max(1, Math.floor((this.component.state.levels as number) || 4))\n const bays = Math.max(1, Math.floor((this.component.state.bays as number) || 5))\n const shelfBase = Math.max(0, Math.min(\n (this.component.state.shelfBaseHeight as number) || 0,\n depth * 0.9\n ))\n const shelfZone = depth - shelfBase // 실제 shelf 가 차지하는 Y\n\n const baseY = -depth / 2 // rack 바닥 (3D Y 의 최저)\n const shelfBaseY = baseY + shelfBase // 첫 shelf 의 시작 (= level 1 의 바닥)\n const postW = Math.min(width / bays, height) * 0.06\n // beam 두께 = post 와 비슷 (산업 beam 이 post 보다 약간 두꺼움 — 1.2배)\n const beamH = postW * 1.2\n const braceT = postW * 0.6\n\n // Material 은 module-level singleton (rack-materials.ts) — 인스턴스 별 생성 X.\n\n // ── Uprights (vertical posts at every bay boundary) ──────────────\n // bays + 1 vertical positions; for each, one front post + one back post.\n const postGeos: THREE.BufferGeometry[] = []\n for (let i = 0; i <= bays; i++) {\n const xFrac = i / bays - 0.5\n const x = xFrac * width\n // Front + back posts\n for (const zSign of [-1, 1]) {\n const post = new THREE.BoxGeometry(postW, depth, postW)\n post.translate(x, 0, zSign * (height / 2 - postW / 2))\n postGeos.push(post)\n }\n }\n const postMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(postGeos), POST_MATERIAL)\n postMesh.castShadow = true\n postMesh.receiveShadow = true\n this.object3d.add(postMesh)\n\n // ── Horizontal beams (front + back faces at each level) ──────────\n // shelf zone 안 levels+1 위치 (level 0 = shelfBase, level N = 천장).\n const beamGeos: THREE.BufferGeometry[] = []\n for (let lv = 0; lv <= levels; lv++) {\n const yFrac = lv / levels\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n\n for (const zSign of [-1, 1]) {\n const beam = new THREE.BoxGeometry(width, beamH, beamH)\n beam.translate(0, y, zSign * (height / 2 - beamH / 2))\n beamGeos.push(beam)\n }\n }\n const beamMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(beamGeos), BEAM_MATERIAL)\n beamMesh.castShadow = true\n beamMesh.receiveShadow = true\n // hideHorizontalFrame 즉시 토글 위한 별도 group\n this._beamGroup = new THREE.Group()\n this._beamGroup.add(beamMesh)\n this._beamGroup.visible = !((this.component.state as any)?.hideHorizontalFrame)\n this.object3d.add(this._beamGroup)\n\n // ── Diagonal cross-bracing on the back face (the \"X\" pattern) ────\n // Two diagonals per level — \"/\" and \"\\\" — making an X across each\n // bay-tall cell. Visual signature of a load-bearing rack.\n const braceGeos: THREE.BufferGeometry[] = []\n const cellW = width / bays\n const cellH = shelfZone / levels // cell 높이 (shelf zone 안)\n const braceLen = Math.sqrt(cellW * cellW + cellH * cellH)\n const braceAngle = Math.atan2(cellH, cellW)\n const backZ = height / 2 - postW * 0.6\n\n for (let bay = 0; bay < bays; bay++) {\n // Brace only every other bay to keep things visually open\n if (bay % 2 !== 0) continue\n\n const cellCenterX = (bay - bays / 2 + 0.5) * cellW\n\n for (let lv = 0; lv < levels; lv++) {\n const cellCenterY = shelfBaseY + (lv + 0.5) * cellH\n\n for (const sign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(braceLen, braceT, braceT)\n brace.rotateZ(sign * braceAngle)\n brace.translate(cellCenterX, cellCenterY, backZ)\n braceGeos.push(brace)\n }\n }\n }\n if (braceGeos.length > 0) {\n const braceMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(braceGeos), BRACE_MATERIAL)\n braceMesh.castShadow = true\n this.object3d.add(braceMesh)\n }\n\n // ── Shelf planes (level 별 반투명 무볼륨 판) ────────────────────────────\n // 각 level 의 *바닥 면* 에 plane — cell 위치 시각 인식. carrier 가 그 위\n // 에 놓이는 *지지면*. 반투명.\n //\n // X-Z 넓이를 *frame 안쪽* 으로 줄여 mesh 겹침 자체 제거 (Z-fight 회피).\n // X: 양 옆 corner post 안쪽 (-postW 양쪽)\n // Z: 앞/뒤 beam 안쪽 (-beamH 양쪽)\n const shelfW = Math.max(0, width - 2 * postW)\n const shelfD = Math.max(0, height - 2 * beamH)\n const shelfGeo = new THREE.PlaneGeometry(shelfW, shelfD)\n shelfGeo.rotateX(-Math.PI / 2) // X-Y plane → X-Z plane (= horizontal)\n const shelfMaterial = SHELF_MATERIAL\n for (let lv = 0; lv < levels; lv++) {\n // shelf plane Y = 해당 level 의 *load beam top* 정확 일치 (cell 바닥 면).\n // beam center Y = shelfBaseY + yFrac*shelfZone - beamH/2 + (lv===0 ? beamH : 0)\n // beam top Y = beam center + beamH/2 = shelfBaseY + yFrac*shelfZone + (lv===0 ? beamH : 0)\n const yFrac = lv / levels\n const y = shelfBaseY + yFrac * shelfZone + (lv === 0 ? beamH : 0)\n const shelf = new THREE.Mesh(shelfGeo, shelfMaterial)\n shelf.position.set(0, y, 0)\n shelf.receiveShadow = true\n this.object3d.add(shelf)\n }\n\n // state.data 가 있으면 stock InstancedMesh 도 빌드\n this.rebuildStockMesh()\n }\n\n /**\n * state.data 의 각 record 를 한 InstancedMesh instance 로 렌더링.\n * cellMap 에 있는 cellId 만 instance 부여 — 나머지는 무시.\n * Mover/pickAndPlace 와 무관 — 순수 시각화.\n */\n rebuildStockMesh(): void {\n if (this._stockMesh) {\n this.object3d.remove(this._stockMesh)\n this._stockMesh = undefined\n }\n\n const rack: any = this.component\n const data = rack.state?.data as Array<{ cellId: string;[key: string]: any }> | undefined\n if (!Array.isArray(data) || data.length === 0) return\n\n const cellMap = rack.cellMap\n if (!cellMap) return\n\n // rack 의 기하 파라미터 — cellMap / _ensureSlotAttachObject3d 의 levelHeight\n // 계산과 *반드시* 일치해야 함 (shelfBaseHeight > 0 일 때 stock 시각 위치와\n // crane fork target 이 어긋나는 회귀 차단).\n const rs: any = this.component.state\n const rackWidth = (rs?.width as number) || 1000\n const rackDepth = (rs?.depth as number) || 3000\n const rackHeight = (rs?.height as number) || 600\n const bays = Math.max(1, Math.floor(rs?.bays || 5))\n const levels = Math.max(1, Math.floor(rs?.levels || 4))\n const shelfBase = Math.max(0, Math.min((rs?.shelfBaseHeight as number) || 0, rackDepth * 0.9))\n const shelfZone = rackDepth - shelfBase\n const bayWidth = rackWidth / bays\n const levelHeight = shelfZone / levels // ← shelfZone 기준 (cellMap 과 동일)\n const rowDepth = rackHeight\n\n // 한 stock 의 크기 — cell 의 85% × 85% × 70%\n const stockW = bayWidth * 0.85\n const stockH = rowDepth * 0.85\n const stockD = levelHeight * 0.7\n const geo = getStockGeometry(stockW, stockH, stockD)\n\n // 유효한 record 만 추출\n const valid: { cell: any; record: any }[] = []\n for (const r of data) {\n if (!r?.cellId) continue\n const cell = cellMap.findById(r.cellId)\n if (cell) valid.push({ cell, record: r })\n }\n if (valid.length === 0) return\n\n const inst = new THREE.InstancedMesh(geo, STOCK_MATERIAL, valid.length)\n inst.castShadow = false\n inst.receiveShadow = false\n inst.frustumCulled = false\n // things-scene EventManager3D 의 raycast hit-test 가 walk-up 하며 찾는 키:\n // userData.context 에 RealObject 자체 set.\n // 객체 전체 대체가 아니라 속성만 set 해 Three.js 의 기존 userData 필드 보존.\n inst.userData.context = this\n // 클릭 핸들러가 instanceId 로 record 를 역참조할 수 있도록 순서대로 저장.\n inst.userData._records = valid.map(v => v.record)\n\n const m = new THREE.Matrix4()\n const pos = new THREE.Vector3()\n const q = new THREE.Quaternion()\n const s = new THREE.Vector3(1, 1, 1)\n const c = new THREE.Color()\n\n for (let i = 0; i < valid.length; i++) {\n const { cell, record } = valid[i]\n // cell 중심 (rack-center 좌표계)\n const x = cell.localPosition.x + bayWidth / 2 - rackWidth / 2\n const cellCenterY = cell.localPosition.y + levelHeight / 2 - rackDepth / 2\n // stock 바닥 = cell 바닥 → stock 중심 Y = cell 중심 Y - levelHeight/2 + stockD/2\n const y = cellCenterY - levelHeight / 2 + stockD / 2\n const z = cell.localPosition.z + rowDepth / 2 - rackHeight / 2\n\n pos.set(x, y, z)\n m.compose(pos, q, s)\n inst.setMatrixAt(i, m)\n\n // Legend 색상 매핑 — rack 의 resolveLegendColor 호출, 매칭 없으면 default\n const resolved = (rack as any).resolveLegendColor?.(record) ?? STOCK_DEFAULT_COLOR\n c.set(resolved)\n inst.setColorAt(i, c)\n }\n inst.instanceMatrix.needsUpdate = true\n if (inst.instanceColor) inst.instanceColor.needsUpdate = true\n\n // ── CRITICAL: bounding sphere 계산 ────────────────────────────────────────\n // Three.js InstancedMesh.raycast 는 먼저 bounding sphere 로 broad-phase 체크.\n // setMatrixAt 만 호출하고 computeBoundingSphere 를 안 부르면 sphere.radius=0\n // → 어떤 ray 도 안 맞음 → click event 발화 안 됨. raycaster 가 instance 인식\n // 못 하는 것의 *결정적 원인*. needsUpdate 후 명시 호출 필수.\n inst.computeBoundingSphere()\n inst.computeBoundingBox?.()\n\n this.object3d.add(inst)\n this._stockMesh = inst\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'levels' in after ||\n 'bays' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after ||\n 'shelfBaseHeight' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
|
package/dist/storage-rack.d.ts
CHANGED
|
@@ -76,11 +76,20 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
76
76
|
static placement: PlacementArchetype;
|
|
77
77
|
static align: Alignment;
|
|
78
78
|
static defaultDepth: (h: Heights) => number;
|
|
79
|
+
get isObstacle(): boolean;
|
|
80
|
+
obstacleBoundingBox(): {
|
|
81
|
+
left: number;
|
|
82
|
+
top: number;
|
|
83
|
+
width: number;
|
|
84
|
+
height: number;
|
|
85
|
+
y?: number;
|
|
86
|
+
zHeight?: number;
|
|
87
|
+
} | null;
|
|
79
88
|
get nature(): ComponentNature;
|
|
80
89
|
get anchors(): never[];
|
|
81
90
|
/**
|
|
82
91
|
* Runtime — bays / levels 변경 시 anchor 캐시 무효화. cell 위치가 바뀌므로 다음
|
|
83
|
-
* `
|
|
92
|
+
* `_ensureSlotAttachObject3d` 호출이 새 좌표로 갱신.
|
|
84
93
|
*/
|
|
85
94
|
onchange(after: Record<string, unknown>, _before: Record<string, unknown>): void;
|
|
86
95
|
/**
|
|
@@ -116,10 +125,35 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
116
125
|
/**
|
|
117
126
|
* 1-based (bay, row, level) → 0-based cellId 문자열.
|
|
118
127
|
*
|
|
119
|
-
* rack.
|
|
120
|
-
* rack.
|
|
128
|
+
* rack.slotIdOf(1, 1, 6) → '0-0-5'
|
|
129
|
+
* rack.slotIdOf(3, 1, 4) → '2-0-3'
|
|
130
|
+
*/
|
|
131
|
+
slotIdOf(bay: number, row?: number, level?: number): string;
|
|
132
|
+
/**
|
|
133
|
+
* 모든 slot id 의 목록 (Plan A 의 *cell* = slot 통일). bays × rows × levels 전 조합.
|
|
134
|
+
* storage-rack 의 rows 는 항상 1 (단일 row) 이므로 실제론 bays × levels.
|
|
135
|
+
*
|
|
136
|
+
* SlottedHolder.slotIds — capability 기반 enumeration entry point.
|
|
137
|
+
*/
|
|
138
|
+
slotIds(): ReadonlyArray<string>;
|
|
139
|
+
/**
|
|
140
|
+
* 점유된 slot 의 id 목록 — state.data record + *carrier* children 둘 다.
|
|
141
|
+
* RackCell (시각 proxy) 같은 *carrier 아닌 자식* 은 제외 (isCarriable 검사).
|
|
142
|
+
*
|
|
143
|
+
* @param filter predicate — 통과하는 slotId 만. sweep 중 즉시 reject.
|
|
144
|
+
*/
|
|
145
|
+
occupiedSlotIds(filter?: (slotId: string) => boolean): ReadonlyArray<string>;
|
|
146
|
+
/** 비어있는 slot 의 id 목록 — slotIds() - occupiedSlotIds(). filter 도 동일. */
|
|
147
|
+
emptySlotIds(filter?: (slotId: string) => boolean): ReadonlyArray<string>;
|
|
148
|
+
/**
|
|
149
|
+
* slot 의 *world position* — anchor object3d *생성 없이* 직접 계산. crane 의
|
|
150
|
+
* reach 검사 등 *match 전 단계* 에서 *anchor 생성 0* 으로 사용.
|
|
121
151
|
*/
|
|
122
|
-
|
|
152
|
+
slotWorldPosition(cellId: string): {
|
|
153
|
+
x: number;
|
|
154
|
+
y: number;
|
|
155
|
+
z: number;
|
|
156
|
+
} | undefined;
|
|
123
157
|
/** cellId 에 carrier 가 있는가 — child carrier 또는 state.data record 어느 쪽이든. */
|
|
124
158
|
hasCarrierAt(cellId: string): boolean;
|
|
125
159
|
/** cellId 매칭되는 rack 의 직접 자식 carrier (operation archetype). */
|
|
@@ -226,7 +260,7 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
226
260
|
y: number;
|
|
227
261
|
};
|
|
228
262
|
/** cellId 별 attach anchor object3d cache (rack.object3d 의 자식). */
|
|
229
|
-
private
|
|
263
|
+
private _attachAnchorBySlot;
|
|
230
264
|
/**
|
|
231
265
|
* cellId 위치에 lightweight anchor object3d 를 *singleton 으로* 보장 + 갱신.
|
|
232
266
|
* 이 anchor 가:
|
|
@@ -235,7 +269,7 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
235
269
|
* - 두 용도가 *같은 object3d* 를 공유해 carrier 가 transient 동안 SlotTarget 의
|
|
236
270
|
* pose 와 정확히 동기화.
|
|
237
271
|
*/
|
|
238
|
-
private
|
|
272
|
+
private _ensureSlotAttachObject3d;
|
|
239
273
|
/**
|
|
240
274
|
* 2D — top-down rectangle showing the rack footprint with bay subdivisions.
|
|
241
275
|
* 편집/배치 가 가능하도록 *명시 fill + stroke* — pipeline 분기 무관하게 항상
|
package/dist/storage-rack.js
CHANGED
|
@@ -92,6 +92,21 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
92
92
|
static placement = 'floor';
|
|
93
93
|
static align = 'bottom';
|
|
94
94
|
static defaultDepth = (h) => h.ceiling - h.floor;
|
|
95
|
+
// Phase Auto-Nav (AN-PR-2) — Obstacle 자격.
|
|
96
|
+
get isObstacle() {
|
|
97
|
+
return this.state?.isObstacle !== false;
|
|
98
|
+
}
|
|
99
|
+
obstacleBoundingBox() {
|
|
100
|
+
const s = this.state;
|
|
101
|
+
if (typeof s?.left !== 'number')
|
|
102
|
+
return null;
|
|
103
|
+
return {
|
|
104
|
+
left: s.left, top: s.top,
|
|
105
|
+
width: s.width, height: s.height,
|
|
106
|
+
y: typeof s.zPos === 'number' ? s.zPos : 0,
|
|
107
|
+
zHeight: typeof s.depth === 'number' ? s.depth : 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
95
110
|
get nature() {
|
|
96
111
|
return NATURE;
|
|
97
112
|
}
|
|
@@ -100,7 +115,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
100
115
|
}
|
|
101
116
|
/**
|
|
102
117
|
* Runtime — bays / levels 변경 시 anchor 캐시 무효화. cell 위치가 바뀌므로 다음
|
|
103
|
-
* `
|
|
118
|
+
* `_ensureSlotAttachObject3d` 호출이 새 좌표로 갱신.
|
|
104
119
|
*/
|
|
105
120
|
onchange(after, _before) {
|
|
106
121
|
super.onchange?.(after, _before);
|
|
@@ -110,7 +125,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
110
125
|
'width' in after ||
|
|
111
126
|
'height' in after ||
|
|
112
127
|
'depth' in after) {
|
|
113
|
-
this.
|
|
128
|
+
this._attachAnchorBySlot.clear();
|
|
114
129
|
}
|
|
115
130
|
if ('hideHorizontalFrame' in after) {
|
|
116
131
|
;
|
|
@@ -177,7 +192,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
177
192
|
// 명시 시 (Carriable + CarrierHolder.reparent 모두) 그대로 anchor origin 에 snap.
|
|
178
193
|
const cellId = carrier?.state?.cellId;
|
|
179
194
|
if (cellId) {
|
|
180
|
-
const obj = this.
|
|
195
|
+
const obj = this._ensureSlotAttachObject3d(cellId);
|
|
181
196
|
if (obj)
|
|
182
197
|
return { attach: obj, localPosition: { x: 0, y: 0, z: 0 } };
|
|
183
198
|
}
|
|
@@ -209,12 +224,94 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
209
224
|
/**
|
|
210
225
|
* 1-based (bay, row, level) → 0-based cellId 문자열.
|
|
211
226
|
*
|
|
212
|
-
* rack.
|
|
213
|
-
* rack.
|
|
227
|
+
* rack.slotIdOf(1, 1, 6) → '0-0-5'
|
|
228
|
+
* rack.slotIdOf(3, 1, 4) → '2-0-3'
|
|
214
229
|
*/
|
|
215
|
-
|
|
230
|
+
slotIdOf(bay, row = 1, level = 1) {
|
|
216
231
|
return `${bay - 1}-${row - 1}-${level - 1}`;
|
|
217
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* 모든 slot id 의 목록 (Plan A 의 *cell* = slot 통일). bays × rows × levels 전 조합.
|
|
235
|
+
* storage-rack 의 rows 는 항상 1 (단일 row) 이므로 실제론 bays × levels.
|
|
236
|
+
*
|
|
237
|
+
* SlottedHolder.slotIds — capability 기반 enumeration entry point.
|
|
238
|
+
*/
|
|
239
|
+
slotIds() {
|
|
240
|
+
const bays = Math.max(1, Math.floor(this.state?.bays ?? 5));
|
|
241
|
+
const levels = Math.max(1, Math.floor(this.state?.levels ?? 4));
|
|
242
|
+
const ids = [];
|
|
243
|
+
for (let bay = 1; bay <= bays; bay++) {
|
|
244
|
+
for (let level = 1; level <= levels; level++) {
|
|
245
|
+
ids.push(this.slotIdOf(bay, 1, level));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return ids;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* 점유된 slot 의 id 목록 — state.data record + *carrier* children 둘 다.
|
|
252
|
+
* RackCell (시각 proxy) 같은 *carrier 아닌 자식* 은 제외 (isCarriable 검사).
|
|
253
|
+
*
|
|
254
|
+
* @param filter predicate — 통과하는 slotId 만. sweep 중 즉시 reject.
|
|
255
|
+
*/
|
|
256
|
+
occupiedSlotIds(filter) {
|
|
257
|
+
const set = new Set();
|
|
258
|
+
for (const r of this.records) {
|
|
259
|
+
const cid = r?.cellId;
|
|
260
|
+
if (typeof cid === 'string' && (!filter || filter(cid)))
|
|
261
|
+
set.add(cid);
|
|
262
|
+
}
|
|
263
|
+
for (const c of this.components ?? []) {
|
|
264
|
+
if (!c?.isCarriable)
|
|
265
|
+
continue;
|
|
266
|
+
const cid = c?.state?.cellId;
|
|
267
|
+
if (typeof cid === 'string' && (!filter || filter(cid)))
|
|
268
|
+
set.add(cid);
|
|
269
|
+
}
|
|
270
|
+
return Array.from(set);
|
|
271
|
+
}
|
|
272
|
+
/** 비어있는 slot 의 id 목록 — slotIds() - occupiedSlotIds(). filter 도 동일. */
|
|
273
|
+
emptySlotIds(filter) {
|
|
274
|
+
const occ = new Set(this.occupiedSlotIds());
|
|
275
|
+
const result = [];
|
|
276
|
+
for (const id of this.slotIds()) {
|
|
277
|
+
if (occ.has(id))
|
|
278
|
+
continue;
|
|
279
|
+
if (filter && !filter(id))
|
|
280
|
+
continue;
|
|
281
|
+
result.push(id);
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* slot 의 *world position* — anchor object3d *생성 없이* 직접 계산. crane 의
|
|
287
|
+
* reach 검사 등 *match 전 단계* 에서 *anchor 생성 0* 으로 사용.
|
|
288
|
+
*/
|
|
289
|
+
slotWorldPosition(cellId) {
|
|
290
|
+
const ro = this._realObject;
|
|
291
|
+
if (!ro?.object3d)
|
|
292
|
+
return undefined;
|
|
293
|
+
const cell = this.cellMap?.findById(cellId);
|
|
294
|
+
if (!cell)
|
|
295
|
+
return undefined;
|
|
296
|
+
const rs = this.state;
|
|
297
|
+
const rackWidth = rs?.width || 1000;
|
|
298
|
+
const rackDepth = rs?.depth || 3000;
|
|
299
|
+
const rackHeight = rs?.height || 600;
|
|
300
|
+
const bays = Math.max(1, Math.floor(rs?.bays || 5));
|
|
301
|
+
const levels = Math.max(1, Math.floor(rs?.levels || 4));
|
|
302
|
+
const shelfBase = Math.max(0, Math.min(rs?.shelfBaseHeight || 0, rackDepth * 0.9));
|
|
303
|
+
const shelfZone = rackDepth - shelfBase;
|
|
304
|
+
const bayWidth = rackWidth / bays;
|
|
305
|
+
const levelHeight = shelfZone / levels;
|
|
306
|
+
const stockD = levelHeight * 0.7;
|
|
307
|
+
const rowDepth = rackHeight;
|
|
308
|
+
const x = cell.localPosition.x + bayWidth / 2 - rackWidth / 2;
|
|
309
|
+
const y = cell.localPosition.y - rackDepth / 2 + stockD / 2;
|
|
310
|
+
const z = cell.localPosition.z + rowDepth / 2 - rackHeight / 2;
|
|
311
|
+
const v = new THREE.Vector3(x, y, z);
|
|
312
|
+
ro.object3d.localToWorld(v);
|
|
313
|
+
return { x: v.x, y: v.y, z: v.z };
|
|
314
|
+
}
|
|
218
315
|
/** cellId 에 carrier 가 있는가 — child carrier 또는 state.data record 어느 쪽이든. */
|
|
219
316
|
hasCarrierAt(cellId) {
|
|
220
317
|
if (this._carrierChildAt(cellId))
|
|
@@ -242,7 +339,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
242
339
|
obtainCarrier(idOrBay, row, level) {
|
|
243
340
|
const cellId = typeof idOrBay === 'string'
|
|
244
341
|
? idOrBay
|
|
245
|
-
: this.
|
|
342
|
+
: this.slotIdOf(idOrBay, row ?? 1, level ?? 1);
|
|
246
343
|
const existing = this._carrierChildAt(cellId);
|
|
247
344
|
if (existing)
|
|
248
345
|
return existing;
|
|
@@ -416,7 +513,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
416
513
|
* 이걸 attach frame 으로 사용 (transit 중 carrier 가 slot 위치에 정렬).
|
|
417
514
|
*/
|
|
418
515
|
getSlotAttachObject3d(cellId) {
|
|
419
|
-
return this.
|
|
516
|
+
return this._ensureSlotAttachObject3d(cellId);
|
|
420
517
|
}
|
|
421
518
|
/**
|
|
422
519
|
* SlottedHolder 컨트랙 — slot 의 *expected carrier* 의 3D 크기 (slot 자체의 기하 크기가
|
|
@@ -434,7 +531,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
434
531
|
const cell = this.cellMap?.findById(cellId);
|
|
435
532
|
if (!cell)
|
|
436
533
|
return undefined;
|
|
437
|
-
const stockD = cell.size.height * 0.7; // matches
|
|
534
|
+
const stockD = cell.size.height * 0.7; // matches _ensureSlotAttachObject3d + storage-rack-3d.rebuildStockMesh
|
|
438
535
|
return {
|
|
439
536
|
width: cell.size.width,
|
|
440
537
|
height: cell.size.depth, // 2D height = Z extent (front-back)
|
|
@@ -451,7 +548,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
451
548
|
slotTargetAt(idOrBay, row, level) {
|
|
452
549
|
const cellId = typeof idOrBay === 'string'
|
|
453
550
|
? idOrBay
|
|
454
|
-
: this.
|
|
551
|
+
: this.slotIdOf(idOrBay, row ?? 1, level ?? 1);
|
|
455
552
|
return new SlotTarget(this, cellId);
|
|
456
553
|
}
|
|
457
554
|
/**
|
|
@@ -495,7 +592,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
495
592
|
return { x: localX, y: localY };
|
|
496
593
|
}
|
|
497
594
|
/** cellId 별 attach anchor object3d cache (rack.object3d 의 자식). */
|
|
498
|
-
|
|
595
|
+
_attachAnchorBySlot = new Map();
|
|
499
596
|
/**
|
|
500
597
|
* cellId 위치에 lightweight anchor object3d 를 *singleton 으로* 보장 + 갱신.
|
|
501
598
|
* 이 anchor 가:
|
|
@@ -504,16 +601,16 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
504
601
|
* - 두 용도가 *같은 object3d* 를 공유해 carrier 가 transient 동안 SlotTarget 의
|
|
505
602
|
* pose 와 정확히 동기화.
|
|
506
603
|
*/
|
|
507
|
-
|
|
604
|
+
_ensureSlotAttachObject3d(cellId) {
|
|
508
605
|
const ro = this._realObject;
|
|
509
606
|
if (!ro?.object3d)
|
|
510
607
|
return undefined;
|
|
511
|
-
let obj = this.
|
|
608
|
+
let obj = this._attachAnchorBySlot.get(cellId);
|
|
512
609
|
if (!obj) {
|
|
513
610
|
obj = new THREE.Object3D();
|
|
514
611
|
obj.name = `rack-slot-anchor:${cellId}`;
|
|
515
612
|
ro.object3d.add(obj);
|
|
516
|
-
this.
|
|
613
|
+
this._attachAnchorBySlot.set(cellId, obj);
|
|
517
614
|
}
|
|
518
615
|
const cell = this.cellMap?.findById(cellId);
|
|
519
616
|
if (!cell)
|