@operato/scene-storage 10.0.0-beta.43 → 10.0.0-beta.46
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 +37 -0
- package/dist/box-3d.d.ts +2 -0
- package/dist/box-3d.js +103 -64
- package/dist/box-3d.js.map +1 -1
- 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 +567 -46
- package/dist/crane.js.map +1 -1
- package/dist/pallet-3d.d.ts +2 -0
- package/dist/pallet-3d.js +103 -53
- package/dist/pallet-3d.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 +94 -10
- package/dist/rack-grid.js +468 -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 +31 -6
- package/dist/storage-rack.js +96 -14
- package/dist/storage-rack.js.map +1 -1
- package/package.json +3 -3
- package/src/box-3d.ts +121 -68
- package/src/crane-3d.ts +34 -4
- package/src/crane.ts +615 -55
- package/src/pallet-3d.ts +122 -55
- package/src/parcel-3d.ts +19 -1
- package/src/rack-grid-3d.ts +31 -8
- package/src/rack-grid.ts +488 -82
- package/src/storage-rack-3d.ts +1 -1
- package/src/storage-rack.ts +96 -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/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
|
@@ -80,7 +80,7 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
80
80
|
get anchors(): never[];
|
|
81
81
|
/**
|
|
82
82
|
* Runtime — bays / levels 변경 시 anchor 캐시 무효화. cell 위치가 바뀌므로 다음
|
|
83
|
-
* `
|
|
83
|
+
* `_ensureSlotAttachObject3d` 호출이 새 좌표로 갱신.
|
|
84
84
|
*/
|
|
85
85
|
onchange(after: Record<string, unknown>, _before: Record<string, unknown>): void;
|
|
86
86
|
/**
|
|
@@ -116,10 +116,35 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
116
116
|
/**
|
|
117
117
|
* 1-based (bay, row, level) → 0-based cellId 문자열.
|
|
118
118
|
*
|
|
119
|
-
* rack.
|
|
120
|
-
* rack.
|
|
119
|
+
* rack.slotIdOf(1, 1, 6) → '0-0-5'
|
|
120
|
+
* rack.slotIdOf(3, 1, 4) → '2-0-3'
|
|
121
121
|
*/
|
|
122
|
-
|
|
122
|
+
slotIdOf(bay: number, row?: number, level?: number): string;
|
|
123
|
+
/**
|
|
124
|
+
* 모든 slot id 의 목록 (Plan A 의 *cell* = slot 통일). bays × rows × levels 전 조합.
|
|
125
|
+
* storage-rack 의 rows 는 항상 1 (단일 row) 이므로 실제론 bays × levels.
|
|
126
|
+
*
|
|
127
|
+
* SlottedHolder.slotIds — capability 기반 enumeration entry point.
|
|
128
|
+
*/
|
|
129
|
+
slotIds(): ReadonlyArray<string>;
|
|
130
|
+
/**
|
|
131
|
+
* 점유된 slot 의 id 목록 — state.data record + *carrier* children 둘 다.
|
|
132
|
+
* RackCell (시각 proxy) 같은 *carrier 아닌 자식* 은 제외 (isCarriable 검사).
|
|
133
|
+
*
|
|
134
|
+
* @param filter predicate — 통과하는 slotId 만. sweep 중 즉시 reject.
|
|
135
|
+
*/
|
|
136
|
+
occupiedSlotIds(filter?: (slotId: string) => boolean): ReadonlyArray<string>;
|
|
137
|
+
/** 비어있는 slot 의 id 목록 — slotIds() - occupiedSlotIds(). filter 도 동일. */
|
|
138
|
+
emptySlotIds(filter?: (slotId: string) => boolean): ReadonlyArray<string>;
|
|
139
|
+
/**
|
|
140
|
+
* slot 의 *world position* — anchor object3d *생성 없이* 직접 계산. crane 의
|
|
141
|
+
* reach 검사 등 *match 전 단계* 에서 *anchor 생성 0* 으로 사용.
|
|
142
|
+
*/
|
|
143
|
+
slotWorldPosition(cellId: string): {
|
|
144
|
+
x: number;
|
|
145
|
+
y: number;
|
|
146
|
+
z: number;
|
|
147
|
+
} | undefined;
|
|
123
148
|
/** cellId 에 carrier 가 있는가 — child carrier 또는 state.data record 어느 쪽이든. */
|
|
124
149
|
hasCarrierAt(cellId: string): boolean;
|
|
125
150
|
/** cellId 매칭되는 rack 의 직접 자식 carrier (operation archetype). */
|
|
@@ -226,7 +251,7 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
226
251
|
y: number;
|
|
227
252
|
};
|
|
228
253
|
/** cellId 별 attach anchor object3d cache (rack.object3d 의 자식). */
|
|
229
|
-
private
|
|
254
|
+
private _attachAnchorBySlot;
|
|
230
255
|
/**
|
|
231
256
|
* cellId 위치에 lightweight anchor object3d 를 *singleton 으로* 보장 + 갱신.
|
|
232
257
|
* 이 anchor 가:
|
|
@@ -235,7 +260,7 @@ export default class Rack extends Rack_base implements SlottedHolder {
|
|
|
235
260
|
* - 두 용도가 *같은 object3d* 를 공유해 carrier 가 transient 동안 SlotTarget 의
|
|
236
261
|
* pose 와 정확히 동기화.
|
|
237
262
|
*/
|
|
238
|
-
private
|
|
263
|
+
private _ensureSlotAttachObject3d;
|
|
239
264
|
/**
|
|
240
265
|
* 2D — top-down rectangle showing the rack footprint with bay subdivisions.
|
|
241
266
|
* 편집/배치 가 가능하도록 *명시 fill + stroke* — pipeline 분기 무관하게 항상
|
package/dist/storage-rack.js
CHANGED
|
@@ -100,7 +100,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
102
|
* Runtime — bays / levels 변경 시 anchor 캐시 무효화. cell 위치가 바뀌므로 다음
|
|
103
|
-
* `
|
|
103
|
+
* `_ensureSlotAttachObject3d` 호출이 새 좌표로 갱신.
|
|
104
104
|
*/
|
|
105
105
|
onchange(after, _before) {
|
|
106
106
|
super.onchange?.(after, _before);
|
|
@@ -110,7 +110,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
110
110
|
'width' in after ||
|
|
111
111
|
'height' in after ||
|
|
112
112
|
'depth' in after) {
|
|
113
|
-
this.
|
|
113
|
+
this._attachAnchorBySlot.clear();
|
|
114
114
|
}
|
|
115
115
|
if ('hideHorizontalFrame' in after) {
|
|
116
116
|
;
|
|
@@ -177,7 +177,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
177
177
|
// 명시 시 (Carriable + CarrierHolder.reparent 모두) 그대로 anchor origin 에 snap.
|
|
178
178
|
const cellId = carrier?.state?.cellId;
|
|
179
179
|
if (cellId) {
|
|
180
|
-
const obj = this.
|
|
180
|
+
const obj = this._ensureSlotAttachObject3d(cellId);
|
|
181
181
|
if (obj)
|
|
182
182
|
return { attach: obj, localPosition: { x: 0, y: 0, z: 0 } };
|
|
183
183
|
}
|
|
@@ -209,12 +209,94 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
209
209
|
/**
|
|
210
210
|
* 1-based (bay, row, level) → 0-based cellId 문자열.
|
|
211
211
|
*
|
|
212
|
-
* rack.
|
|
213
|
-
* rack.
|
|
212
|
+
* rack.slotIdOf(1, 1, 6) → '0-0-5'
|
|
213
|
+
* rack.slotIdOf(3, 1, 4) → '2-0-3'
|
|
214
214
|
*/
|
|
215
|
-
|
|
215
|
+
slotIdOf(bay, row = 1, level = 1) {
|
|
216
216
|
return `${bay - 1}-${row - 1}-${level - 1}`;
|
|
217
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* 모든 slot id 의 목록 (Plan A 의 *cell* = slot 통일). bays × rows × levels 전 조합.
|
|
220
|
+
* storage-rack 의 rows 는 항상 1 (단일 row) 이므로 실제론 bays × levels.
|
|
221
|
+
*
|
|
222
|
+
* SlottedHolder.slotIds — capability 기반 enumeration entry point.
|
|
223
|
+
*/
|
|
224
|
+
slotIds() {
|
|
225
|
+
const bays = Math.max(1, Math.floor(this.state?.bays ?? 5));
|
|
226
|
+
const levels = Math.max(1, Math.floor(this.state?.levels ?? 4));
|
|
227
|
+
const ids = [];
|
|
228
|
+
for (let bay = 1; bay <= bays; bay++) {
|
|
229
|
+
for (let level = 1; level <= levels; level++) {
|
|
230
|
+
ids.push(this.slotIdOf(bay, 1, level));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return ids;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 점유된 slot 의 id 목록 — state.data record + *carrier* children 둘 다.
|
|
237
|
+
* RackCell (시각 proxy) 같은 *carrier 아닌 자식* 은 제외 (isCarriable 검사).
|
|
238
|
+
*
|
|
239
|
+
* @param filter predicate — 통과하는 slotId 만. sweep 중 즉시 reject.
|
|
240
|
+
*/
|
|
241
|
+
occupiedSlotIds(filter) {
|
|
242
|
+
const set = new Set();
|
|
243
|
+
for (const r of this.records) {
|
|
244
|
+
const cid = r?.cellId;
|
|
245
|
+
if (typeof cid === 'string' && (!filter || filter(cid)))
|
|
246
|
+
set.add(cid);
|
|
247
|
+
}
|
|
248
|
+
for (const c of this.components ?? []) {
|
|
249
|
+
if (!c?.isCarriable)
|
|
250
|
+
continue;
|
|
251
|
+
const cid = c?.state?.cellId;
|
|
252
|
+
if (typeof cid === 'string' && (!filter || filter(cid)))
|
|
253
|
+
set.add(cid);
|
|
254
|
+
}
|
|
255
|
+
return Array.from(set);
|
|
256
|
+
}
|
|
257
|
+
/** 비어있는 slot 의 id 목록 — slotIds() - occupiedSlotIds(). filter 도 동일. */
|
|
258
|
+
emptySlotIds(filter) {
|
|
259
|
+
const occ = new Set(this.occupiedSlotIds());
|
|
260
|
+
const result = [];
|
|
261
|
+
for (const id of this.slotIds()) {
|
|
262
|
+
if (occ.has(id))
|
|
263
|
+
continue;
|
|
264
|
+
if (filter && !filter(id))
|
|
265
|
+
continue;
|
|
266
|
+
result.push(id);
|
|
267
|
+
}
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* slot 의 *world position* — anchor object3d *생성 없이* 직접 계산. crane 의
|
|
272
|
+
* reach 검사 등 *match 전 단계* 에서 *anchor 생성 0* 으로 사용.
|
|
273
|
+
*/
|
|
274
|
+
slotWorldPosition(cellId) {
|
|
275
|
+
const ro = this._realObject;
|
|
276
|
+
if (!ro?.object3d)
|
|
277
|
+
return undefined;
|
|
278
|
+
const cell = this.cellMap?.findById(cellId);
|
|
279
|
+
if (!cell)
|
|
280
|
+
return undefined;
|
|
281
|
+
const rs = this.state;
|
|
282
|
+
const rackWidth = rs?.width || 1000;
|
|
283
|
+
const rackDepth = rs?.depth || 3000;
|
|
284
|
+
const rackHeight = rs?.height || 600;
|
|
285
|
+
const bays = Math.max(1, Math.floor(rs?.bays || 5));
|
|
286
|
+
const levels = Math.max(1, Math.floor(rs?.levels || 4));
|
|
287
|
+
const shelfBase = Math.max(0, Math.min(rs?.shelfBaseHeight || 0, rackDepth * 0.9));
|
|
288
|
+
const shelfZone = rackDepth - shelfBase;
|
|
289
|
+
const bayWidth = rackWidth / bays;
|
|
290
|
+
const levelHeight = shelfZone / levels;
|
|
291
|
+
const stockD = levelHeight * 0.7;
|
|
292
|
+
const rowDepth = rackHeight;
|
|
293
|
+
const x = cell.localPosition.x + bayWidth / 2 - rackWidth / 2;
|
|
294
|
+
const y = cell.localPosition.y - rackDepth / 2 + stockD / 2;
|
|
295
|
+
const z = cell.localPosition.z + rowDepth / 2 - rackHeight / 2;
|
|
296
|
+
const v = new THREE.Vector3(x, y, z);
|
|
297
|
+
ro.object3d.localToWorld(v);
|
|
298
|
+
return { x: v.x, y: v.y, z: v.z };
|
|
299
|
+
}
|
|
218
300
|
/** cellId 에 carrier 가 있는가 — child carrier 또는 state.data record 어느 쪽이든. */
|
|
219
301
|
hasCarrierAt(cellId) {
|
|
220
302
|
if (this._carrierChildAt(cellId))
|
|
@@ -242,7 +324,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
242
324
|
obtainCarrier(idOrBay, row, level) {
|
|
243
325
|
const cellId = typeof idOrBay === 'string'
|
|
244
326
|
? idOrBay
|
|
245
|
-
: this.
|
|
327
|
+
: this.slotIdOf(idOrBay, row ?? 1, level ?? 1);
|
|
246
328
|
const existing = this._carrierChildAt(cellId);
|
|
247
329
|
if (existing)
|
|
248
330
|
return existing;
|
|
@@ -416,7 +498,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
416
498
|
* 이걸 attach frame 으로 사용 (transit 중 carrier 가 slot 위치에 정렬).
|
|
417
499
|
*/
|
|
418
500
|
getSlotAttachObject3d(cellId) {
|
|
419
|
-
return this.
|
|
501
|
+
return this._ensureSlotAttachObject3d(cellId);
|
|
420
502
|
}
|
|
421
503
|
/**
|
|
422
504
|
* SlottedHolder 컨트랙 — slot 의 *expected carrier* 의 3D 크기 (slot 자체의 기하 크기가
|
|
@@ -434,7 +516,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
434
516
|
const cell = this.cellMap?.findById(cellId);
|
|
435
517
|
if (!cell)
|
|
436
518
|
return undefined;
|
|
437
|
-
const stockD = cell.size.height * 0.7; // matches
|
|
519
|
+
const stockD = cell.size.height * 0.7; // matches _ensureSlotAttachObject3d + storage-rack-3d.rebuildStockMesh
|
|
438
520
|
return {
|
|
439
521
|
width: cell.size.width,
|
|
440
522
|
height: cell.size.depth, // 2D height = Z extent (front-back)
|
|
@@ -451,7 +533,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
451
533
|
slotTargetAt(idOrBay, row, level) {
|
|
452
534
|
const cellId = typeof idOrBay === 'string'
|
|
453
535
|
? idOrBay
|
|
454
|
-
: this.
|
|
536
|
+
: this.slotIdOf(idOrBay, row ?? 1, level ?? 1);
|
|
455
537
|
return new SlotTarget(this, cellId);
|
|
456
538
|
}
|
|
457
539
|
/**
|
|
@@ -495,7 +577,7 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
495
577
|
return { x: localX, y: localY };
|
|
496
578
|
}
|
|
497
579
|
/** cellId 별 attach anchor object3d cache (rack.object3d 의 자식). */
|
|
498
|
-
|
|
580
|
+
_attachAnchorBySlot = new Map();
|
|
499
581
|
/**
|
|
500
582
|
* cellId 위치에 lightweight anchor object3d 를 *singleton 으로* 보장 + 갱신.
|
|
501
583
|
* 이 anchor 가:
|
|
@@ -504,16 +586,16 @@ let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbs
|
|
|
504
586
|
* - 두 용도가 *같은 object3d* 를 공유해 carrier 가 transient 동안 SlotTarget 의
|
|
505
587
|
* pose 와 정확히 동기화.
|
|
506
588
|
*/
|
|
507
|
-
|
|
589
|
+
_ensureSlotAttachObject3d(cellId) {
|
|
508
590
|
const ro = this._realObject;
|
|
509
591
|
if (!ro?.object3d)
|
|
510
592
|
return undefined;
|
|
511
|
-
let obj = this.
|
|
593
|
+
let obj = this._attachAnchorBySlot.get(cellId);
|
|
512
594
|
if (!obj) {
|
|
513
595
|
obj = new THREE.Object3D();
|
|
514
596
|
obj.name = `rack-slot-anchor:${cellId}`;
|
|
515
597
|
ro.object3d.add(obj);
|
|
516
|
-
this.
|
|
598
|
+
this._attachAnchorBySlot.set(cellId, obj);
|
|
517
599
|
}
|
|
518
600
|
const cell = this.cellMap?.findById(cellId);
|
|
519
601
|
if (!cell)
|