@operato/scene-storage 10.0.0-beta.35 → 10.0.0-beta.36
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 +9 -0
- package/dist/crane-3d.js +53 -19
- package/dist/crane-3d.js.map +1 -1
- package/dist/crane.d.ts +15 -2
- package/dist/crane.js +181 -19
- package/dist/crane.js.map +1 -1
- package/package.json +2 -2
- package/src/crane-3d.ts +55 -19
- package/src/crane.ts +200 -19
- package/tsconfig.tsbuildinfo +1 -1
package/dist/crane.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crane.js","sourceRoot":"","sources":["../src/crane.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { SlotDef, State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Legendable,\n Mover,\n Placeable,\n type AttachFrame,\n type Alignment,\n type Heights,\n type LegendBinding,\n type MoveOptions,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { Crane3D } from './crane-3d.js'\n\n/**\n * Crane status — the operating state of a stacker crane.\n *\n * - `idle` — parked at home position\n * - `moving` — translating (horizontal) or along the mast (vertical);\n * typically combined\n * - `loading` — extracting a carrier from a rack cell\n * - `unloading` — placing a carrier into a rack cell\n * - `error` — fault / e-stop / collision warning\n */\nexport type CraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error'\n\n/** Crane 컴포넌트 state */\nexport interface CraneState extends State {\n // ── 운영 상태 ──\n status?: CraneStatus\n\n /**\n * Crane 이 서비스 할 타깃 컴포넌트의 refid (또는 id).\n * 명시되면 그 컴포넌트 (또는 그 children) 의 위치를 stop point 로 사용 — heuristic\n * 으로 sibling 추정 안 함. 미명시면 sibling 안에서 추측.\n * 예: RackGrid 의 refid 를 지정하면 그 안의 cell 들 사이에서만 이동.\n */\n target?: string\n\n // ── 액추에이터 ──\n /** 마스트 따라 carriage 의 수직 위치 (mm). */\n carriageHeight?: number\n /**\n * Fork prong 의 최대 신축 가능 길이 (mm). cell 깊이 + 여유.\n * 일반적으로 pallet 깊이 (1200mm) + 여유 ≈ 1300mm 또는 그 이하 (default 600).\n */\n forkLength?: number\n /**\n * Fork 의 *현재 신축 거리* (mm). 0 = collapsed (shuttle 안 들어가있음),\n * forkLength = fully extended (cell 안 carrier 의 fork pocket 도달).\n *\n * 시뮬레이션 흐름:\n * idle: 0\n * approach → 0\n * pick / place: 0 → forkLength (extending) → 0 (retracting with carrier)\n */\n forkExtension?: number\n /**\n * Fork 의 *carrier 들어올림 높이* (mm). 0 = neutral (cell shelf 와 같은 면),\n * +값 = carrier 를 *위로* 들어올림 (fork pocket 안으로 진입 후 위로 lifting).\n */\n forkLift?: number\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst BODY_LEGEND = {\n idle: '#888',\n moving: '#aabbcc',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#c66',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#222222',\n moving: '#44ff44',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#ff3333',\n default: '#222222'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Loading', value: 'loading' },\n { display: 'Unloading', value: 'unloading' },\n { display: 'Error', value: 'error' }\n ]\n }\n },\n {\n type: 'string',\n label: 'target',\n name: 'target',\n placeholder: 'refid of target (e.g., a RackGrid) — crane serves only its cells'\n },\n {\n type: 'number',\n label: 'carriage-height',\n name: 'carriageHeight',\n placeholder: 'mm — height of carriage on mast'\n },\n {\n type: 'number',\n label: 'fork-length',\n name: 'forkLength',\n placeholder: 'mm — fork prong 최대 신축 길이 (default 600)'\n },\n {\n type: 'number',\n label: 'fork-extension',\n name: 'forkExtension',\n placeholder: 'mm — 현재 신축 거리 (0 = collapsed)'\n },\n {\n type: 'number',\n label: 'fork-lift',\n name: 'forkLift',\n placeholder: 'mm — carrier 들어올림 높이 (+ 위로)'\n }\n ],\n help: 'scene/component/crane'\n}\n\n// Mixin chain: Mover → CarrierHolder → ContainerCapacity → Legendable → Placeable → ContainerAbstract\n//\n// Mover: pick / place / pickAndPlace / moveTo / engage primitives\n// CarrierHolder: attachPointFor() — where the carrier sits on the crane (carriage fork)\n// ContainerCapacity: receive() / dispatch() / canReceive() / slots — slot tracking +\n// TRANSFER_SLOT_KEY bookkeeping during transit\n// Legendable: status → bodyColor / lampEmissive colour mapping\n// Placeable: floor-archetype 3D positioning\n// ContainerAbstract: child management — carrier becomes a child while in transit\n//\n// Note: ContainerAbstract replaces Shape. The 2D outline is drawn manually in\n// render() below (a simple top-down rectangle), matching the old Shape output\n// without the Shape base-class overhead.\n/**\n * Crane — a stacker / retrieval picker. Picker contract instance.\n *\n * Moves carriers between rack cells (or any Placeable target). The crane\n * is *picker-side* of the Phase G/H Pickable contract — it knows how to\n * `pick(carrier)` / `place(carrier, target)` and `engage(target, kind)`\n * to align its actuators. It is *storage-agnostic* — any Rack / Cell /\n * Spot / Conveyor that implements the receive/dispatch protocol is a\n * valid target.\n *\n * Structure: a tall vertical mast that translates horizontally on a rail\n * or free floor path, with a carriage that slides up/down the mast carrying\n * a shuttle / forks.\n *\n * **Monitoring mode**: crane status is driven by external data binding\n * (`state.status`, `state.carriageHeight`). The carrier is referenced\n * via data binding — it is NOT a child of the crane in monitoring mode.\n *\n * **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, target)`\n * (or `crane.pickAndPlace(carrier, target)`). Mover handles navigation +\n * engage + reparent. During transit the carrier IS a child of the crane.\n */\n@sceneComponent('crane')\nexport default class Crane extends Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) {\n declare state: CraneState\n declare _realObject?: Crane3D\n\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.ceiling - h.floor\n\n /** Yaw offset: crane model is drawn with the rail axis along X (right = forward). */\n static yawOffset = 0\n\n /**\n * Phase H — the crane uses telescoping forks to pick up a carrier in a rack\n * cell. Same mechanism as a forklift fork — engages the pallet's fork pocket.\n * (Mover mixin chain is `: any` so override keyword is not usable, only the\n * getter is defined.)\n */\n get toolType(): string {\n return 'forklift-fork'\n }\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n // ── ContainerCapacity ─────────────────────────────────────────────────────\n\n /** Stacker crane carries at most one load at a time on its forks. */\n get slots(): SlotDef[] {\n return [{ id: 'forks', maxCount: 1 }]\n }\n\n // ── CarrierHolder — attach frame (carriage fork position) ─────────────────\n\n /**\n * Return the 3D attach frame on the crane's carriage (fork tip).\n * Carriers are attached here while the crane is in transit (pick phase).\n *\n * The Crane3D exposes `getCarriageFrame()` — a sub-Object3D that\n * tracks the carriage height and sits at the fork TCP. If the 3D object\n * isn't built yet (e.g. before scene initialization), fall back to the\n * crane's own object3d centre.\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const ro = this._realObject\n const frame = ro?.getCarriageFrame?.()\n if (frame) {\n const carrierDepth = resolveCarrierDepth(carrier)\n // fork blade 위 (top surface) 에 pallet pocket 끼우듯 얹힘. y = blade top + carrier/2.\n // z = blade 중심 (forkLength 의 반쪽 위치 — pallet center 가 blade 중간에 위치).\n const platformTopY = ro?.platformTopY ?? 0\n const bladeMidZ = ro?.bladeMidZ ?? 0\n return {\n attach: frame,\n localPosition: { x: 0, y: platformTopY + carrierDepth / 2, z: bladeMidZ }\n }\n }\n const root = this._realObject?.object3d\n if (!root) return null\n return { attach: root }\n }\n\n // ── Mover overrides ───────────────────────────────────────────────────────\n\n /**\n * Domain-specific actuation between arrival and reparent.\n *\n * Simulation sequence for PICK:\n * 1. Mover.pick() navigates crane to carrier position (moveTo).\n * 2. engage('pick') → snap carriage height + status 'loading'.\n * 3. Carrier is reparented to crane (becomes child).\n *\n * For now: set status and snap carriage height. A full simulation\n * would tween the carriageHeight here (animate Crane3D).\n *\n * Status lifecycle:\n * idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle\n * The 'moving' state is not set from Mover.moveTo() because TypeScript\n * can't call super.moveTo() on an `: any`-typed mixin. External data\n * binding sets 'moving' in monitoring mode; override pick()/place() to\n * set it in full simulation environments.\n */\n async engage(\n target: Component,\n kind: 'pick' | 'place',\n _options: MoveOptions = {}\n ): Promise<void> {\n if (kind === 'pick') {\n this.setState({ status: 'loading' as CraneStatus })\n const carrierY = resolveCarrierCenterY(target)\n if (carrierY !== null) {\n this.setState({ carriageHeight: carrierY })\n }\n } else {\n this.setState({ status: 'unloading' as CraneStatus })\n }\n // In a full simulation: await carriage-motion tween here.\n }\n\n // ── Domain aliases ────────────────────────────────────────────────────────\n\n /** Fetch a carrier from a rack cell (semantically = pick). */\n fetch(carrier: Component, options?: MoveOptions): Promise<void> {\n return this.pick(carrier, options)\n }\n\n /** Deposit a carrier into a rack cell (semantically = place). */\n deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void> {\n return this.place(carrier, cell, options)\n }\n\n // ── 2D rendering ─────────────────────────────────────────────────────────\n\n /**\n * 2D — top-down rectangle showing the crane's footprint along the rail.\n * The crane is much taller than wide, so the 2D mark is small.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n const fillColor = (this.state.bodyColor as string) || '#888'\n ctx.save()\n ctx.fillStyle = fillColor\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n ctx.fill()\n ctx.restore()\n }\n\n // ── 3D ───────────────────────────────────────────────────────────────────\n\n buildRealObject(): RealObject | undefined {\n if (!this._simStarted) {\n this._simStarted = true\n // 초기 지연 800~2800ms 사이 random — 여러 crane 이 동기적으로 시작하지 않도록.\n const initialDelay = 800 + Math.random() * 2000\n setTimeout(() => {\n this.simulate().catch(e => console.error('[Crane] simulate', e))\n }, initialDelay)\n }\n return new Crane3D(this)\n }\n\n // ── Random animation (visual smoke test) ─────────────────────────────────\n // things-scene 의 component.animate() (frameClock 기반, pause/speed 대응) 를 사용.\n\n private _simStarted = false\n private _simRunning = false\n private _simAbort = false\n // Rail = crane 의 *로컬 X 축* (state.rotation 적용). simulate 진입 시 1회 계산해서 캐시.\n private _railOriginX = 0\n private _railOriginY = 0\n private _railCos = 1\n private _railSin = 0\n private _railMin = NaN // local-X 축 rail offset min\n private _railMax = NaN // local-X 축 rail offset max\n private _targetSide = 1 // +Z (fork +Z 로 뻗어야 하나) / -Z\n\n /** Continuous random pick → transport → place cycles. Visual smoke test. */\n async simulate(): Promise<void> {\n if (this._simRunning) return\n this._simRunning = true\n this._simAbort = false\n try {\n this._initRailRange()\n while (!this._simAbort) {\n await this._oneCycle()\n }\n } finally {\n this._simRunning = false\n }\n }\n\n /**\n * state.target bbox 를 crane 의 *로컬 X 축* 으로 projection 해서 rail range 1회 계산.\n * Rotation 적용된 crane 의 local X (= rail) 방향으로 움직이도록 cos/sin 캐시.\n */\n private _initRailRange(): void {\n this._railMin = this._railMax = NaN\n\n // 1순위: state.target refid 로 명시. 2순위: crane 의 parent 컴포넌트 (자연스러운\n // \"이 컨테이너 안에서만 움직여\" 기본값).\n const targetRefId = (this.state as any).target as string | undefined\n const root = (this as any).root\n let target = targetRefId ? root?.findById?.(targetRefId) : null\n if (!target) {\n const parent = (this as any).parent\n // parent 가 root/model-layer 같은 top-level 이면 bbox 가 의미없음 → skip\n if (parent && parent !== root && (parent.state as any)?.width > 0) {\n target = parent\n }\n }\n if (!target) return\n\n const tState = target.state ?? {}\n const tl = numOr(tState.left, 0)\n const tt = numOr(tState.top, 0)\n const tw = numOr(tState.width, 0)\n const th = numOr(tState.height, 0)\n if (tw <= 0 || th <= 0) return\n\n const W = numOr((this.state as any).width, 100)\n const H = numOr((this.state as any).height, 100)\n const rotation = numOr((this.state as any).rotation, 0)\n const cos = Math.cos(rotation)\n const sin = Math.sin(rotation)\n\n // Rail origin = crane 의 현재 canvas center. 이 점에서 local X 방향으로 ±rail offset.\n this._railOriginX = numOr((this.state as any).left, 0) + W / 2\n this._railOriginY = numOr((this.state as any).top, 0) + H / 2\n this._railCos = cos\n this._railSin = sin\n\n // Target bbox 4 corner 를 crane local frame 으로 projection.\n // local X (rail) = dx*cos + dy*sin\n // local Z (aisle) = -dx*sin + dy*cos\n const corners = [\n { x: tl, y: tt },\n { x: tl + tw, y: tt },\n { x: tl, y: tt + th },\n { x: tl + tw, y: tt + th }\n ]\n let minRail = Infinity\n let maxRail = -Infinity\n let aisleSum = 0\n for (const c of corners) {\n const dx = c.x - this._railOriginX\n const dy = c.y - this._railOriginY\n const rail = dx * cos + dy * sin\n const aisle = -dx * sin + dy * cos\n if (rail < minRail) minRail = rail\n if (rail > maxRail) maxRail = rail\n aisleSum += aisle\n }\n // Crane 자체 W 만큼 안쪽으로 좁혀 — crane center 가 [minRail+W/2, maxRail-W/2] 안에\n // 있으면 crane 전체가 target bbox 안 (local X 방향).\n this._railMin = minRail + W / 2\n this._railMax = Math.max(this._railMin, maxRail - W / 2)\n this._targetSide = aisleSum >= 0 ? +1 : -1\n }\n\n stopSimulate(): void {\n this._simAbort = true\n // 진행 중인 animate() controller 도 즉시 정지 — onComplete 가 호출되어 _tween Promise\n // 가 resolve → simulate() while 루프가 다음 반복에서 _simAbort 보고 종료. 이걸로\n // 루프 stack 의 strong ref 해소, frameClock subscription 해제.\n const ctrl = (this as any)._animate\n if (ctrl && typeof ctrl.stop === 'function') {\n try { ctrl.stop() } catch {}\n }\n }\n\n /**\n * 컴포넌트 dispose 시 (보드 종료, scene reload 등) 진행 중 simulate 루프와 animate\n * subscription 을 정리. 미정리 시 루프의 await stack 이 component 를 strong reference\n * 로 잡아 GC 안 됨 + frameClock 콜백 살아남음.\n */\n dispose(): void {\n this.stopSimulate()\n super.dispose?.()\n }\n\n private async _oneCycle(): Promise<void> {\n const W = numOr((this.state as any).width, 100)\n const H = numOr((this.state as any).height, 100)\n const D = numOr((this.state as any).depth, W * 4)\n const forkLen = numOr((this.state as any).forkLength, H * 0.6)\n\n if (!Number.isFinite(this._railMin) || !Number.isFinite(this._railMax)) {\n await new Promise(r => setTimeout(r, 800))\n return\n }\n\n const pickRail = () => this._railMin + Math.random() * Math.max(0, this._railMax - this._railMin)\n const toLeftTop = (rail: number) => ({\n left: this._railOriginX + rail * this._railCos - W / 2,\n top: this._railOriginY + rail * this._railSin - H / 2\n })\n\n const source = toLeftTop(pickRail())\n const dest = toLeftTop(pickRail())\n\n const sourceCH = Math.random() * D * 0.75\n const destCH = Math.random() * D * 0.75\n const liftH = Math.max(20, D * 0.02)\n const side = this._targetSide\n\n // Tween duration 은 base 의 70~130% 사이 random — 여러 crane 이 같은 타이밍으로 안 보이도록.\n const jitter = (base: number): number => base * (0.7 + Math.random() * 0.6)\n\n // 이동: crane local X 방향 → canvas (left, top) 둘 다 동시 변경 (rotation 적용 시 diagonal)\n await this._tween({ status: 'moving', left: source.left, top: source.top, carriageHeight: sourceCH }, jitter(1500))\n await this._tween({ status: 'loading', forkExtension: side * forkLen }, jitter(700))\n await this._tween({ forkLift: liftH }, jitter(400))\n await this._tween({ forkExtension: 0 }, jitter(700))\n await this._tween({ status: 'moving', left: dest.left, top: dest.top, carriageHeight: destCH }, jitter(1500))\n await this._tween({ status: 'unloading', forkExtension: side * forkLen }, jitter(700))\n await this._tween({ forkLift: 0 }, jitter(400))\n await this._tween({ status: 'idle', forkExtension: 0 }, jitter(700))\n\n // 사이클 사이 짧은 idle (200~1000ms) — 자연스러운 phase 분산\n await new Promise(r => setTimeout(r, 200 + Math.random() * 800))\n }\n\n /**\n * things-scene 의 `component.animate()` 로 한 구간 tween. step 콜백에서 setState 호출.\n * frameClock.simTime 기반이라 scene pause / speed change / suppressAnimations 모두 자동 대응.\n */\n private _tween(targets: Record<string, any>, duration: number): Promise<void> {\n const start: Record<string, number> = {}\n for (const k of Object.keys(targets)) {\n if (k === 'status') continue\n const v = (this.state as any)[k]\n start[k] = typeof v === 'number' && Number.isFinite(v) ? v : 0\n }\n if (typeof targets.status === 'string') {\n this.setState({ status: targets.status })\n }\n return new Promise<void>(resolve => {\n let resolved = false\n const finish = () => {\n if (resolved) return\n resolved = true\n resolve()\n }\n const controller = (this as any).animate({\n duration,\n ease: 'inout',\n delta: 'quad',\n step: (dx: number) => {\n if (this._simAbort) {\n controller.stop?.()\n return finish()\n }\n const update: Record<string, number> = {}\n for (const k of Object.keys(start)) {\n const tgt = targets[k]\n if (typeof tgt !== 'number') continue\n update[k] = start[k] + (tgt - start[k]) * dx\n }\n this.setState(update as any)\n },\n onComplete: finish\n })\n controller.start()\n })\n }\n}\n\nfunction resolveCarrierDepth(c: Component): number {\n const eff = (c as any)._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n return numOr((c as any)?.state?.depth, 0)\n}\n\nfunction resolveCarrierCenterY(c: Component): number | null {\n const pos = (c as any).state\n if (!pos) return null\n // zPos is the 3D Y center of a Placeable component in things-scene\n const zPos = numOr(pos.zPos, NaN)\n if (!Number.isNaN(zPos)) return zPos\n return null\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n"]}
|
|
1
|
+
{"version":3,"file":"crane.js","sourceRoot":"","sources":["../src/crane.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { SlotDef, State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Legendable,\n Mover,\n Placeable,\n type AttachFrame,\n type Alignment,\n type Heights,\n type LegendBinding,\n type MoveOptions,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { Crane3D } from './crane-3d.js'\n\n/**\n * Crane status — the operating state of a stacker crane.\n *\n * - `idle` — parked at home position\n * - `moving` — translating (horizontal) or along the mast (vertical);\n * typically combined\n * - `loading` — extracting a carrier from a rack cell\n * - `unloading` — placing a carrier into a rack cell\n * - `error` — fault / e-stop / collision warning\n */\nexport type CraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error'\n\n/** Crane 컴포넌트 state */\nexport interface CraneState extends State {\n // ── 운영 상태 ──\n status?: CraneStatus\n\n /**\n * Crane 이 서비스 할 타깃 컴포넌트의 refid (또는 id).\n * 명시되면 그 컴포넌트 (또는 그 children) 의 위치를 stop point 로 사용 — heuristic\n * 으로 sibling 추정 안 함. 미명시면 sibling 안에서 추측.\n * 예: RackGrid 의 refid 를 지정하면 그 안의 cell 들 사이에서만 이동.\n */\n target?: string\n\n // ── 액추에이터 ──\n /** 마스트 따라 carriage 의 수직 위치 (mm). */\n carriageHeight?: number\n /**\n * Fork prong 의 최대 신축 가능 길이 (mm). cell 깊이 + 여유.\n * 일반적으로 pallet 깊이 (1200mm) + 여유 ≈ 1300mm 또는 그 이하 (default 600).\n */\n forkLength?: number\n /**\n * Fork 의 *현재 신축 거리* (mm). 0 = collapsed (shuttle 안 들어가있음),\n * forkLength = fully extended (cell 안 carrier 의 fork pocket 도달).\n *\n * 시뮬레이션 흐름:\n * idle: 0\n * approach → 0\n * pick / place: 0 → forkLength (extending) → 0 (retracting with carrier)\n */\n forkExtension?: number\n /**\n * Fork 의 *carrier 들어올림 높이* (mm). 0 = neutral (cell shelf 와 같은 면),\n * +값 = carrier 를 *위로* 들어올림 (fork pocket 안으로 진입 후 위로 lifting).\n */\n forkLift?: number\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst BODY_LEGEND = {\n idle: '#888',\n moving: '#aabbcc',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#c66',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#222222',\n moving: '#44ff44',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#ff3333',\n default: '#222222'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Loading', value: 'loading' },\n { display: 'Unloading', value: 'unloading' },\n { display: 'Error', value: 'error' }\n ]\n }\n },\n {\n type: 'string',\n label: 'target',\n name: 'target',\n placeholder: 'refid of target (e.g., a RackGrid) — crane serves only its cells'\n },\n {\n type: 'number',\n label: 'carriage-height',\n name: 'carriageHeight',\n placeholder: 'mm — height of carriage on mast'\n },\n {\n type: 'number',\n label: 'fork-length',\n name: 'forkLength',\n placeholder: 'mm — fork prong 최대 신축 길이 (default 600)'\n },\n {\n type: 'number',\n label: 'fork-extension',\n name: 'forkExtension',\n placeholder: 'mm — 현재 신축 거리 (0 = collapsed)'\n },\n {\n type: 'number',\n label: 'fork-lift',\n name: 'forkLift',\n placeholder: 'mm — carrier 들어올림 높이 (+ 위로)'\n }\n ],\n help: 'scene/component/crane'\n}\n\n// Mixin chain: Mover → CarrierHolder → ContainerCapacity → Legendable → Placeable → ContainerAbstract\n//\n// Mover: pick / place / pickAndPlace / moveTo / engage primitives\n// CarrierHolder: attachPointFor() — where the carrier sits on the crane (carriage fork)\n// ContainerCapacity: receive() / dispatch() / canReceive() / slots — slot tracking +\n// TRANSFER_SLOT_KEY bookkeeping during transit\n// Legendable: status → bodyColor / lampEmissive colour mapping\n// Placeable: floor-archetype 3D positioning\n// ContainerAbstract: child management — carrier becomes a child while in transit\n//\n// Note: ContainerAbstract replaces Shape. The 2D outline is drawn manually in\n// render() below (a simple top-down rectangle), matching the old Shape output\n// without the Shape base-class overhead.\n/**\n * Crane — a stacker / retrieval picker. Picker contract instance.\n *\n * Moves carriers between rack cells (or any Placeable target). The crane\n * is *picker-side* of the Phase G/H Pickable contract — it knows how to\n * `pick(carrier)` / `place(carrier, target)` and `engage(target, kind)`\n * to align its actuators. It is *storage-agnostic* — any Rack / Cell /\n * Spot / Conveyor that implements the receive/dispatch protocol is a\n * valid target.\n *\n * Structure: a tall vertical mast that translates horizontally on a rail\n * or free floor path, with a carriage that slides up/down the mast carrying\n * a shuttle / forks.\n *\n * **Monitoring mode**: crane status is driven by external data binding\n * (`state.status`, `state.carriageHeight`). The carrier is referenced\n * via data binding — it is NOT a child of the crane in monitoring mode.\n *\n * **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, target)`\n * (or `crane.pickAndPlace(carrier, target)`). Mover handles navigation +\n * engage + reparent. During transit the carrier IS a child of the crane.\n */\n@sceneComponent('crane')\nexport default class Crane extends Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) {\n declare state: CraneState\n declare _realObject?: Crane3D\n\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.ceiling - h.floor\n\n /** Yaw offset: crane model is drawn with the rail axis along X (right = forward). */\n static yawOffset = 0\n\n /**\n * Phase H — the crane uses telescoping forks to pick up a carrier in a rack\n * cell. Same mechanism as a forklift fork — engages the pallet's fork pocket.\n * (Mover mixin chain is `: any` so override keyword is not usable, only the\n * getter is defined.)\n */\n get toolType(): string {\n return 'forklift-fork'\n }\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n // ── ContainerCapacity ─────────────────────────────────────────────────────\n\n /** Stacker crane carries at most one load at a time on its forks. */\n get slots(): SlotDef[] {\n return [{ id: 'forks', maxCount: 1 }]\n }\n\n // ── CarrierHolder — attach frame (carriage fork position) ─────────────────\n\n /**\n * Return the 3D attach frame on the crane's carriage (fork tip).\n * Carriers are attached here while the crane is in transit (pick phase).\n *\n * The Crane3D exposes `getCarriageFrame()` — a sub-Object3D that\n * tracks the carriage height and sits at the fork TCP. If the 3D object\n * isn't built yet (e.g. before scene initialization), fall back to the\n * crane's own object3d centre.\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const ro = this._realObject\n const frame = ro?.getCarriageFrame?.()\n if (frame) {\n const carrierDepth = resolveCarrierDepth(carrier)\n // fork blade 위 (top surface) 에 pallet pocket 끼우듯 얹힘. y = blade top + carrier/2.\n // z = blade 중심 (forkLength 의 반쪽 위치 — pallet center 가 blade 중간에 위치).\n const platformTopY = ro?.platformTopY ?? 0\n const bladeMidZ = ro?.bladeMidZ ?? 0\n return {\n attach: frame,\n localPosition: { x: 0, y: platformTopY + carrierDepth / 2, z: bladeMidZ }\n }\n }\n const root = this._realObject?.object3d\n if (!root) return null\n return { attach: root }\n }\n\n // ── Mover overrides ───────────────────────────────────────────────────────\n\n /**\n * Domain-specific actuation between arrival and reparent.\n *\n * Simulation sequence for PICK:\n * 1. Mover.pick() navigates crane to carrier position (moveTo).\n * 2. engage('pick') → snap carriage height + status 'loading'.\n * 3. Carrier is reparented to crane (becomes child).\n *\n * For now: set status and snap carriage height. A full simulation\n * would tween the carriageHeight here (animate Crane3D).\n *\n * Status lifecycle:\n * idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle\n * The 'moving' state is not set from Mover.moveTo() because TypeScript\n * can't call super.moveTo() on an `: any`-typed mixin. External data\n * binding sets 'moving' in monitoring mode; override pick()/place() to\n * set it in full simulation environments.\n */\n async engage(\n target: Component,\n kind: 'pick' | 'place',\n _options: MoveOptions = {}\n ): Promise<void> {\n if (kind === 'pick') {\n this.setState({ status: 'loading' as CraneStatus })\n const carrierY = resolveCarrierCenterY(target)\n if (carrierY !== null) {\n this.setState({ carriageHeight: carrierY })\n }\n } else {\n this.setState({ status: 'unloading' as CraneStatus })\n }\n // In a full simulation: await carriage-motion tween here.\n }\n\n // ── Domain aliases ────────────────────────────────────────────────────────\n\n /** Fetch a carrier from a rack cell (semantically = pick). */\n fetch(carrier: Component, options?: MoveOptions): Promise<void> {\n return this.pick(carrier, options)\n }\n\n /** Deposit a carrier into a rack cell (semantically = place). */\n deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void> {\n return this.place(carrier, cell, options)\n }\n\n // ── 2D rendering ─────────────────────────────────────────────────────────\n\n /**\n * 2D top-down 표현 — 크레인으로 명확히 인식되도록 핵심 부품 그림:\n *\n * - rail (테두리 양쪽 가장자리, dark gray)\n * - twin masts (orange, 옆으로 배치) ← 시각 identity 핵심\n * - carriage (yellow, mast 사이) ← 작은 사각형\n * - forks (yellow, 두 평행선, cross-rail 방향으로 forkLength × extension 비율로 신축)\n * - carrier (lift 됐을 때만, gray 박스 + 들림 표시 ▲)\n *\n * 숨은 축 표현:\n * - carriageHeight (mast Y): 오른쪽 mast 옆 세로 게이지 — fill 비율 = carriageHeight/depth\n * - forkLift (carrier 들기): carrier 가 lift 상태일 때 outline + ▲ 마크\n *\n * 컨벤션: width = rail (X), height = cross-rail (Y, fork 신축 방향).\n * state.rotation 은 canvas 가 처리 (다른 컴포넌트와 동일).\n */\n render(ctx: CanvasRenderingContext2D) {\n const state = this.state as any\n const width = num(state.width, 100)\n const height = num(state.height, 200)\n const left = num(state.left, 0)\n const top = num(state.top, 0)\n\n const depth = num(state.depth, Math.max(width, height) * 4)\n const carriageHeight = clamp(num(state.carriageHeight, 0), 0, depth)\n const forkExtension = num(state.forkExtension, 0) // ± (rail 양쪽 rack)\n const forkLift = num(state.forkLift, 0)\n\n const cx = left + width / 2\n const cy = top + height / 2\n // 적재 여부 — forkLift > 0 (시뮬 중 들어올린 상태) 또는 state.loaded 명시.\n // monitoring 모드는 외부 데이터로 state.loaded 바인딩.\n const carrying = forkLift > 0 || !!state.loaded || !!state.carrying\n\n // 높이 비율 (1F=0 → 최상층=1). carriage / carrier 의 크기 scale 에 공통 사용.\n const heightRatio = depth > 0 ? clamp(carriageHeight / depth, 0, 1) : 0\n const sizeMul = 0.5 + heightRatio * 0.5 // 50% (1F) → 100% (최상층)\n\n const MAST = '#ff7a00' // 주황 — 마스트\n const CARRIAGE_C = '#ffcc00' // 노랑 — carriage shuttle 본체\n const FORK_C = '#a8b0b8' // 은회색 — fork prong (금속 느낌, carriage 와 구분)\n const DARK = '#3a4048'\n // Carrier — 팔레트 갈색 (carriage 노랑 위에서 대비). lift 시 darker + 두꺼운 외곽.\n const CARRIER = forkLift > 0 ? '#7d5530' : '#a08864'\n const CARRIER_LINE = '#3a2a18'\n\n ctx.save()\n\n // 1. Rail — 위/아래 두 줄\n const railW = Math.max(1.5, height * 0.03)\n ctx.fillStyle = DARK\n ctx.fillRect(left, top, width, railW)\n ctx.fillRect(left, top + height - railW, width, railW)\n\n // 2. Twin masts — 두 orange 사각형. mast 길이가 *3D 의 mast height (depth)* 를\n // 의미하므로, 그 위에 carriage 위치 indicator 를 매핑하면 1층/N층 인식 가능.\n const mastW = Math.max(2.5, width * 0.07)\n const mastSpacing = width * 0.75 // 넉넉히 — carrier 가 mast 잠식 안 하도록\n const mastY = top + railW\n const mastH = height - railW * 2\n const mastX1 = cx - mastSpacing / 2 - mastW / 2\n const mastX2 = cx + mastSpacing / 2 - mastW / 2\n ctx.fillStyle = MAST\n ctx.fillRect(mastX1, mastY, mastW, mastH)\n ctx.fillRect(mastX2, mastY, mastW, mastH)\n\n // 2-a. Floor tick marks — mast 위에 N 단계 (예: 5 단) 가로 줄. 층 가늠.\n const numFloors = 5\n ctx.fillStyle = 'rgba(0,0,0,0.25)'\n for (let i = 1; i < numFloors; i++) {\n const tickY = mastY + (mastH * i) / numFloors - 0.5\n ctx.fillRect(mastX1, tickY, mastW, 1)\n ctx.fillRect(mastX2, tickY, mastW, 1)\n }\n\n // 2-b. Carriage 위치 indicator — 마스트 *중심 고정* + *size = carriageHeight 비율*.\n // 1층 (=0): 얇은 선. 최상층 (=depth): 마스트 길이의 100%. 성장/수축 = 상승/하강.\n const indLen = Math.max(2, mastH * heightRatio)\n const indY = mastY + mastH / 2 - indLen / 2\n const indWidth = mastW + 4\n ctx.fillStyle = '#5a3a00' // 어두운 갈색 — orange mast 위에서 강한 대비\n ctx.fillRect(mastX1 - 2, indY, indWidth, indLen)\n ctx.fillRect(mastX2 - 2, indY, indWidth, indLen)\n\n // 3. Carriage (= 적재 deck) — 팔레트 1200×800mm aspect 반영. *폭만* sizeMul 적용 (높이감),\n // 길이 (Y) 고정. carrier / fork 모두 이 carriage 기준으로 derive.\n const carriageW = (mastSpacing - mastW) * sizeMul\n const carriageH = Math.min((mastSpacing - mastW) * 1.3, (height - railW * 2) * 0.55)\n const carriageX = cx - carriageW / 2\n const carriageY = cy - carriageH / 2\n ctx.fillStyle = CARRIAGE_C\n ctx.fillRect(carriageX, carriageY, carriageW, carriageH)\n\n // 4. Fork — 좌우 균형:\n // - 양쪽 모두 *작은 stub* 항상 표시 (crane 이 양쪽 rack 사이 있는 인상)\n // - extension 부호 (+/-) 에 따라 active 쪽이 추가로 신축\n // - 신축 길이 = |forkExtension| (실제 reach 만큼)\n const stubLen = Math.max(2, carriageH * 0.5)\n // 포크 폭 — carriage 의 80% (carrier 90% 보다 약간 좁아 carrier 가 fork 양옆으로\n // 살짝 overhang). carriage 가 sizeMul 적용된 폭이라 fork 도 자동 scale 반영.\n const forkW = carriageW * 0.8\n const forkX = cx - forkW / 2\n const extLen = Math.abs(forkExtension)\n const sign = forkExtension >= 0 ? 1 : -1\n ctx.fillStyle = FORK_C\n // 항상 양쪽 stub\n ctx.fillRect(forkX, carriageY - stubLen, forkW, stubLen) // top stub\n ctx.fillRect(forkX, carriageY + carriageH, forkW, stubLen) // bottom stub\n // active 쪽 추가 신축\n if (extLen > 0.5) {\n if (sign > 0) {\n ctx.fillRect(forkX, carriageY + carriageH + stubLen, forkW, extLen)\n } else {\n ctx.fillRect(forkX, carriageY - stubLen - extLen, forkW, extLen)\n }\n }\n\n // 5. Carrier — *팔레트 크기* 정사각형. 신축 시 fork 끝 (cell 안쪽) 에 배치.\n // - extLen > 0: carrier 가 fork 끝 (cell 안) — pallet 이 cell 로 들어가는 인상\n // - retracted (extLen=0): carriage 중심 — transit 자세\n // - forkLift > 0: 진한 갈색 + 두꺼운 outline (들린 상태 시인)\n if (carrying) {\n // 팔레트 — 항상 carriage 의 90% (폭/길이 모두). carriage 가 sizeMul 적용된 폭이라\n // carrier 도 자동으로 sizeMul 반영. 길이는 carriage 길이 고정에 따라 고정.\n const carrW = carriageW * 0.9\n const carrH = carriageH * 0.9\n let carrCenterY: number\n if (extLen > 0.5) {\n // 포크의 *중간* 에 carrier 배치 — fork prong 이 pallet pocket 에 끼워진 자연스러운\n // 위치 (현실 AS/RS 에서 pallet 이 fork 끝이 아닌 fork 길이 중간 부근에 안착).\n const forkMid = (stubLen + extLen) / 2\n carrCenterY = sign > 0\n ? carriageY + carriageH + forkMid\n : carriageY - forkMid\n } else {\n carrCenterY = cy // transit\n }\n const carrX = cx - carrW / 2\n const carrY = carrCenterY - carrH / 2\n\n // 들린 상태 — drop shadow 로 *떠 있는* 인상 (forkLift > 0). offset 비율 = lift 강도.\n if (forkLift > 0) {\n const forkLengthRef = num(state.forkLength, height * 0.4)\n const liftMax = Math.max(forkLengthRef * 0.05, 20) // forkLift 정상 범위\n const liftRatio = clamp(forkLift / liftMax, 0.3, 1) // 최소 30% 표시 (있다는 것만 보이게)\n const shadowOff = Math.max(2, Math.min(carrW, carrH) * 0.14) * liftRatio\n ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'\n ctx.fillRect(carrX + shadowOff, carrY + shadowOff, carrW, carrH)\n }\n\n // Carrier 본체\n ctx.fillStyle = CARRIER\n ctx.fillRect(carrX, carrY, carrW, carrH)\n ctx.strokeStyle = CARRIER_LINE\n ctx.lineWidth = forkLift > 0 ? 1.5 : 0.8\n ctx.strokeRect(carrX, carrY, carrW, carrH)\n }\n\n // Cargo (carrier) 가 fork 위에 얹혀 fork 신축과 함께 이동하므로 그 자체로 방향\n // (loading/unloading) 이 자연 인식. 별도 status 배지 없음.\n\n // (carriageHeight 시각화는 mast 위 dark band + floor ticks 로 통합 — 별도 측면 게이지 제거)\n\n ctx.restore()\n }\n\n // ── Lifecycle — simulate() 자동 시작 ────────────────────────────────────\n\n added(parent: any): void {\n super.added?.(parent)\n if (this._simStarted) return\n this._simStarted = true\n // 초기 지연 800~2800ms 사이 random — 여러 crane 이 동기적으로 시작하지 않도록.\n // added 에서 시작 → 2D/3D 모드 무관. buildRealObject 는 3D 진입 시만 호출돼 시점이\n // 늦거나 누락될 수 있으므로 부적합.\n const initialDelay = 800 + Math.random() * 2000\n setTimeout(() => {\n this.simulate().catch(e => console.error('[Crane] simulate', e))\n }, initialDelay)\n }\n\n // ── 3D ───────────────────────────────────────────────────────────────────\n\n buildRealObject(): RealObject | undefined {\n return new Crane3D(this)\n }\n\n // ── Random animation (visual smoke test) ─────────────────────────────────\n // things-scene 의 component.animate() (frameClock 기반, pause/speed 대응) 를 사용.\n\n private _simStarted = false\n private _simRunning = false\n private _simAbort = false\n // Rail = crane 의 *로컬 X 축* (state.rotation 적용). simulate 진입 시 1회 계산해서 캐시.\n private _railOriginX = 0\n private _railOriginY = 0\n private _railCos = 1\n private _railSin = 0\n private _railMin = NaN // local-X 축 rail offset min\n private _railMax = NaN // local-X 축 rail offset max\n private _targetSide = 1 // +Z (fork +Z 로 뻗어야 하나) / -Z\n\n /** Continuous random pick → transport → place cycles. Visual smoke test. */\n async simulate(): Promise<void> {\n if (this._simRunning) return\n this._simRunning = true\n this._simAbort = false\n try {\n this._initRailRange()\n while (!this._simAbort) {\n await this._oneCycle()\n }\n } finally {\n this._simRunning = false\n }\n }\n\n /**\n * state.target bbox 를 crane 의 *로컬 X 축* 으로 projection 해서 rail range 1회 계산.\n * Rotation 적용된 crane 의 local X (= rail) 방향으로 움직이도록 cos/sin 캐시.\n */\n private _initRailRange(): void {\n this._railMin = this._railMax = NaN\n\n // 1순위: state.target refid 로 명시. 2순위: crane 의 parent 컴포넌트 (자연스러운\n // \"이 컨테이너 안에서만 움직여\" 기본값).\n const targetRefId = (this.state as any).target as string | undefined\n const root = (this as any).root\n let target = targetRefId ? root?.findById?.(targetRefId) : null\n if (!target) {\n const parent = (this as any).parent\n // parent 가 root/model-layer 같은 top-level 이면 bbox 가 의미없음 → skip\n if (parent && parent !== root && (parent.state as any)?.width > 0) {\n target = parent\n }\n }\n if (!target) return\n\n const tState = target.state ?? {}\n const tl = numOr(tState.left, 0)\n const tt = numOr(tState.top, 0)\n const tw = numOr(tState.width, 0)\n const th = numOr(tState.height, 0)\n if (tw <= 0 || th <= 0) return\n\n const W = numOr((this.state as any).width, 100)\n const H = numOr((this.state as any).height, 100)\n const rotation = numOr((this.state as any).rotation, 0)\n const cos = Math.cos(rotation)\n const sin = Math.sin(rotation)\n\n // Rail origin = crane 의 현재 canvas center. 이 점에서 local X 방향으로 ±rail offset.\n this._railOriginX = numOr((this.state as any).left, 0) + W / 2\n this._railOriginY = numOr((this.state as any).top, 0) + H / 2\n this._railCos = cos\n this._railSin = sin\n\n // Target bbox 4 corner 를 crane local frame 으로 projection.\n // local X (rail) = dx*cos + dy*sin\n // local Z (aisle) = -dx*sin + dy*cos\n const corners = [\n { x: tl, y: tt },\n { x: tl + tw, y: tt },\n { x: tl, y: tt + th },\n { x: tl + tw, y: tt + th }\n ]\n let minRail = Infinity\n let maxRail = -Infinity\n let aisleSum = 0\n for (const c of corners) {\n const dx = c.x - this._railOriginX\n const dy = c.y - this._railOriginY\n const rail = dx * cos + dy * sin\n const aisle = -dx * sin + dy * cos\n if (rail < minRail) minRail = rail\n if (rail > maxRail) maxRail = rail\n aisleSum += aisle\n }\n // Crane 자체 W 만큼 안쪽으로 좁혀 — crane center 가 [minRail+W/2, maxRail-W/2] 안에\n // 있으면 crane 전체가 target bbox 안 (local X 방향).\n this._railMin = minRail + W / 2\n this._railMax = Math.max(this._railMin, maxRail - W / 2)\n this._targetSide = aisleSum >= 0 ? +1 : -1\n }\n\n stopSimulate(): void {\n this._simAbort = true\n // 진행 중인 animate() controller 도 즉시 정지 — onComplete 가 호출되어 _tween Promise\n // 가 resolve → simulate() while 루프가 다음 반복에서 _simAbort 보고 종료. 이걸로\n // 루프 stack 의 strong ref 해소, frameClock subscription 해제.\n const ctrl = (this as any)._animate\n if (ctrl && typeof ctrl.stop === 'function') {\n try { ctrl.stop() } catch {}\n }\n }\n\n /**\n * 컴포넌트 dispose 시 (보드 종료, scene reload 등) 진행 중 simulate 루프와 animate\n * subscription 을 정리. 미정리 시 루프의 await stack 이 component 를 strong reference\n * 로 잡아 GC 안 됨 + frameClock 콜백 살아남음.\n */\n dispose(): void {\n this.stopSimulate()\n super.dispose?.()\n }\n\n private async _oneCycle(): Promise<void> {\n const W = numOr((this.state as any).width, 100)\n const H = numOr((this.state as any).height, 100)\n const D = numOr((this.state as any).depth, W * 4)\n const forkLen = numOr((this.state as any).forkLength, H * 0.6)\n\n if (!Number.isFinite(this._railMin) || !Number.isFinite(this._railMax)) {\n await new Promise(r => setTimeout(r, 800))\n return\n }\n\n const pickRail = () => this._railMin + Math.random() * Math.max(0, this._railMax - this._railMin)\n const toLeftTop = (rail: number) => ({\n left: this._railOriginX + rail * this._railCos - W / 2,\n top: this._railOriginY + rail * this._railSin - H / 2\n })\n\n const source = toLeftTop(pickRail())\n const dest = toLeftTop(pickRail())\n\n const sourceCH = Math.random() * D * 0.75\n const destCH = Math.random() * D * 0.75\n const liftH = Math.max(20, D * 0.02)\n // 양쪽 rack 모두 서비스 — source/dest 각각 ±Z random (cycle 별 다름).\n const sideA = Math.random() < 0.5 ? -1 : +1\n const sideB = Math.random() < 0.5 ? -1 : +1\n\n // Tween duration 은 base 의 70~130% 사이 random — 여러 crane 이 같은 타이밍으로 안 보이도록.\n const jitter = (base: number): number => base * (0.7 + Math.random() * 0.6)\n\n // 이동: crane local X 방향 → canvas (left, top) 둘 다 동시 변경 (rotation 적용 시 diagonal)\n await this._tween({ status: 'moving', left: source.left, top: source.top, carriageHeight: sourceCH }, jitter(1500))\n await this._tween({ status: 'loading', forkExtension: sideA * forkLen }, jitter(700))\n await this._tween({ forkLift: liftH }, jitter(400))\n await this._tween({ forkExtension: 0 }, jitter(700))\n await this._tween({ status: 'moving', left: dest.left, top: dest.top, carriageHeight: destCH }, jitter(1500))\n await this._tween({ status: 'unloading', forkExtension: sideB * forkLen }, jitter(700))\n await this._tween({ forkLift: 0 }, jitter(400))\n await this._tween({ status: 'idle', forkExtension: 0 }, jitter(700))\n\n // 사이클 사이 짧은 idle (200~1000ms) — 자연스러운 phase 분산\n await new Promise(r => setTimeout(r, 200 + Math.random() * 800))\n }\n\n /**\n * things-scene 의 `component.animate()` 로 한 구간 tween. step 콜백에서 setState 호출.\n * frameClock.simTime 기반이라 scene pause / speed change / suppressAnimations 모두 자동 대응.\n */\n private _tween(targets: Record<string, any>, duration: number): Promise<void> {\n const start: Record<string, number> = {}\n for (const k of Object.keys(targets)) {\n if (k === 'status') continue\n const v = (this.state as any)[k]\n start[k] = typeof v === 'number' && Number.isFinite(v) ? v : 0\n }\n if (typeof targets.status === 'string') {\n this.setState({ status: targets.status })\n }\n return new Promise<void>(resolve => {\n let resolved = false\n const finish = () => {\n if (resolved) return\n resolved = true\n resolve()\n }\n const controller = (this as any).animate({\n duration,\n ease: 'inout',\n delta: 'quad',\n step: (dx: number) => {\n if (this._simAbort) {\n controller.stop?.()\n return finish()\n }\n const update: Record<string, number> = {}\n for (const k of Object.keys(start)) {\n const tgt = targets[k]\n if (typeof tgt !== 'number') continue\n update[k] = start[k] + (tgt - start[k]) * dx\n }\n this.setState(update as any)\n },\n onComplete: finish\n })\n controller.start()\n })\n }\n}\n\nfunction resolveCarrierDepth(c: Component): number {\n const eff = (c as any)._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n return numOr((c as any)?.state?.depth, 0)\n}\n\nfunction resolveCarrierCenterY(c: Component): number | null {\n const pos = (c as any).state\n if (!pos) return null\n // zPos is the 3D Y center of a Placeable component in things-scene\n const zPos = numOr(pos.zPos, NaN)\n if (!Number.isNaN(zPos)) return zPos\n return null\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n\n// ── 2D render helpers ────────────────────────────────────────────────────\nfunction num(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n\nfunction clamp(v: number, min: number, max: number): number {\n return v < min ? min : v > max ? max : v\n}\n\n/** \"#rrggbb\" 또는 \"name\" 색에 alpha 적용. 실패 시 fallback rgba. */\nfunction withAlpha(color: string, alpha: number): string {\n // \"#rrggbb\" 형식만 지원 — name color 는 rgba 직접 변환 못해서 그대로 사용\n if (color && color[0] === '#' && color.length === 7) {\n const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 16)\n return `rgba(${r},${g},${b},${alpha})`\n }\n return color\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.
|
|
5
|
+
"version": "10.0.0-beta.36",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"module": "dist/index.js",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"typescript": "^5.0.4"
|
|
46
46
|
},
|
|
47
47
|
"prettier": "@hatiolab/prettier-config",
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "2e880bda0f408d2d17910721c4d140a12cce8253"
|
|
49
49
|
}
|
package/src/crane-3d.ts
CHANGED
|
@@ -165,8 +165,11 @@ export class Crane3D extends RealObjectGroup {
|
|
|
165
165
|
this.object3d.add(mesh)
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
// ── Carriage (
|
|
169
|
-
|
|
168
|
+
// ── Carriage + Fork 어셈블리 (forkLift 시 함께 이동) ───────────────
|
|
169
|
+
// 시각 단절 방지를 위해 carriage 와 fork 를 *함께* forkLift 만큼 올림.
|
|
170
|
+
// 의미상으로는 carriageHeight 가 mast 위 carriage 위치, forkLift 가 *그 어셈블리의*
|
|
171
|
+
// 추가 미세 Y. 둘 다 적용된 위치를 carriage / fork 둘 다 공유.
|
|
172
|
+
const carriageY = baseTrolleyY + baseH / 2 + carriageHeight + forkLift + carriageH / 2
|
|
170
173
|
{
|
|
171
174
|
const geo = new THREE.BoxGeometry(carriageW, carriageH, carriageZ)
|
|
172
175
|
const mesh = new THREE.Mesh(geo, carriageMat)
|
|
@@ -176,31 +179,64 @@ export class Crane3D extends RealObjectGroup {
|
|
|
176
179
|
this.object3d.add(mesh)
|
|
177
180
|
}
|
|
178
181
|
|
|
179
|
-
// ── Two-prong forks (
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
const
|
|
182
|
+
// ── Two-prong forks (양옆 stub + active 신축, 2D 와 동일 모델) ─────
|
|
183
|
+
// ext=0: 양 ±Z 면에 작은 stub. carriage 안에 들어있는 인상 (튀어나옴 없음)
|
|
184
|
+
// ext=±forkLen: active 쪽 stub + |ext| 길이로 신장. 반대쪽 stub 유지.
|
|
185
|
+
// 회전 flip 없음 → ext 가 0 을 지날 때 시각 점프 없음.
|
|
186
|
+
const forkY = carriageY // carriage 중심 Y (embed)
|
|
187
|
+
const stubL = Math.min(carriageZ * 0.2, Math.max(bladeL * 0.05, 6))
|
|
188
|
+
const absExt = Math.abs(forkExtension)
|
|
189
|
+
const sign = forkExtension >= 0 ? 1 : -1
|
|
185
190
|
{
|
|
186
191
|
const group = new THREE.Group()
|
|
187
|
-
group.position.set(0, forkY,
|
|
188
|
-
// forkSign 으로 blade tip 방향 설정
|
|
189
|
-
group.rotation.y = forkSign < 0 ? Math.PI : 0
|
|
192
|
+
group.position.set(0, forkY, 0)
|
|
190
193
|
|
|
194
|
+
// 양옆 stub — 두 prong × 두 측면 = 4 box
|
|
195
|
+
const stubGeo = new THREE.BoxGeometry(bladeW, bladeH, stubL)
|
|
191
196
|
for (const xOff of [-bladeSpacing / 2, +bladeSpacing / 2]) {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
for (const zSide of [-1, +1]) {
|
|
198
|
+
const mesh = new THREE.Mesh(stubGeo, forkMat)
|
|
199
|
+
mesh.position.set(xOff, 0, zSide * (carriageZ / 2 + stubL / 2))
|
|
200
|
+
mesh.castShadow = true
|
|
201
|
+
mesh.receiveShadow = true
|
|
202
|
+
group.add(mesh)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Active side 신장
|
|
207
|
+
if (absExt > 0.5) {
|
|
208
|
+
const extGeo = new THREE.BoxGeometry(bladeW, bladeH, absExt)
|
|
209
|
+
for (const xOff of [-bladeSpacing / 2, +bladeSpacing / 2]) {
|
|
210
|
+
const mesh = new THREE.Mesh(extGeo, forkMat)
|
|
211
|
+
mesh.position.set(xOff, 0, sign * (carriageZ / 2 + stubL + absExt / 2))
|
|
212
|
+
mesh.castShadow = true
|
|
213
|
+
mesh.receiveShadow = true
|
|
214
|
+
group.add(mesh)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Pallet — 정지 시 carriage 중심 Z, 신축 시 fork 중간으로 이동.
|
|
219
|
+
const loaded = forkLift > 0 || !!(this.component.state as any).loaded || !!(this.component.state as any).carrying
|
|
220
|
+
if (loaded) {
|
|
221
|
+
const palletMat = new THREE.MeshStandardMaterial({
|
|
222
|
+
color: 0xa08864, metalness: 0.1, roughness: 0.85
|
|
223
|
+
})
|
|
224
|
+
const palletW = bladeSpacing * 1.2
|
|
225
|
+
const palletH = Math.max(bladeH * 2.5, carriageH * 0.5)
|
|
226
|
+
const palletL = Math.max(bladeL * 0.3, carriageZ * 0.6)
|
|
227
|
+
const geo = new THREE.BoxGeometry(palletW, palletH, palletL)
|
|
228
|
+
const pallet = new THREE.Mesh(geo, palletMat)
|
|
229
|
+
const palletZ = absExt < 0.5 ? 0 : sign * (carriageZ / 2 + absExt / 2)
|
|
230
|
+
pallet.position.set(0, carriageH / 2 + palletH / 2, palletZ)
|
|
231
|
+
pallet.castShadow = true
|
|
232
|
+
pallet.receiveShadow = true
|
|
233
|
+
group.add(pallet)
|
|
198
234
|
}
|
|
199
235
|
|
|
200
236
|
this.object3d.add(group)
|
|
201
237
|
this._forkGroup = group
|
|
202
|
-
this._forkTopY =
|
|
203
|
-
this._bladeMidZ = 0
|
|
238
|
+
this._forkTopY = carriageH / 2
|
|
239
|
+
this._bladeMidZ = absExt < 0.5 ? 0 : sign * (carriageZ / 2 + absExt / 2)
|
|
204
240
|
}
|
|
205
241
|
|
|
206
242
|
// ── Top frame (connects mast tops) ────────────────────────────────
|
package/src/crane.ts
CHANGED
|
@@ -301,31 +301,189 @@ export default class Crane extends Mover(CarrierHolder(ContainerCapacity(Legenda
|
|
|
301
301
|
// ── 2D rendering ─────────────────────────────────────────────────────────
|
|
302
302
|
|
|
303
303
|
/**
|
|
304
|
-
* 2D
|
|
305
|
-
*
|
|
304
|
+
* 2D top-down 표현 — 크레인으로 명확히 인식되도록 핵심 부품 그림:
|
|
305
|
+
*
|
|
306
|
+
* - rail (테두리 양쪽 가장자리, dark gray)
|
|
307
|
+
* - twin masts (orange, 옆으로 배치) ← 시각 identity 핵심
|
|
308
|
+
* - carriage (yellow, mast 사이) ← 작은 사각형
|
|
309
|
+
* - forks (yellow, 두 평행선, cross-rail 방향으로 forkLength × extension 비율로 신축)
|
|
310
|
+
* - carrier (lift 됐을 때만, gray 박스 + 들림 표시 ▲)
|
|
311
|
+
*
|
|
312
|
+
* 숨은 축 표현:
|
|
313
|
+
* - carriageHeight (mast Y): 오른쪽 mast 옆 세로 게이지 — fill 비율 = carriageHeight/depth
|
|
314
|
+
* - forkLift (carrier 들기): carrier 가 lift 상태일 때 outline + ▲ 마크
|
|
315
|
+
*
|
|
316
|
+
* 컨벤션: width = rail (X), height = cross-rail (Y, fork 신축 방향).
|
|
317
|
+
* state.rotation 은 canvas 가 처리 (다른 컴포넌트와 동일).
|
|
306
318
|
*/
|
|
307
319
|
render(ctx: CanvasRenderingContext2D) {
|
|
308
|
-
const
|
|
309
|
-
const
|
|
320
|
+
const state = this.state as any
|
|
321
|
+
const width = num(state.width, 100)
|
|
322
|
+
const height = num(state.height, 200)
|
|
323
|
+
const left = num(state.left, 0)
|
|
324
|
+
const top = num(state.top, 0)
|
|
325
|
+
|
|
326
|
+
const depth = num(state.depth, Math.max(width, height) * 4)
|
|
327
|
+
const carriageHeight = clamp(num(state.carriageHeight, 0), 0, depth)
|
|
328
|
+
const forkExtension = num(state.forkExtension, 0) // ± (rail 양쪽 rack)
|
|
329
|
+
const forkLift = num(state.forkLift, 0)
|
|
330
|
+
|
|
331
|
+
const cx = left + width / 2
|
|
332
|
+
const cy = top + height / 2
|
|
333
|
+
// 적재 여부 — forkLift > 0 (시뮬 중 들어올린 상태) 또는 state.loaded 명시.
|
|
334
|
+
// monitoring 모드는 외부 데이터로 state.loaded 바인딩.
|
|
335
|
+
const carrying = forkLift > 0 || !!state.loaded || !!state.carrying
|
|
336
|
+
|
|
337
|
+
// 높이 비율 (1F=0 → 최상층=1). carriage / carrier 의 크기 scale 에 공통 사용.
|
|
338
|
+
const heightRatio = depth > 0 ? clamp(carriageHeight / depth, 0, 1) : 0
|
|
339
|
+
const sizeMul = 0.5 + heightRatio * 0.5 // 50% (1F) → 100% (최상층)
|
|
340
|
+
|
|
341
|
+
const MAST = '#ff7a00' // 주황 — 마스트
|
|
342
|
+
const CARRIAGE_C = '#ffcc00' // 노랑 — carriage shuttle 본체
|
|
343
|
+
const FORK_C = '#a8b0b8' // 은회색 — fork prong (금속 느낌, carriage 와 구분)
|
|
344
|
+
const DARK = '#3a4048'
|
|
345
|
+
// Carrier — 팔레트 갈색 (carriage 노랑 위에서 대비). lift 시 darker + 두꺼운 외곽.
|
|
346
|
+
const CARRIER = forkLift > 0 ? '#7d5530' : '#a08864'
|
|
347
|
+
const CARRIER_LINE = '#3a2a18'
|
|
348
|
+
|
|
310
349
|
ctx.save()
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
ctx.
|
|
350
|
+
|
|
351
|
+
// 1. Rail — 위/아래 두 줄
|
|
352
|
+
const railW = Math.max(1.5, height * 0.03)
|
|
353
|
+
ctx.fillStyle = DARK
|
|
354
|
+
ctx.fillRect(left, top, width, railW)
|
|
355
|
+
ctx.fillRect(left, top + height - railW, width, railW)
|
|
356
|
+
|
|
357
|
+
// 2. Twin masts — 두 orange 사각형. mast 길이가 *3D 의 mast height (depth)* 를
|
|
358
|
+
// 의미하므로, 그 위에 carriage 위치 indicator 를 매핑하면 1층/N층 인식 가능.
|
|
359
|
+
const mastW = Math.max(2.5, width * 0.07)
|
|
360
|
+
const mastSpacing = width * 0.75 // 넉넉히 — carrier 가 mast 잠식 안 하도록
|
|
361
|
+
const mastY = top + railW
|
|
362
|
+
const mastH = height - railW * 2
|
|
363
|
+
const mastX1 = cx - mastSpacing / 2 - mastW / 2
|
|
364
|
+
const mastX2 = cx + mastSpacing / 2 - mastW / 2
|
|
365
|
+
ctx.fillStyle = MAST
|
|
366
|
+
ctx.fillRect(mastX1, mastY, mastW, mastH)
|
|
367
|
+
ctx.fillRect(mastX2, mastY, mastW, mastH)
|
|
368
|
+
|
|
369
|
+
// 2-a. Floor tick marks — mast 위에 N 단계 (예: 5 단) 가로 줄. 층 가늠.
|
|
370
|
+
const numFloors = 5
|
|
371
|
+
ctx.fillStyle = 'rgba(0,0,0,0.25)'
|
|
372
|
+
for (let i = 1; i < numFloors; i++) {
|
|
373
|
+
const tickY = mastY + (mastH * i) / numFloors - 0.5
|
|
374
|
+
ctx.fillRect(mastX1, tickY, mastW, 1)
|
|
375
|
+
ctx.fillRect(mastX2, tickY, mastW, 1)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 2-b. Carriage 위치 indicator — 마스트 *중심 고정* + *size = carriageHeight 비율*.
|
|
379
|
+
// 1층 (=0): 얇은 선. 최상층 (=depth): 마스트 길이의 100%. 성장/수축 = 상승/하강.
|
|
380
|
+
const indLen = Math.max(2, mastH * heightRatio)
|
|
381
|
+
const indY = mastY + mastH / 2 - indLen / 2
|
|
382
|
+
const indWidth = mastW + 4
|
|
383
|
+
ctx.fillStyle = '#5a3a00' // 어두운 갈색 — orange mast 위에서 강한 대비
|
|
384
|
+
ctx.fillRect(mastX1 - 2, indY, indWidth, indLen)
|
|
385
|
+
ctx.fillRect(mastX2 - 2, indY, indWidth, indLen)
|
|
386
|
+
|
|
387
|
+
// 3. Carriage (= 적재 deck) — 팔레트 1200×800mm aspect 반영. *폭만* sizeMul 적용 (높이감),
|
|
388
|
+
// 길이 (Y) 고정. carrier / fork 모두 이 carriage 기준으로 derive.
|
|
389
|
+
const carriageW = (mastSpacing - mastW) * sizeMul
|
|
390
|
+
const carriageH = Math.min((mastSpacing - mastW) * 1.3, (height - railW * 2) * 0.55)
|
|
391
|
+
const carriageX = cx - carriageW / 2
|
|
392
|
+
const carriageY = cy - carriageH / 2
|
|
393
|
+
ctx.fillStyle = CARRIAGE_C
|
|
394
|
+
ctx.fillRect(carriageX, carriageY, carriageW, carriageH)
|
|
395
|
+
|
|
396
|
+
// 4. Fork — 좌우 균형:
|
|
397
|
+
// - 양쪽 모두 *작은 stub* 항상 표시 (crane 이 양쪽 rack 사이 있는 인상)
|
|
398
|
+
// - extension 부호 (+/-) 에 따라 active 쪽이 추가로 신축
|
|
399
|
+
// - 신축 길이 = |forkExtension| (실제 reach 만큼)
|
|
400
|
+
const stubLen = Math.max(2, carriageH * 0.5)
|
|
401
|
+
// 포크 폭 — carriage 의 80% (carrier 90% 보다 약간 좁아 carrier 가 fork 양옆으로
|
|
402
|
+
// 살짝 overhang). carriage 가 sizeMul 적용된 폭이라 fork 도 자동 scale 반영.
|
|
403
|
+
const forkW = carriageW * 0.8
|
|
404
|
+
const forkX = cx - forkW / 2
|
|
405
|
+
const extLen = Math.abs(forkExtension)
|
|
406
|
+
const sign = forkExtension >= 0 ? 1 : -1
|
|
407
|
+
ctx.fillStyle = FORK_C
|
|
408
|
+
// 항상 양쪽 stub
|
|
409
|
+
ctx.fillRect(forkX, carriageY - stubLen, forkW, stubLen) // top stub
|
|
410
|
+
ctx.fillRect(forkX, carriageY + carriageH, forkW, stubLen) // bottom stub
|
|
411
|
+
// active 쪽 추가 신축
|
|
412
|
+
if (extLen > 0.5) {
|
|
413
|
+
if (sign > 0) {
|
|
414
|
+
ctx.fillRect(forkX, carriageY + carriageH + stubLen, forkW, extLen)
|
|
415
|
+
} else {
|
|
416
|
+
ctx.fillRect(forkX, carriageY - stubLen - extLen, forkW, extLen)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// 5. Carrier — *팔레트 크기* 정사각형. 신축 시 fork 끝 (cell 안쪽) 에 배치.
|
|
421
|
+
// - extLen > 0: carrier 가 fork 끝 (cell 안) — pallet 이 cell 로 들어가는 인상
|
|
422
|
+
// - retracted (extLen=0): carriage 중심 — transit 자세
|
|
423
|
+
// - forkLift > 0: 진한 갈색 + 두꺼운 outline (들린 상태 시인)
|
|
424
|
+
if (carrying) {
|
|
425
|
+
// 팔레트 — 항상 carriage 의 90% (폭/길이 모두). carriage 가 sizeMul 적용된 폭이라
|
|
426
|
+
// carrier 도 자동으로 sizeMul 반영. 길이는 carriage 길이 고정에 따라 고정.
|
|
427
|
+
const carrW = carriageW * 0.9
|
|
428
|
+
const carrH = carriageH * 0.9
|
|
429
|
+
let carrCenterY: number
|
|
430
|
+
if (extLen > 0.5) {
|
|
431
|
+
// 포크의 *중간* 에 carrier 배치 — fork prong 이 pallet pocket 에 끼워진 자연스러운
|
|
432
|
+
// 위치 (현실 AS/RS 에서 pallet 이 fork 끝이 아닌 fork 길이 중간 부근에 안착).
|
|
433
|
+
const forkMid = (stubLen + extLen) / 2
|
|
434
|
+
carrCenterY = sign > 0
|
|
435
|
+
? carriageY + carriageH + forkMid
|
|
436
|
+
: carriageY - forkMid
|
|
437
|
+
} else {
|
|
438
|
+
carrCenterY = cy // transit
|
|
439
|
+
}
|
|
440
|
+
const carrX = cx - carrW / 2
|
|
441
|
+
const carrY = carrCenterY - carrH / 2
|
|
442
|
+
|
|
443
|
+
// 들린 상태 — drop shadow 로 *떠 있는* 인상 (forkLift > 0). offset 비율 = lift 강도.
|
|
444
|
+
if (forkLift > 0) {
|
|
445
|
+
const forkLengthRef = num(state.forkLength, height * 0.4)
|
|
446
|
+
const liftMax = Math.max(forkLengthRef * 0.05, 20) // forkLift 정상 범위
|
|
447
|
+
const liftRatio = clamp(forkLift / liftMax, 0.3, 1) // 최소 30% 표시 (있다는 것만 보이게)
|
|
448
|
+
const shadowOff = Math.max(2, Math.min(carrW, carrH) * 0.14) * liftRatio
|
|
449
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'
|
|
450
|
+
ctx.fillRect(carrX + shadowOff, carrY + shadowOff, carrW, carrH)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Carrier 본체
|
|
454
|
+
ctx.fillStyle = CARRIER
|
|
455
|
+
ctx.fillRect(carrX, carrY, carrW, carrH)
|
|
456
|
+
ctx.strokeStyle = CARRIER_LINE
|
|
457
|
+
ctx.lineWidth = forkLift > 0 ? 1.5 : 0.8
|
|
458
|
+
ctx.strokeRect(carrX, carrY, carrW, carrH)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Cargo (carrier) 가 fork 위에 얹혀 fork 신축과 함께 이동하므로 그 자체로 방향
|
|
462
|
+
// (loading/unloading) 이 자연 인식. 별도 status 배지 없음.
|
|
463
|
+
|
|
464
|
+
// (carriageHeight 시각화는 mast 위 dark band + floor ticks 로 통합 — 별도 측면 게이지 제거)
|
|
465
|
+
|
|
315
466
|
ctx.restore()
|
|
316
467
|
}
|
|
317
468
|
|
|
469
|
+
// ── Lifecycle — simulate() 자동 시작 ────────────────────────────────────
|
|
470
|
+
|
|
471
|
+
added(parent: any): void {
|
|
472
|
+
super.added?.(parent)
|
|
473
|
+
if (this._simStarted) return
|
|
474
|
+
this._simStarted = true
|
|
475
|
+
// 초기 지연 800~2800ms 사이 random — 여러 crane 이 동기적으로 시작하지 않도록.
|
|
476
|
+
// added 에서 시작 → 2D/3D 모드 무관. buildRealObject 는 3D 진입 시만 호출돼 시점이
|
|
477
|
+
// 늦거나 누락될 수 있으므로 부적합.
|
|
478
|
+
const initialDelay = 800 + Math.random() * 2000
|
|
479
|
+
setTimeout(() => {
|
|
480
|
+
this.simulate().catch(e => console.error('[Crane] simulate', e))
|
|
481
|
+
}, initialDelay)
|
|
482
|
+
}
|
|
483
|
+
|
|
318
484
|
// ── 3D ───────────────────────────────────────────────────────────────────
|
|
319
485
|
|
|
320
486
|
buildRealObject(): RealObject | undefined {
|
|
321
|
-
if (!this._simStarted) {
|
|
322
|
-
this._simStarted = true
|
|
323
|
-
// 초기 지연 800~2800ms 사이 random — 여러 crane 이 동기적으로 시작하지 않도록.
|
|
324
|
-
const initialDelay = 800 + Math.random() * 2000
|
|
325
|
-
setTimeout(() => {
|
|
326
|
-
this.simulate().catch(e => console.error('[Crane] simulate', e))
|
|
327
|
-
}, initialDelay)
|
|
328
|
-
}
|
|
329
487
|
return new Crane3D(this)
|
|
330
488
|
}
|
|
331
489
|
|
|
@@ -471,18 +629,20 @@ export default class Crane extends Mover(CarrierHolder(ContainerCapacity(Legenda
|
|
|
471
629
|
const sourceCH = Math.random() * D * 0.75
|
|
472
630
|
const destCH = Math.random() * D * 0.75
|
|
473
631
|
const liftH = Math.max(20, D * 0.02)
|
|
474
|
-
|
|
632
|
+
// 양쪽 rack 모두 서비스 — source/dest 각각 ±Z random (cycle 별 다름).
|
|
633
|
+
const sideA = Math.random() < 0.5 ? -1 : +1
|
|
634
|
+
const sideB = Math.random() < 0.5 ? -1 : +1
|
|
475
635
|
|
|
476
636
|
// Tween duration 은 base 의 70~130% 사이 random — 여러 crane 이 같은 타이밍으로 안 보이도록.
|
|
477
637
|
const jitter = (base: number): number => base * (0.7 + Math.random() * 0.6)
|
|
478
638
|
|
|
479
639
|
// 이동: crane local X 방향 → canvas (left, top) 둘 다 동시 변경 (rotation 적용 시 diagonal)
|
|
480
640
|
await this._tween({ status: 'moving', left: source.left, top: source.top, carriageHeight: sourceCH }, jitter(1500))
|
|
481
|
-
await this._tween({ status: 'loading', forkExtension:
|
|
641
|
+
await this._tween({ status: 'loading', forkExtension: sideA * forkLen }, jitter(700))
|
|
482
642
|
await this._tween({ forkLift: liftH }, jitter(400))
|
|
483
643
|
await this._tween({ forkExtension: 0 }, jitter(700))
|
|
484
644
|
await this._tween({ status: 'moving', left: dest.left, top: dest.top, carriageHeight: destCH }, jitter(1500))
|
|
485
|
-
await this._tween({ status: 'unloading', forkExtension:
|
|
645
|
+
await this._tween({ status: 'unloading', forkExtension: sideB * forkLen }, jitter(700))
|
|
486
646
|
await this._tween({ forkLift: 0 }, jitter(400))
|
|
487
647
|
await this._tween({ status: 'idle', forkExtension: 0 }, jitter(700))
|
|
488
648
|
|
|
@@ -553,3 +713,24 @@ function resolveCarrierCenterY(c: Component): number | null {
|
|
|
553
713
|
function numOr(v: unknown, dflt: number): number {
|
|
554
714
|
return typeof v === 'number' && Number.isFinite(v) ? v : dflt
|
|
555
715
|
}
|
|
716
|
+
|
|
717
|
+
// ── 2D render helpers ────────────────────────────────────────────────────
|
|
718
|
+
function num(v: unknown, dflt: number): number {
|
|
719
|
+
return typeof v === 'number' && Number.isFinite(v) ? v : dflt
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function clamp(v: number, min: number, max: number): number {
|
|
723
|
+
return v < min ? min : v > max ? max : v
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/** "#rrggbb" 또는 "name" 색에 alpha 적용. 실패 시 fallback rgba. */
|
|
727
|
+
function withAlpha(color: string, alpha: number): string {
|
|
728
|
+
// "#rrggbb" 형식만 지원 — name color 는 rgba 직접 변환 못해서 그대로 사용
|
|
729
|
+
if (color && color[0] === '#' && color.length === 7) {
|
|
730
|
+
const r = parseInt(color.slice(1, 3), 16)
|
|
731
|
+
const g = parseInt(color.slice(3, 5), 16)
|
|
732
|
+
const b = parseInt(color.slice(5, 7), 16)
|
|
733
|
+
return `rgba(${r},${g},${b},${alpha})`
|
|
734
|
+
}
|
|
735
|
+
return color
|
|
736
|
+
}
|