@operato/scene-storage 10.0.0-beta.56 → 10.0.0-beta.57

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.
@@ -1 +1 @@
1
- {"version":3,"file":"stockpile.js","sourceRoot":"","sources":["../src/stockpile.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Stockpile — 평치(block/floor) 보관 영역. 사각형 footprint + `state.data` 의 record[].\n * 실제 carrier component 를 자식으로 두지 않고, 인벤토리(records)의 *_개수와 종류_* 만으로\n * 가상 carrier mesh 를 자동 적치(stackPattern × carrierPreset)해 시각화한다.\n *\n * 데이터 idiom — StorageRack 과 동일 (`state.data: Record[]`). mover 가 pick/place 하면\n * record push/pop, _realObject 가 records.length 기준 mesh 를 재배치.\n *\n * 1단계 — 시각화: 모델에 `data: [{id:'a'},{id:'b'},...]` 가 들어오면 mesh 가 자동 적치.\n * 2단계(추후) — mover 통합: obtainCarrier 가 transient carrier 컴포넌트 materialize,\n * receiveAt 가 record push + carrier dispose.\n */\n\nimport * as THREE from 'three'\nimport { Component, ComponentNature, ContainerAbstract, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Placeable,\n RecordStorage,\n SlotTarget,\n type AttachFrame,\n type Alignment,\n type Heights,\n type PlacementArchetype,\n type SlottedHolder\n} from '@operato/scene-base'\n\nimport { Stockpile3D } from './stockpile-3d.js'\n\nexport type StackPattern = 'row' | 'staggered' | 'pyramid' | 'column' | 'pile'\nexport type CarrierPreset = 'box' | 'pallet' | 'drum' | 'sack' | 'crate' | 'bale'\nexport type PickPolicy = 'lifo' | 'fifo'\n\n/** 적치된 carrier 의 record. 향후 sku / weight / 입고시각 등 확장. */\nexport interface StockpileRecord {\n id: string\n [key: string]: any\n}\n\nexport interface StockpileState extends State {\n /** 적치 carrier 의 record 목록 — storage-rack 의 state.data 와 동일 idiom. */\n data?: StockpileRecord[]\n\n /** 적치 형태 프리셋. default 'row'. */\n stackPattern?: StackPattern\n /** 가상 carrier 종류 프리셋. default 'box'. */\n carrierPreset?: CarrierPreset\n\n /** 가상 carrier 한 개의 크기 (없으면 preset 별 기본). */\n carrierWidth?: number\n carrierHeight?: number\n carrierDepth?: number\n /** 적치된 carrier 사이의 평면(xz) 간격 — 0 이면 딱 붙음, default 3. y(단 적층)는 항상 딱 붙음. */\n carrierGap?: number\n\n /** 최대 적치 수 (undefined = 무제한). */\n capacity?: number\n /** 위로 몇 단까지 (undefined = stack 형태에 맡김). */\n stackHeightLimit?: number\n /** 어느 끝에서 빼나. default 'lifo'. */\n pickPolicy?: PickPolicy\n\n /** click 시 invoke 할 Popup 컴포넌트 id (StorageRack / RackGrid 와 동일 패턴). */\n popupRef?: string\n\n /**\n * Legend 컴포넌트 id. legend 의 `state.status = {field, ranges, defaultColor}` 를\n * 참조해 각 record 의 field 값을 색상으로 매핑한다 (StorageRack 와 동일 패턴).\n * 미명시 시 scene 안 `type='legend'` 첫 컴포넌트 자동 발견.\n */\n legendTarget?: string\n\n material3d?: Material3D\n}\n\nconst SLOT_ID = 'pile'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n { type: 'select', label: 'stack-pattern', name: 'stackPattern',\n property: { options: ['row', 'staggered', 'pyramid', 'column', 'pile'] } },\n { type: 'select', label: 'carrier-preset', name: 'carrierPreset',\n property: { options: ['box', 'pallet', 'drum', 'sack', 'crate', 'bale'] } },\n { type: 'number', label: 'carrier-width', name: 'carrierWidth' },\n { type: 'number', label: 'carrier-height', name: 'carrierHeight' },\n { type: 'number', label: 'carrier-depth', name: 'carrierDepth' },\n { type: 'number', label: 'carrier-gap', name: 'carrierGap' },\n { type: 'number', label: 'capacity', name: 'capacity' },\n { type: 'number', label: 'stack-height-limit', name: 'stackHeightLimit' },\n { type: 'select', label: 'pick-policy', name: 'pickPolicy',\n property: { options: ['lifo', 'fifo'] } },\n { type: 'id-input', label: 'popup-ref', name: 'popupRef',\n property: { component: 'popup' } },\n { type: 'id-input', label: 'legend-target', name: 'legendTarget',\n property: { component: 'legend' },\n placeholder: '미명시 시 scene 의 legend 자동 발견' }\n ],\n help: 'scene/component/stockpile'\n}\n\n@sceneComponent('stockpile')\nexport default class Stockpile extends RecordStorage<StockpileRecord>()(\n CarrierHolder(Placeable(ContainerAbstract))\n) implements SlottedHolder {\n declare state: StockpileState\n declare _realObject?: Stockpile3D\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (_h: Heights) => 5 // pad 두께\n\n get nature(): ComponentNature { return NATURE }\n get anchors() { return [] }\n\n // ── records / inventoryCount: RecordStorage mixin 제공.\n\n // ── SlottedHolder duck-type override — 단일 slot ('pile') ───────────────\n // mixin default 는 record 마다 slotId. Stockpile 은 *_단일 슬롯_* 시맨틱이라\n // 자기 구현 유지.\n slotIds(): ReadonlyArray<string> { return [SLOT_ID] }\n\n hasCarrierAt(slotId: string): boolean {\n return slotId === SLOT_ID && this.inventoryCount > 0\n }\n\n canReceiveAt(slotId: string, _carrier?: Component): boolean {\n if (slotId !== SLOT_ID) return false\n const cap = this.state.capacity\n if (typeof cap === 'number' && this.inventoryCount >= cap) return false\n return true\n }\n\n occupiedSlotIds(): ReadonlyArray<string> {\n return this.inventoryCount > 0 ? [SLOT_ID] : []\n }\n emptySlotIds(): ReadonlyArray<string> {\n return this.canReceiveAt(SLOT_ID) ? [SLOT_ID] : []\n }\n\n /**\n * record 한 개를 빼서 carrier 컴포넌트로 transient materialize. mover 가 pickup\n * 하면 이걸 reparent 해서 들고 다닌다. storage-rack._materializeCarrier 와 동일\n * 패턴 — Component.register(type) 으로 클래스 lookup, addComponent({silent: true})\n * 로 cascade 차단.\n */\n obtainCarrier(slotId: string): Component | null {\n if (slotId !== SLOT_ID) return null\n if (this.records.length === 0) return null\n const records = [...this.records]\n const policy = (this.state.pickPolicy ?? 'lifo') as PickPolicy\n const record = policy === 'fifo' ? records.shift()! : records.pop()!\n this._setDataSilently(records)\n return this._materializeCarrier(record)\n }\n\n private _materializeCarrier(record: StockpileRecord): Component | null {\n // carrierPreset → 등록된 컴포넌트 type. 미등록(drum/sack/bale 등) 은 시각 mesh\n // 전용이라 carrier 컴포넌트로는 fallback ('parcel'/'box').\n const preset = (this.state.carrierPreset ?? 'box') as CarrierPreset\n const PRESET_TO_TYPE: Record<CarrierPreset, string> = {\n box: 'box',\n pallet: 'pallet',\n crate: 'box',\n drum: 'parcel',\n sack: 'parcel',\n bale: 'parcel'\n }\n const carrierType = (record as any).type ?? PRESET_TO_TYPE[preset] ?? 'parcel'\n const CarrierClass = (Component as any).register(carrierType) as\n | (new (...args: any[]) => Component) | undefined\n if (!CarrierClass) {\n console.warn(`[stockpile] carrier type \"${carrierType}\" 미등록 — obtainCarrier 실패`)\n return null\n }\n\n // 크기 — state.carrier* 또는 preset default\n const stockpileW = (this.state as any).width ?? 100\n const stockpileH = (this.state as any).height ?? 100\n const cw = this.state.carrierWidth ?? 30\n const ch = this.state.carrierHeight ?? 30\n const cd = this.state.carrierDepth ?? 22\n\n // id/refid/transform 류 제외 — scene 안 기존 component 충돌 회피 (storage-rack 패턴)\n const { id: _id, refid: _refid, transform: _tf, ...recordCopy } = record as any\n // stockpile-inner 좌표 — center 에 놓기 (Mover 가 곧 pick 해 들고 감)\n const carrierState: any = {\n ...recordCopy,\n type: carrierType,\n width: cw,\n height: ch,\n depth: cd,\n refid: _nextStockpileCarrierRefid(),\n left: stockpileW / 2 - cw / 2,\n top: stockpileH / 2 - ch / 2\n }\n\n const carrier = new CarrierClass(carrierState, (this as any)._app)\n // silent: refreshMappings cascade 차단 (transient 라 매핑 재계산 불필요)\n ;(this as any).addComponent(carrier, { silent: true })\n void (carrier as any).realObject\n ;(carrier as any).applyHolderAttachPoint?.()\n return carrier\n }\n\n /**\n * carrier 의 state → StockpileRecord 추출. id 누락 시 `stk-` prefix 자동 생성.\n * transform/refid 등 *_물리적 위치 필드_* 는 *_제외_* — record 는 *_논리적 식별_*\n * 만. SlottedHolder duck 의 표준 entry point — receiveAt 안에서 호출.\n */\n recordFromCarrier(carrier: Component, _slotId: string): StockpileRecord {\n const cstate: any = (carrier as any)?.state ?? {}\n const cid = cstate.id ?? `stk-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`\n return {\n id: String(cid),\n ...(cstate.type ? { type: cstate.type } : {}),\n ...(typeof cstate.width === 'number' ? { width: cstate.width } : {}),\n ...(typeof cstate.height === 'number' ? { height: cstate.height } : {}),\n ...(typeof cstate.depth === 'number' ? { depth: cstate.depth } : {})\n }\n }\n\n /**\n * carrier 를 받아 record 로 push, carrier 객체는 dispose (시각은 _realObject 가\n * records 길이 기준 자동 갱신). capacity 초과 시도는 canReceiveAt 가 이미 차단.\n */\n async receiveAt(slotId: string, carrier: Component, _options?: any): Promise<void> {\n const record = this.recordFromCarrier(carrier, slotId)\n const records = [...this.records, record]\n this._setDataSilently(records)\n ;(carrier as any)?.dispose?.()\n }\n\n /** dispatch → handoff 의 accept 분기 — receiveAt 으로 위임. */\n async accept(carrier: Component, options?: any): Promise<void> {\n return this.receiveAt(SLOT_ID, carrier, options)\n }\n async receive(carrier: Component, options?: any): Promise<void> {\n return this.receiveAt(SLOT_ID, carrier, options)\n }\n\n /**\n * mover 가 obtainCarrier 로 빼낸 transient carrier 를 pad 위에 안착 (잠깐 보이는\n * 위치). mover 가 pick 하면 즉시 deck 으로 reparent 되므로 잔류는 짧다.\n * Spot 과 동일 idiom — pad-top + carrier 의 halfDepth 만큼 들어올림.\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const ro = this._realObject\n const frame = ro?.getAttachFrame?.()\n if (!frame) return null\n const carrierDepth = resolveDepth(carrier)\n return {\n attach: frame,\n localPosition: { x: 0, y: carrierDepth / 2, z: 0 }\n }\n }\n\n slotTargetAt(slotId: string): SlotTarget {\n return new SlotTarget(this as any, slotId)\n }\n getSlotAttachObject3d(slotId: string): any {\n // slotId 가 'pile' 이면 pad. record.id 면 그 carrier mesh — popup tether 가 그\n // stock 에 정확히 연결되도록.\n return (this as any)._realObject?.getAttachFrame?.(slotId)\n }\n\n // _setDataSilently 는 RecordStorage mixin 제공 — 다만 mixin 은 _rebuildVisual\n // 호출. Stockpile 의 3D 갱신은 update() 라서 hook override.\n _rebuildVisual(): void {\n this._realObject?.update?.()\n }\n\n /**\n * 2D — outlined pad + 인벤토리 카운트 텍스트. 평치 의도가 한 눈에 — 사각 영역 위에\n * 적재된 carrier 수가 가운데 큰 글씨로.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { left = 0, top = 0, width = 100, height = 100 } = this.state\n const fillStyle = (this.state.fillStyle as string) || '#c89c5c'\n const strokeStyle = (this.state.strokeStyle as string) || fillStyle\n\n // pad (반투명)\n ctx.save()\n ctx.fillStyle = fillStyle\n ctx.globalAlpha = 0.18\n ctx.fillRect(left, top, width, height)\n ctx.restore()\n\n // outline (dashed)\n ctx.save()\n ctx.strokeStyle = strokeStyle\n ctx.lineWidth = 1.5\n ctx.setLineDash([6, 3])\n ctx.strokeRect(left + 0.75, top + 0.75, width - 1.5, height - 1.5)\n ctx.setLineDash([])\n ctx.restore()\n\n // 인벤토리 수 + capacity (있으면)\n ctx.save()\n const fontSize = Math.min(width, height) * 0.22\n ctx.fillStyle = '#333'\n ctx.font = `bold ${fontSize}px sans-serif`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n const label = typeof this.state.capacity === 'number'\n ? `${this.inventoryCount}/${this.state.capacity}`\n : `${this.inventoryCount}`\n ctx.fillText(label, left + width / 2, top + height / 2)\n ctx.restore()\n }\n\n // ── Popup 연동 — storage-rack 동일 패턴 ───────────────────────────────\n /**\n * things-scene EventManager3D 가 raycast → object3d.userData.context.component 의\n * `trigger(\"click\", mouseEvent)` 을 호출 → eventMap 으로 receive. pad / carrier mesh\n * 어느 쪽을 클릭하든 stockpile 전체 popup invoke (단일 slot 이라 cell 구분 없음).\n */\n get eventMap() {\n return {\n '(self)': {\n '(self)': {\n click: this._onStockpileClick\n }\n }\n }\n }\n\n private _onStockpileClick = (mouseEvent: MouseEvent) => {\n // view mode 에서만 동작 (modeling 중 click 은 framework 선택 로직 우선).\n if (!(this as any).app?.isViewMode) return\n const hit = this._raycastStockpileHit(mouseEvent)\n if (!hit) return\n // hit.object 가 carrier mesh 면 userData.recordId 보유 → 그 stock 의 popup.\n // pad / 기타면 stockpile 전체 popup.\n const recordId = hit.object?.userData?.recordId as string | undefined\n this._dispatchStockpilePopup(typeof recordId === 'string' ? recordId : undefined)\n }\n\n /**\n * state.popupRef 가 가리키는 Popup 컴포넌트를 invoke.\n * - recordId 명시 → 그 record 의 anchor = mesh. payload = 해당 record.\n * - 미명시 (pad 클릭) → 'pile' anchor (pad). payload = 전체 inventory.\n *\n * RecordStorage mixin 의 `_invokePopup(slotId, payload)` 를 활용. 단일 slot\n * 시맨틱이 record/pile 두 모드라 mixin 위 wrapper 로 dispatch.\n */\n private _dispatchStockpilePopup(recordId?: string): void {\n if (!this.state.popupRef) return\n if (recordId) {\n const record = (this.records as ReadonlyArray<StockpileRecord>)\n .find(r => r.id === recordId) ?? { id: recordId }\n this._invokePopup(recordId, record)\n } else {\n this._invokePopup(SLOT_ID, {\n componentId: (this.state as any).id,\n records: this.records,\n inventoryCount: this.inventoryCount,\n capacity: this.state.capacity,\n carrierPreset: this.state.carrierPreset,\n stackPattern: this.state.stackPattern\n })\n }\n }\n\n /**\n * 클릭 시 framework 의 mouse NDC 를 재사용해 raycast → *우리 stockpile* 의 어떤 mesh 가\n * closest hit 인지 반환 (다른 object 가 더 가까우면 undefined). storage-rack._raycastRackHit\n * 와 동일 패턴 — capability.getObjectsByRaycast 우선, 없으면 scene/camera/canvas 재구성.\n */\n private _raycastStockpileHit(mouseEvent: MouseEvent): THREE.Intersection | undefined {\n const ro: any = (this as any)._realObject\n if (!ro?.object3d) return undefined\n\n const tc: any = ro.threeContainer\n if (!tc) return undefined\n\n const cap: any = tc._threeCapability ?? tc._capability\n let intersects: THREE.Intersection[] | undefined\n if (cap?.getObjectsByRaycast) {\n intersects = cap.getObjectsByRaycast() as THREE.Intersection[] | undefined\n }\n if (!intersects || intersects.length === 0) {\n const scene = tc.scene3d as THREE.Scene | undefined\n const renderer = tc.renderer3d as THREE.WebGLRenderer | undefined\n const camera =\n (tc.activeCamera3d as THREE.Camera | undefined) ??\n (cap?.activeCamera as THREE.Camera | undefined) ??\n (cap?.camera as THREE.Camera | undefined)\n const canvas = renderer?.domElement\n if (!scene || !canvas || !camera) return undefined\n const rect = canvas.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return undefined\n const ndc = new THREE.Vector2(\n ((mouseEvent.clientX - rect.left) / rect.width) * 2 - 1,\n -((mouseEvent.clientY - rect.top) / rect.height) * 2 + 1\n )\n const raycaster = new THREE.Raycaster()\n raycaster.setFromCamera(ndc, camera)\n intersects = raycaster.intersectObjects(scene.children, true)\n }\n if (!intersects || intersects.length === 0) return undefined\n\n // 가장 가까운 hit 이 *이 stockpile* 의 descendant 여야 한다 (다른 mesh 가 사이에 있으면 skip).\n const closest = intersects[0]\n let obj: THREE.Object3D | null = closest.object\n while (obj) {\n if (obj.userData?.context === ro) return closest\n obj = obj.parent\n }\n return undefined\n }\n\n // legendTarget / _onLegendChanged / resolveLegendColor — RecordStorage mixin 제공.\n\n buildRealObject(): RealObject | undefined {\n return new Stockpile3D(this)\n }\n}\n\n// transient carrier refid — scene 내 기존 컴포넌트와 충돌 회피용 큰 시작값\n// (storage-rack 의 _nextCarrierRefid 와 같은 idiom, 별도 범위로 분리).\nlet _stockpileCarrierSeq = 0\nfunction _nextStockpileCarrierRefid(): number {\n return 800000 + (_stockpileCarrierSeq++)\n}\n\nfunction resolveDepth(c: Component): number {\n const eff = (c as any)._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n const d = (c as any)?.state?.depth\n return typeof d === 'number' && Number.isFinite(d) ? d : 0\n}\n"]}
1
+ {"version":3,"file":"stockpile.js","sourceRoot":"","sources":["../src/stockpile.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Stockpile — 평치(block/floor) 보관 영역. 사각형 footprint + `state.data` 의 record[].\n * 실제 carrier component 를 자식으로 두지 않고, 인벤토리(records)의 *_개수와 종류_* 만으로\n * 가상 carrier mesh 를 자동 적치(stackPattern × carrierPreset)해 시각화한다.\n *\n * 데이터 idiom — StorageRack 과 동일 (`state.data: Record[]`). mover 가 pick/place 하면\n * record push/pop, _realObject 가 records.length 기준 mesh 를 재배치.\n *\n * 1단계 — 시각화: 모델에 `data: [{id:'a'},{id:'b'},...]` 가 들어오면 mesh 가 자동 적치.\n * 2단계(추후) — mover 통합: obtainCarrier 가 transient carrier 컴포넌트 materialize,\n * receiveAt 가 record push + carrier dispose.\n */\n\nimport * as THREE from 'three'\nimport { Component, ComponentNature, ContainerAbstract, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Placeable,\n RecordStorage,\n SlotTarget,\n type AttachFrame,\n type Alignment,\n type Heights,\n type PlacementArchetype,\n type SlottedHolder\n} from '@operato/scene-base'\n\nimport { Stockpile3D } from './stockpile-3d.js'\n\nexport type StackPattern = 'row' | 'staggered' | 'pyramid' | 'column' | 'pile'\nexport type CarrierPreset = 'box' | 'pallet' | 'drum' | 'sack' | 'crate' | 'bale'\nexport type PickPolicy = 'lifo' | 'fifo'\n\n/** 적치된 carrier 의 record. 향후 sku / weight / 입고시각 등 확장. */\nexport interface StockpileRecord {\n id: string\n [key: string]: any\n}\n\nexport interface StockpileState extends State {\n /** 적치 carrier 의 record 목록 — storage-rack 의 state.data 와 동일 idiom. */\n data?: StockpileRecord[]\n\n /** 적치 형태 프리셋. default 'row'. */\n stackPattern?: StackPattern\n /** 가상 carrier 종류 프리셋. default 'box'. */\n carrierPreset?: CarrierPreset\n\n /** 가상 carrier 한 개의 크기 (없으면 preset 별 기본). */\n carrierWidth?: number\n carrierHeight?: number\n carrierDepth?: number\n /** 적치된 carrier 사이의 평면(xz) 간격 — 0 이면 딱 붙음, default 3. y(단 적층)는 항상 딱 붙음. */\n carrierGap?: number\n\n /** 최대 적치 수 (undefined = 무제한). */\n capacity?: number\n /** 위로 몇 단까지 (undefined = stack 형태에 맡김). */\n stackHeightLimit?: number\n /** 어느 끝에서 빼나. default 'lifo'. */\n pickPolicy?: PickPolicy\n\n /**\n * 라우팅 행선지 값 (RoutingTarget). sorter/router 가 carrier.destination 과 string\n * 매칭해 이 StockPile 로 분배. 미설정 시 routingDestination() 가 state.id 로 fallback.\n */\n destination?: string\n\n /** click 시 invoke 할 Popup 컴포넌트 id (StorageRack / RackGrid 와 동일 패턴). */\n popupRef?: string\n\n /**\n * Legend 컴포넌트 id. legend 의 `state.status = {field, ranges, defaultColor}` 를\n * 참조해 각 record 의 field 값을 색상으로 매핑한다 (StorageRack 와 동일 패턴).\n * 미명시 시 scene 안 `type='legend'` 첫 컴포넌트 자동 발견.\n */\n legendTarget?: string\n\n material3d?: Material3D\n}\n\nconst SLOT_ID = 'pile'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n { type: 'select', label: 'stack-pattern', name: 'stackPattern',\n property: { options: ['row', 'staggered', 'pyramid', 'column', 'pile'] } },\n { type: 'select', label: 'carrier-preset', name: 'carrierPreset',\n property: { options: ['box', 'pallet', 'drum', 'sack', 'crate', 'bale'] } },\n { type: 'number', label: 'carrier-width', name: 'carrierWidth' },\n { type: 'number', label: 'carrier-height', name: 'carrierHeight' },\n { type: 'number', label: 'carrier-depth', name: 'carrierDepth' },\n { type: 'number', label: 'carrier-gap', name: 'carrierGap' },\n { type: 'number', label: 'capacity', name: 'capacity' },\n { type: 'number', label: 'stack-height-limit', name: 'stackHeightLimit' },\n { type: 'select', label: 'pick-policy', name: 'pickPolicy',\n property: { options: ['lifo', 'fifo'] } },\n { type: 'string', label: 'destination', name: 'destination' },\n { type: 'id-input', label: 'popup-ref', name: 'popupRef',\n property: { component: 'popup' } },\n { type: 'id-input', label: 'legend-target', name: 'legendTarget',\n property: { component: 'legend' },\n placeholder: '미명시 시 scene 의 legend 자동 발견' }\n ],\n help: 'scene/component/stockpile'\n}\n\n@sceneComponent('stockpile')\nexport default class Stockpile extends RecordStorage<StockpileRecord>()(\n CarrierHolder(Placeable(ContainerAbstract))\n) implements SlottedHolder {\n declare state: StockpileState\n declare _realObject?: Stockpile3D\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (_h: Heights) => 5 // pad 두께\n\n /**\n * RoutingTarget — 자기 행선지 값 publish. sorter 가 carrier.destination 과 매칭.\n * 미설정 시 자기 id 로 (단일 sink 직접 지정 fallback). chute 와 동일 규약.\n */\n routingDestination(): string {\n return (this.state.destination as string | undefined) ?? (this.state.id as string)\n }\n\n // ── 그래프 참여 ─────────────────────────────────────────────────────────────\n // StockPile 은 *방향 없는 받는 footprint*. 별도 면 anchor 를 publish 하지 않는다 —\n // 어느 면으로 받는지는 의미 없고, 슈트/컨베이어의 *출구 anchor 가 자기 footprint 에\n // 닿으면*(matchOutbounds 의 deliversInto, bounds 기반) 자동 연결된다. (사용자 명시\n // 2026-06-14: \"스톡파일은 슈트의 출구와 면한 부분이 있으면 그곳을 링크\".) RoutingTarget\n // (routingDestination) 만으로 그래프 노드로 수집되고, bounds 는 coordinator 가 채운다.\n // 멀리 떨어진 타겟은 명시 링크(nextRef/RoutingLinks)로 연결.\n\n get nature(): ComponentNature { return NATURE }\n get anchors() { return [] }\n\n // ── records / inventoryCount: RecordStorage mixin 제공.\n\n // ── SlottedHolder duck-type override — 단일 slot ('pile') ───────────────\n // mixin default 는 record 마다 slotId. Stockpile 은 *_단일 슬롯_* 시맨틱이라\n // 자기 구현 유지.\n slotIds(): ReadonlyArray<string> { return [SLOT_ID] }\n\n hasCarrierAt(slotId: string): boolean {\n return slotId === SLOT_ID && this.inventoryCount > 0\n }\n\n canReceiveAt(slotId: string, _carrier?: Component): boolean {\n if (slotId !== SLOT_ID) return false\n const cap = this.state.capacity\n if (typeof cap === 'number' && this.inventoryCount >= cap) return false\n return true\n }\n\n /** Transferable contract — dispatch/Transfer planning 이 capacity 확인에 호출. 단일 slot 위임. */\n canReceive(carrier?: Component): boolean {\n return this.canReceiveAt(SLOT_ID, carrier)\n }\n\n occupiedSlotIds(): ReadonlyArray<string> {\n return this.inventoryCount > 0 ? [SLOT_ID] : []\n }\n emptySlotIds(): ReadonlyArray<string> {\n return this.canReceiveAt(SLOT_ID) ? [SLOT_ID] : []\n }\n\n /**\n * record 한 개를 빼서 carrier 컴포넌트로 transient materialize. mover 가 pickup\n * 하면 이걸 reparent 해서 들고 다닌다. storage-rack._materializeCarrier 와 동일\n * 패턴 — Component.register(type) 으로 클래스 lookup, addComponent({silent: true})\n * 로 cascade 차단.\n */\n obtainCarrier(slotId: string): Component | null {\n if (slotId !== SLOT_ID) return null\n if (this.records.length === 0) return null\n const records = [...this.records]\n const policy = (this.state.pickPolicy ?? 'lifo') as PickPolicy\n const record = policy === 'fifo' ? records.shift()! : records.pop()!\n this._setDataSilently(records)\n return this._materializeCarrier(record)\n }\n\n private _materializeCarrier(record: StockpileRecord): Component | null {\n // carrierPreset → 등록된 컴포넌트 type. 미등록(drum/sack/bale 등) 은 시각 mesh\n // 전용이라 carrier 컴포넌트로는 fallback ('parcel'/'box').\n const preset = (this.state.carrierPreset ?? 'box') as CarrierPreset\n const PRESET_TO_TYPE: Record<CarrierPreset, string> = {\n box: 'box',\n pallet: 'pallet',\n crate: 'box',\n drum: 'parcel',\n sack: 'parcel',\n bale: 'parcel'\n }\n const carrierType = (record as any).type ?? PRESET_TO_TYPE[preset] ?? 'parcel'\n const CarrierClass = (Component as any).register(carrierType) as\n | (new (...args: any[]) => Component) | undefined\n if (!CarrierClass) {\n console.warn(`[stockpile] carrier type \"${carrierType}\" 미등록 — obtainCarrier 실패`)\n return null\n }\n\n // 크기 — state.carrier* 또는 preset default\n const stockpileW = (this.state as any).width ?? 100\n const stockpileH = (this.state as any).height ?? 100\n const cw = this.state.carrierWidth ?? 30\n const ch = this.state.carrierHeight ?? 30\n const cd = this.state.carrierDepth ?? 22\n\n // id/refid/transform 류 제외 — scene 안 기존 component 충돌 회피 (storage-rack 패턴)\n const { id: _id, refid: _refid, transform: _tf, ...recordCopy } = record as any\n // stockpile-inner 좌표 — center 에 놓기 (Mover 가 곧 pick 해 들고 감)\n const carrierState: any = {\n ...recordCopy,\n type: carrierType,\n width: cw,\n height: ch,\n depth: cd,\n refid: _nextStockpileCarrierRefid(),\n left: stockpileW / 2 - cw / 2,\n top: stockpileH / 2 - ch / 2\n }\n\n const carrier = new CarrierClass(carrierState, (this as any)._app)\n // silent: refreshMappings cascade 차단 (transient 라 매핑 재계산 불필요)\n ;(this as any).addComponent(carrier, { silent: true })\n void (carrier as any).realObject\n ;(carrier as any).applyHolderAttachPoint?.()\n return carrier\n }\n\n /**\n * carrier 의 state → StockpileRecord 추출. id 누락 시 `stk-` prefix 자동 생성.\n * transform/refid 등 *_물리적 위치 필드_* 는 *_제외_* — record 는 *_논리적 식별_*\n * 만. SlottedHolder duck 의 표준 entry point — receiveAt 안에서 호출.\n */\n recordFromCarrier(carrier: Component, _slotId: string): StockpileRecord {\n const cstate: any = (carrier as any)?.state ?? {}\n const cid = cstate.id ?? `stk-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`\n return {\n id: String(cid),\n ...(cstate.type ? { type: cstate.type } : {}),\n ...(typeof cstate.width === 'number' ? { width: cstate.width } : {}),\n ...(typeof cstate.height === 'number' ? { height: cstate.height } : {}),\n ...(typeof cstate.depth === 'number' ? { depth: cstate.depth } : {})\n }\n }\n\n /**\n * carrier 를 받아 record 로 push, carrier 객체는 dispose (시각은 _realObject 가\n * records 길이 기준 자동 갱신). capacity 초과 시도는 canReceiveAt 가 이미 차단.\n */\n async receiveAt(slotId: string, carrier: Component, _options?: any): Promise<void> {\n const record = this.recordFromCarrier(carrier, slotId)\n const records = [...this.records, record]\n this._setDataSilently(records)\n ;(carrier as any)?.dispose?.()\n }\n\n /** dispatch → handoff 의 accept 분기 — receiveAt 으로 위임. */\n async accept(carrier: Component, options?: any): Promise<void> {\n return this.receiveAt(SLOT_ID, carrier, options)\n }\n async receive(carrier: Component, options?: any): Promise<void> {\n return this.receiveAt(SLOT_ID, carrier, options)\n }\n\n /**\n * mover 가 obtainCarrier 로 빼낸 transient carrier 를 pad 위에 안착 (잠깐 보이는\n * 위치). mover 가 pick 하면 즉시 deck 으로 reparent 되므로 잔류는 짧다.\n * Spot 과 동일 idiom — pad-top + carrier 의 halfDepth 만큼 들어올림.\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const ro = this._realObject\n const frame = ro?.getAttachFrame?.()\n if (!frame) return null\n const carrierDepth = resolveDepth(carrier)\n return {\n attach: frame,\n localPosition: { x: 0, y: carrierDepth / 2, z: 0 }\n }\n }\n\n slotTargetAt(slotId: string): SlotTarget {\n return new SlotTarget(this as any, slotId)\n }\n getSlotAttachObject3d(slotId: string): any {\n // slotId 가 'pile' 이면 pad. record.id 면 그 carrier mesh — popup tether 가 그\n // stock 에 정확히 연결되도록.\n return (this as any)._realObject?.getAttachFrame?.(slotId)\n }\n\n // _setDataSilently 는 RecordStorage mixin 제공 — 다만 mixin 은 _rebuildVisual\n // 호출. Stockpile 의 3D 갱신은 update() 라서 hook override.\n _rebuildVisual(): void {\n this._realObject?.update?.()\n }\n\n /**\n * 2D — outlined pad + 인벤토리 카운트 텍스트. 평치 의도가 한 눈에 — 사각 영역 위에\n * 적재된 carrier 수가 가운데 큰 글씨로.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { left = 0, top = 0, width = 100, height = 100 } = this.state\n const fillStyle = (this.state.fillStyle as string) || '#c89c5c'\n const strokeStyle = (this.state.strokeStyle as string) || fillStyle\n\n // pad (반투명)\n ctx.save()\n ctx.fillStyle = fillStyle\n ctx.globalAlpha = 0.18\n ctx.fillRect(left, top, width, height)\n ctx.restore()\n\n // outline (dashed)\n ctx.save()\n ctx.strokeStyle = strokeStyle\n ctx.lineWidth = 1.5\n ctx.setLineDash([6, 3])\n ctx.strokeRect(left + 0.75, top + 0.75, width - 1.5, height - 1.5)\n ctx.setLineDash([])\n ctx.restore()\n\n // 인벤토리 수 + capacity (있으면)\n ctx.save()\n const fontSize = Math.min(width, height) * 0.22\n ctx.fillStyle = '#333'\n ctx.font = `bold ${fontSize}px sans-serif`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n const label = typeof this.state.capacity === 'number'\n ? `${this.inventoryCount}/${this.state.capacity}`\n : `${this.inventoryCount}`\n ctx.fillText(label, left + width / 2, top + height / 2)\n ctx.restore()\n }\n\n // ── Popup 연동 — storage-rack 동일 패턴 ───────────────────────────────\n /**\n * things-scene EventManager3D 가 raycast → object3d.userData.context.component 의\n * `trigger(\"click\", mouseEvent)` 을 호출 → eventMap 으로 receive. pad / carrier mesh\n * 어느 쪽을 클릭하든 stockpile 전체 popup invoke (단일 slot 이라 cell 구분 없음).\n */\n get eventMap() {\n return {\n '(self)': {\n '(self)': {\n click: this._onStockpileClick\n }\n }\n }\n }\n\n private _onStockpileClick = (mouseEvent: MouseEvent) => {\n // view mode 에서만 동작 (modeling 중 click 은 framework 선택 로직 우선).\n if (!(this as any).app?.isViewMode) return\n const hit = this._raycastStockpileHit(mouseEvent)\n if (!hit) return\n // hit.object 가 carrier mesh 면 userData.recordId 보유 → 그 stock 의 popup.\n // pad / 기타면 stockpile 전체 popup.\n const recordId = hit.object?.userData?.recordId as string | undefined\n this._dispatchStockpilePopup(typeof recordId === 'string' ? recordId : undefined)\n }\n\n /**\n * state.popupRef 가 가리키는 Popup 컴포넌트를 invoke.\n * - recordId 명시 → 그 record 의 anchor = mesh. payload = 해당 record.\n * - 미명시 (pad 클릭) → 'pile' anchor (pad). payload = 전체 inventory.\n *\n * RecordStorage mixin 의 `_invokePopup(slotId, payload)` 를 활용. 단일 slot\n * 시맨틱이 record/pile 두 모드라 mixin 위 wrapper 로 dispatch.\n */\n private _dispatchStockpilePopup(recordId?: string): void {\n if (!this.state.popupRef) return\n if (recordId) {\n const record = (this.records as ReadonlyArray<StockpileRecord>)\n .find(r => r.id === recordId) ?? { id: recordId }\n this._invokePopup(recordId, record)\n } else {\n this._invokePopup(SLOT_ID, {\n componentId: (this.state as any).id,\n records: this.records,\n inventoryCount: this.inventoryCount,\n capacity: this.state.capacity,\n carrierPreset: this.state.carrierPreset,\n stackPattern: this.state.stackPattern\n })\n }\n }\n\n /**\n * 클릭 시 framework 의 mouse NDC 를 재사용해 raycast → *우리 stockpile* 의 어떤 mesh 가\n * closest hit 인지 반환 (다른 object 가 더 가까우면 undefined). storage-rack._raycastRackHit\n * 와 동일 패턴 — capability.getObjectsByRaycast 우선, 없으면 scene/camera/canvas 재구성.\n */\n private _raycastStockpileHit(mouseEvent: MouseEvent): THREE.Intersection | undefined {\n const ro: any = (this as any)._realObject\n if (!ro?.object3d) return undefined\n\n const tc: any = ro.threeContainer\n if (!tc) return undefined\n\n const cap: any = tc._threeCapability ?? tc._capability\n let intersects: THREE.Intersection[] | undefined\n if (cap?.getObjectsByRaycast) {\n intersects = cap.getObjectsByRaycast() as THREE.Intersection[] | undefined\n }\n if (!intersects || intersects.length === 0) {\n const scene = tc.scene3d as THREE.Scene | undefined\n const renderer = tc.renderer3d as THREE.WebGLRenderer | undefined\n const camera =\n (tc.activeCamera3d as THREE.Camera | undefined) ??\n (cap?.activeCamera as THREE.Camera | undefined) ??\n (cap?.camera as THREE.Camera | undefined)\n const canvas = renderer?.domElement\n if (!scene || !canvas || !camera) return undefined\n const rect = canvas.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return undefined\n const ndc = new THREE.Vector2(\n ((mouseEvent.clientX - rect.left) / rect.width) * 2 - 1,\n -((mouseEvent.clientY - rect.top) / rect.height) * 2 + 1\n )\n const raycaster = new THREE.Raycaster()\n raycaster.setFromCamera(ndc, camera)\n intersects = raycaster.intersectObjects(scene.children, true)\n }\n if (!intersects || intersects.length === 0) return undefined\n\n // 가장 가까운 hit 이 *이 stockpile* 의 descendant 여야 한다 (다른 mesh 가 사이에 있으면 skip).\n const closest = intersects[0]\n let obj: THREE.Object3D | null = closest.object\n while (obj) {\n if (obj.userData?.context === ro) return closest\n obj = obj.parent\n }\n return undefined\n }\n\n // legendTarget / _onLegendChanged / resolveLegendColor — RecordStorage mixin 제공.\n\n buildRealObject(): RealObject | undefined {\n return new Stockpile3D(this)\n }\n}\n\n// transient carrier refid — scene 내 기존 컴포넌트와 충돌 회피용 큰 시작값\n// (storage-rack 의 _nextCarrierRefid 와 같은 idiom, 별도 범위로 분리).\nlet _stockpileCarrierSeq = 0\nfunction _nextStockpileCarrierRefid(): number {\n return 800000 + (_stockpileCarrierSeq++)\n}\n\nfunction resolveDepth(c: Component): number {\n const eff = (c as any)._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n const d = (c as any)?.state?.depth\n return typeof d === 'number' && Number.isFinite(d) ? d : 0\n}\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/scene-storage",
3
3
  "description": "Storage-domain components for things-scene (smart factory / logistics) — pallet, box, parcel; AS/RS and shelves planned.",
4
4
  "author": "heartyoh",
5
- "version": "10.0.0-beta.56",
5
+ "version": "10.0.0-beta.57",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@hatiolab/things-scene": "^10.0.0-beta.1",
29
- "@operato/scene-base": "^10.0.0-beta.56",
29
+ "@operato/scene-base": "^10.0.0-beta.57",
30
30
  "three": "^0.183.0"
31
31
  },
32
32
  "devDependencies": {
@@ -45,5 +45,5 @@
45
45
  "typescript": "^5.0.4"
46
46
  },
47
47
  "prettier": "@hatiolab/prettier-config",
48
- "gitHead": "0f2063b2545025837fa21ef15bfb47e7b4a07f1d"
48
+ "gitHead": "67047749cb873dfe1b29d21e19d5883b1dea150f"
49
49
  }
@@ -13,12 +13,79 @@
13
13
 
14
14
  import * as THREE from 'three'
15
15
  import { RealObjectGroup } from '@hatiolab/things-scene'
16
+ import { computeStackPositions } from '@operato/scene-base'
16
17
 
17
18
  import type Stockpile from './stockpile.js'
18
19
  import type { StackPattern, CarrierPreset } from './stockpile.js'
19
20
 
20
21
  const PAD_DEPTH = 2
21
22
 
23
+ /**
24
+ * 그래프(root.__flowGraph)에서 이 stockpile 로 *들어오는* 노드(입구)를 찾아, 적치를 *입구
25
+ * 맞은편부터* 하도록 reverseX/Z 를 산출한다. storage 가 conveyance 를 import 하지 않고
26
+ * 캐시 속성만 일반적으로 읽음(토폴로지 viz 와 동일 방식). 입구/그래프 없으면 undefined →
27
+ * 기본(-x/-z 모서리부터). 입구 방향은 stockpile local frame 기준(회전 반영).
28
+ */
29
+ function inflowFillOpts(stockpile: any): { advanceAlongX?: boolean; reverse?: boolean } | undefined {
30
+ const root = stockpile?.root
31
+ // 로드 직후(sim 전)엔 그래프 캐시가 없을 수 있다 — conveyance 가 globalThis 에 심은
32
+ // 빌더로 즉석 build(토폴로지 viz 와 동일). 없으면(컨베이어 미로드) 기본값.
33
+ if (root && !root.__flowGraph) {
34
+ try { (globalThis as any).__operatoBuildFlowGraph?.(root) } catch { /* ignore */ }
35
+ }
36
+ const cache = root?.__flowGraph
37
+ const out = cache?.graph?.out as Map<string, Array<string | null>> | undefined
38
+ const comps = cache?.components as Map<string, any> | undefined
39
+ // 그래프 빌더(flow-graph-coordinator collect)와 *동일* idOf — refid 는 set('refid') 로
40
+ // state.refid 에 저장되므로 c.refid 와 state.refid 를 둘 다 확인해야 한다. (c.refid 만 보면
41
+ // 명시 id 없는 stockpile 이 전부 undefined → bail → 기본값으로 떨어져 입구 도출 실패.)
42
+ const idOf = (c: any): string | undefined => {
43
+ const raw = c?.state?.id
44
+ if (typeof raw === 'string' && raw.length > 0) return raw
45
+ const refid = c?.refid ?? c?.state?.refid
46
+ return refid != null ? String(refid) : undefined
47
+ }
48
+ const myId = idOf(stockpile)
49
+ if (!out || !comps || !myId) return undefined
50
+ let feeder: any
51
+ out.forEach((targets, srcId) => {
52
+ if (!feeder && Array.isArray(targets) && targets.includes(myId)) feeder = comps.get(srcId)
53
+ })
54
+ const ss = stockpile?.state
55
+ // feeder(입구) 못 찾으면 기본값(undefined) — 마주한 슈트 없는 stockpile.
56
+ if (!feeder || !ss) return undefined
57
+ // 입구 흐름 = *소스 링크의 방향* — feeder(슈트)의 배출 anchor(이 stockpile 로 향하는) direction.
58
+ // (중심-중심 벡터가 아니라 링크 방향면을 써야 정확. 긴 슈트의 중심은 실제 유입면과 다를 수 있음.)
59
+ let dir: { x: number; z: number } | undefined
60
+ try {
61
+ const outs = feeder.getSegmentTopology?.()?.outbounds as Array<{ worldPos?: { x: number; z: number }; direction?: { x: number; z: number } }> | undefined
62
+ if (Array.isArray(outs) && outs.length) {
63
+ const sx = (ss.left ?? 0) + (ss.width ?? 0) / 2
64
+ const sz = (ss.top ?? 0) + (ss.height ?? 0) / 2
65
+ let best = outs[0]
66
+ let bestD = Infinity
67
+ for (const a of outs) {
68
+ if (!a?.worldPos || !a.direction) continue
69
+ const d = (a.worldPos.x - sx) ** 2 + (a.worldPos.z - sz) ** 2
70
+ if (d < bestD) { bestD = d; best = a } // stockpile 에 가장 가까운 배출 anchor
71
+ }
72
+ if (best?.direction) dir = best.direction
73
+ }
74
+ } catch { /* topology 못 얻으면 fallback 없음 → 기본 */ }
75
+ if (!dir) return undefined
76
+ // 흐름 방향(world)을 stockpile local frame 으로 (회전 -rot).
77
+ const rot = typeof ss.rotation === 'number' ? ss.rotation : 0
78
+ const cos = Math.cos(-rot)
79
+ const sin = Math.sin(-rot)
80
+ const lx = dir.x * cos - dir.z * sin
81
+ const lz = dir.x * sin + dir.z * cos
82
+ // 흐름이 x 우세면 x축 진행. items 는 흐름 방향 끝(먼저 닿는 곳)부터 쌓여 입구쪽으로 →
83
+ // 흐름이 + 면 + 끝부터(reverse).
84
+ const advanceAlongX = Math.abs(lx) >= Math.abs(lz)
85
+ const reverse = advanceAlongX ? lx > 0 : lz > 0
86
+ return { advanceAlongX, reverse }
87
+ }
88
+
22
89
  /** carrierPreset 별 default 색 (state.carrierWidth/Height/Depth 미지정 시 크기와 함께). */
23
90
  const PRESET_COLOR: Record<CarrierPreset, number> = {
24
91
  box: 0xb87333, // kraft brown
@@ -186,7 +253,15 @@ export class Stockpile3D extends RealObjectGroup {
186
253
  return presetDefaultColor
187
254
  }
188
255
 
189
- const positions = this._computeStackPositions(pattern, count, areaW, areaH, cw, ch, cd, gap, state.stackHeightLimit)
256
+ // stackHeightLimit = *총 적치 높이*(scene unit) 단(layer) 개수가 아니다. 아이템
257
+ // 한 단 높이(cd)로 나눠 최대 단 수로 환산해 전달. 예: 한도 60 + 아이템높이 10 → 6단.
258
+ const heightLimit = state.stackHeightLimit
259
+ const maxLayers = typeof heightLimit === 'number' && heightLimit > 0 && cd > 0
260
+ ? Math.max(1, Math.floor(heightLimit / cd))
261
+ : undefined
262
+ // 입구 맞은편부터 채우기 — 그래프에서 이 stockpile 로 들어오는 노드(입구)를 도출.
263
+ const fillOpts = inflowFillOpts(stockpile)
264
+ const positions = computeStackPositions(pattern, count, { w: areaW, h: areaH }, { w: cw, h: ch, d: cd }, gap, maxLayers, fillOpts)
190
265
  this._meshByRecordId.clear()
191
266
  const records = stockpile.records
192
267
  for (let i = 0; i < positions.length; i++) {
@@ -213,111 +288,6 @@ export class Stockpile3D extends RealObjectGroup {
213
288
  return this._meshByRecordId.get(recordId)
214
289
  }
215
290
 
216
- /**
217
- * stackPattern 별 각 carrier 의 local 좌표(영역 중심 기준). y 는 pad 위에 쌓이는
218
- * 높이(cd 단위), x/z 는 영역 평면 위 분포.
219
- */
220
- private _computeStackPositions(
221
- pattern: StackPattern,
222
- count: number,
223
- areaW: number,
224
- areaH: number,
225
- cw: number,
226
- ch: number,
227
- cd: number,
228
- gap: number,
229
- heightLimit?: number
230
- ): Array<{ x: number; y: number; z: number }> {
231
- const out: Array<{ x: number; y: number; z: number }> = []
232
-
233
- // column — 한 자리에 수직 적층
234
- if (pattern === 'column') {
235
- const layers = heightLimit ?? count
236
- for (let i = 0; i < count; i++) {
237
- const layer = Math.min(i, layers - 1)
238
- out.push({ x: 0, y: layer * cd + cd / 2, z: 0 })
239
- }
240
- return out
241
- }
242
-
243
- // 격자 cell — 행/열 수. cell stride = carrier + gap (인접 carrier 사이 분리).
244
- const stridX = cw + gap
245
- const stridZ = ch + gap
246
- const cols = Math.max(1, Math.floor((areaW + gap) / stridX))
247
- const rows = Math.max(1, Math.floor((areaH + gap) / stridZ))
248
- const cellsPerLayer = cols * rows
249
-
250
- // pyramid — 위로 갈수록 행/열 -1 씩 (사다리꼴)
251
- if (pattern === 'pyramid') {
252
- let remaining = count
253
- let layer = 0
254
- while (remaining > 0) {
255
- const lcols = Math.max(1, cols - layer)
256
- const lrows = Math.max(1, rows - layer)
257
- const cellsThisLayer = lcols * lrows
258
- const take = Math.min(remaining, cellsThisLayer)
259
- for (let i = 0; i < take; i++) {
260
- const c = i % lcols
261
- const r = Math.floor(i / lcols)
262
- // 가운데 정렬 — 줄어든 만큼 offset
263
- const offsetCol = (cols - lcols) / 2
264
- const offsetRow = (rows - lrows) / 2
265
- const x = -areaW / 2 + (c + offsetCol + 0.5) * stridX
266
- const z = -areaH / 2 + (r + offsetRow + 0.5) * stridZ
267
- out.push({ x, y: layer * cd + cd / 2, z })
268
- }
269
- remaining -= take
270
- layer++
271
- if (lcols === 1 && lrows === 1) {
272
- // 정상에 도달 — 남은 건 column 처럼 위로
273
- for (let i = 0; i < remaining; i++) {
274
- out.push({ x: 0, y: (layer + i) * cd + cd / 2, z: 0 })
275
- }
276
- break
277
- }
278
- if (heightLimit && layer >= heightLimit) break
279
- }
280
- return out
281
- }
282
-
283
- // pile — 자연 더미. row 와 비슷하되 가장자리는 약간 옆으로 흘러내림 효과
284
- // (간단 구현 — 단마다 약간 random offset).
285
- if (pattern === 'pile') {
286
- // pseudo-random (인덱스 기반, deterministic)
287
- const rng = (i: number) => ((Math.sin(i * 12.9898) * 43758.5453) % 1 + 1) % 1
288
- for (let i = 0; i < count; i++) {
289
- const layer = Math.floor(i / cellsPerLayer)
290
- const idx = i % cellsPerLayer
291
- const c = idx % cols
292
- const r = Math.floor(idx / cols)
293
- const jitterX = (rng(i * 2) - 0.5) * cw * 0.2
294
- const jitterZ = (rng(i * 2 + 1) - 0.5) * ch * 0.2
295
- const x = -areaW / 2 + (c + 0.5) * stridX + jitterX
296
- const z = -areaH / 2 + (r + 0.5) * stridZ + jitterZ
297
- out.push({ x, y: layer * cd + cd / 2, z })
298
- if (heightLimit && layer >= heightLimit - 1 && idx === cellsPerLayer - 1) break
299
- }
300
- return out
301
- }
302
-
303
- // row (default) / staggered — 격자 적층
304
- for (let i = 0; i < count; i++) {
305
- const layer = Math.floor(i / cellsPerLayer)
306
- if (heightLimit && layer >= heightLimit) break
307
- const idx = i % cellsPerLayer
308
- const c = idx % cols
309
- const r = Math.floor(idx / cols)
310
- let x = -areaW / 2 + (c + 0.5) * stridX
311
- const z = -areaH / 2 + (r + 0.5) * stridZ
312
- // staggered — 홀수 layer 는 1/2 cell offset (벽돌식)
313
- if (pattern === 'staggered' && layer % 2 === 1) {
314
- x += stridX / 2
315
- }
316
- out.push({ x, y: layer * cd + cd / 2, z })
317
- }
318
- return out
319
- }
320
-
321
291
  /** carrierPreset 별 geometry. drum 은 cylinder, 나머지는 box. */
322
292
  private _carrierGeometry(preset: CarrierPreset, w: number, h: number, d: number): THREE.BufferGeometry {
323
293
  if (preset === 'drum') {
@@ -162,7 +162,11 @@ export class StockpileGrid3D extends RealObjectGroup {
162
162
  const ch = state.carrierHeight ?? def.h
163
163
  const cd = state.carrierDepth ?? def.d
164
164
  const gap = state.carrierGap ?? 10
165
- const heightLimit = state.stackHeightLimit as number | undefined
165
+ // stackHeightLimit = *총 적치 높이*(scene unit) → 단(layer) 수로 환산 (아이템 한 단 = cd).
166
+ const rawHeightLimit = state.stackHeightLimit as number | undefined
167
+ const heightLimit = typeof rawHeightLimit === 'number' && rawHeightLimit > 0 && cd > 0
168
+ ? Math.max(1, Math.floor(rawHeightLimit / cd))
169
+ : undefined
166
170
 
167
171
  const cellW = grid.cellW
168
172
  const cellH = grid.cellH
package/src/stockpile.ts CHANGED
@@ -63,6 +63,12 @@ export interface StockpileState extends State {
63
63
  /** 어느 끝에서 빼나. default 'lifo'. */
64
64
  pickPolicy?: PickPolicy
65
65
 
66
+ /**
67
+ * 라우팅 행선지 값 (RoutingTarget). sorter/router 가 carrier.destination 과 string
68
+ * 매칭해 이 StockPile 로 분배. 미설정 시 routingDestination() 가 state.id 로 fallback.
69
+ */
70
+ destination?: string
71
+
66
72
  /** click 시 invoke 할 Popup 컴포넌트 id (StorageRack / RackGrid 와 동일 패턴). */
67
73
  popupRef?: string
68
74
 
@@ -95,6 +101,7 @@ const NATURE: ComponentNature = {
95
101
  { type: 'number', label: 'stack-height-limit', name: 'stackHeightLimit' },
96
102
  { type: 'select', label: 'pick-policy', name: 'pickPolicy',
97
103
  property: { options: ['lifo', 'fifo'] } },
104
+ { type: 'string', label: 'destination', name: 'destination' },
98
105
  { type: 'id-input', label: 'popup-ref', name: 'popupRef',
99
106
  property: { component: 'popup' } },
100
107
  { type: 'id-input', label: 'legend-target', name: 'legendTarget',
@@ -115,6 +122,22 @@ export default class Stockpile extends RecordStorage<StockpileRecord>()(
115
122
  static align: Alignment = 'bottom'
116
123
  static defaultDepth = (_h: Heights) => 5 // pad 두께
117
124
 
125
+ /**
126
+ * RoutingTarget — 자기 행선지 값 publish. sorter 가 carrier.destination 과 매칭.
127
+ * 미설정 시 자기 id 로 (단일 sink 직접 지정 fallback). chute 와 동일 규약.
128
+ */
129
+ routingDestination(): string {
130
+ return (this.state.destination as string | undefined) ?? (this.state.id as string)
131
+ }
132
+
133
+ // ── 그래프 참여 ─────────────────────────────────────────────────────────────
134
+ // StockPile 은 *방향 없는 받는 footprint*. 별도 면 anchor 를 publish 하지 않는다 —
135
+ // 어느 면으로 받는지는 의미 없고, 슈트/컨베이어의 *출구 anchor 가 자기 footprint 에
136
+ // 닿으면*(matchOutbounds 의 deliversInto, bounds 기반) 자동 연결된다. (사용자 명시
137
+ // 2026-06-14: "스톡파일은 슈트의 출구와 면한 부분이 있으면 그곳을 링크".) RoutingTarget
138
+ // (routingDestination) 만으로 그래프 노드로 수집되고, bounds 는 coordinator 가 채운다.
139
+ // 멀리 떨어진 타겟은 명시 링크(nextRef/RoutingLinks)로 연결.
140
+
118
141
  get nature(): ComponentNature { return NATURE }
119
142
  get anchors() { return [] }
120
143
 
@@ -136,6 +159,11 @@ export default class Stockpile extends RecordStorage<StockpileRecord>()(
136
159
  return true
137
160
  }
138
161
 
162
+ /** Transferable contract — dispatch/Transfer planning 이 capacity 확인에 호출. 단일 slot 위임. */
163
+ canReceive(carrier?: Component): boolean {
164
+ return this.canReceiveAt(SLOT_ID, carrier)
165
+ }
166
+
139
167
  occupiedSlotIds(): ReadonlyArray<string> {
140
168
  return this.inventoryCount > 0 ? [SLOT_ID] : []
141
169
  }