@operato/scene-storage 10.0.0-beta.44 → 10.0.0-beta.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/dist/crane-3d.d.ts +10 -0
- package/dist/crane-3d.js +34 -5
- package/dist/crane-3d.js.map +1 -1
- package/dist/crane.d.ts +136 -6
- package/dist/crane.js +578 -48
- package/dist/crane.js.map +1 -1
- package/dist/parcel-3d.d.ts +1 -0
- package/dist/parcel-3d.js +18 -1
- package/dist/parcel-3d.js.map +1 -1
- package/dist/rack-grid-3d.js +26 -8
- package/dist/rack-grid-3d.js.map +1 -1
- package/dist/rack-grid.d.ts +103 -10
- package/dist/rack-grid.js +484 -86
- package/dist/rack-grid.js.map +1 -1
- package/dist/storage-rack-3d.js +1 -1
- package/dist/storage-rack-3d.js.map +1 -1
- package/dist/storage-rack.d.ts +40 -6
- package/dist/storage-rack.js +111 -14
- package/dist/storage-rack.js.map +1 -1
- package/package.json +4 -4
- package/src/crane-3d.ts +34 -4
- package/src/crane.ts +625 -57
- package/src/parcel-3d.ts +19 -1
- package/src/rack-grid-3d.ts +31 -8
- package/src/rack-grid.ts +504 -82
- package/src/storage-rack-3d.ts +1 -1
- package/src/storage-rack.ts +111 -14
- package/test/test-coord-alignment.ts +2 -2
- package/test/test-crane-bay-match.ts +130 -0
- package/test/test-crane-binding-resolve.ts +168 -0
- package/test/test-crane-duration.ts +90 -0
- package/test/test-crane-rotation-reach.ts +218 -0
- package/test/test-rack-grid-3d-alignment.ts +235 -0
- package/test/test-rack-grid-3d-attach-real.ts +375 -0
- package/test/test-rack-grid-cell.ts +2 -2
- package/test/test-rack-grid-location.ts +2 -2
- package/test/test-rack-grid-occupied-slots.ts +165 -0
- package/test/test-rack-grid-picking-position.ts +154 -0
- package/test/test-rack-grid-slot-api.ts +483 -0
- package/test/test-slot-ids-enumeration.ts +137 -0
- package/test/things-scene-loader-impl.mjs +37 -0
- package/test/things-scene-loader.mjs +24 -0
- package/translations/en.json +2 -0
- package/translations/ja.json +2 -0
- package/translations/ko.json +2 -0
- package/translations/ms.json +2 -0
- package/translations/zh.json +2 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,50 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [10.0.0-beta.47](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.46...v10.0.0-beta.47) (2026-05-26)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
* **scene-base:** AN-PR-2 — Obstacle interface + 기존 컴포넌트 자격 부여 ([3e45e19](https://github.com/things-scene/operato-scene/commit/3e45e19f53c04d7f01bbbe9ea079f5f7f7a7d8f5))
|
|
12
|
+
* **transport/storage:** Phase Z PR-4 — multi-carrier mover (forkSlots state) ([5e398d1](https://github.com/things-scene/operato-scene/commit/5e398d12075c7732d42a27a439d2246acb735289))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### :bug: Bug Fix
|
|
16
|
+
|
|
17
|
+
* **storage:** test runner ESM 해결 — Node loader (Component 가 transfer bundle 미포함) ([984ca9c](https://github.com/things-scene/operato-scene/commit/984ca9c09cedd90cb60fb000a014914ab01bd69d))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [10.0.0-beta.46](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.45...v10.0.0-beta.46) (2026-05-24)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### :rocket: New Features
|
|
25
|
+
|
|
26
|
+
* capability 기반 simulate + binding + RackGrid 셀 리스트 정보 정확화 ([3dbcf66](https://github.com/things-scene/operato-scene/commit/3dbcf6645a1d0d6212523a85197b357380f9815a))
|
|
27
|
+
* **crane:** capability 기반 자동 simulate — 인접 SlottedHolder 의 slot ([16bba83](https://github.com/things-scene/operato-scene/commit/16bba83ecf1cc678fcaee78f04542e50f2bebd14))
|
|
28
|
+
* **crane:** Phase Z PR-2 — dispatcher 명시 작업 통합 main loop ([08f8e65](https://github.com/things-scene/operato-scene/commit/08f8e65cdc038c4dd15244c603c0bd002431747f))
|
|
29
|
+
* **slotted-holder:** occupiedSlotIds/emptySlotIds 에 filter predicate 추가 ([5adecd5](https://github.com/things-scene/operato-scene/commit/5adecd5c87eee2dc12d50c72e0842aec222b9084))
|
|
30
|
+
* **slotted-holder:** slotIds() capability — slot enumeration entry point ([a9de95a](https://github.com/things-scene/operato-scene/commit/a9de95a04dde1758fe2236d80dd177ffad9307ed))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### :bug: Bug Fix
|
|
34
|
+
|
|
35
|
+
* **crane:** bayKey = first segment (col), reach 박스 X 를 rail 폭으로 정정 ([b3a10e9](https://github.com/things-scene/operato-scene/commit/b3a10e96da0ce1739f8420df0f70210094d90f35))
|
|
36
|
+
* **crane:** rail axis = matrixWorld X — 2D rotation 매핑 무관 + duration 거리 비례 ([4944ea7](https://github.com/things-scene/operato-scene/commit/4944ea7a87d5c9205387ef8272e3d6cafb2a2c74))
|
|
37
|
+
* **crane:** reach 박스 = rail 전체 X + fork 최대 extend Z, row 별 대표 검사 ([9fb88e7](https://github.com/things-scene/operato-scene/commit/9fb88e7a7c7ca8e612a07692f3e399d2a9793ee5))
|
|
38
|
+
* **crane:** 마지막 shelf 도달 못 하는 carriageHeight clamp 의 magic 0.85 제거 ([e6a9c1a](https://github.com/things-scene/operato-scene/commit/e6a9c1af1b762f874ad7a584137efe9fba120fa4))
|
|
39
|
+
* **rack-grid:** get layout() 을 TABLE_LAYOUT 으로 복원 — isEmpty 시각 회귀 차단 ([82d9d79](https://github.com/things-scene/operato-scene/commit/82d9d79d4862658faba96ca8171a8ba1a4ae9552))
|
|
40
|
+
* **storage:** RackGrid SlottedHolder 4 메서드 자체 처리 + NaN safety + carrier carried placement ([84f0392](https://github.com/things-scene/operato-scene/commit/84f0392a6d3093c9a93efa15682538719d4a77e1))
|
|
41
|
+
* **storage:** RackGrid 의 handoff carrier follow + visible size 회복 ([22f51e8](https://github.com/things-scene/operato-scene/commit/22f51e8d8813c9e20d765ac87a687771e4d329fa))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### :house: Code Refactoring
|
|
45
|
+
|
|
46
|
+
* **storage:** public/private helper 이름의 cell* → slot* rename ([9de4ea0](https://github.com/things-scene/operato-scene/commit/9de4ea07f39fb349f7b52831aa7d4c3c8165f435))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
6
50
|
## [10.0.0-beta.44](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.43...v10.0.0-beta.44) (2026-05-21)
|
|
7
51
|
|
|
8
52
|
**Note:** Version bump only for package @operato/scene-storage
|
package/dist/crane-3d.d.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { RealObjectGroup } from '@hatiolab/things-scene';
|
|
3
3
|
export declare class Crane3D extends RealObjectGroup {
|
|
4
|
+
/**
|
|
5
|
+
* carriageHeight 의 실제 최댓값 — *mast 의 carriage 이동 가능 범위* 기반.
|
|
6
|
+
* mastH = D - railH * 2 - baseH - topFrameH - topGuideH (mast 의 실제 길이).
|
|
7
|
+
* carriage 의 *top edge 가 mast top 안에 머물도록* carriageH/2 추가 차감.
|
|
8
|
+
*
|
|
9
|
+
* 이전 D * 0.85 magic 대체 — base/top frame 의 실제 차지 비율과 무관한 추정값
|
|
10
|
+
* 이라 crane.depth = rack.depth 동일 모델에서도 carriage 가 rack 상위 cell
|
|
11
|
+
* 도달 못 함. 정확한 mastH 사용으로 *동일 size 모델에서 정상 도달* 보장.
|
|
12
|
+
*/
|
|
13
|
+
static _maxCarriageHeight(D: number, width: number, height: number): number;
|
|
4
14
|
private _forkGroup?;
|
|
5
15
|
private _carrierBaseY;
|
|
6
16
|
private _bladeMidZ;
|
package/dist/crane-3d.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* carriageHeight, forkExtension (±), forkLift
|
|
31
31
|
*/
|
|
32
32
|
import * as THREE from 'three';
|
|
33
|
-
import {
|
|
33
|
+
import { RealObjectGroup } from '@hatiolab/things-scene';
|
|
34
34
|
const MAST_COLOR = 0xff7a00; // mast — orange
|
|
35
35
|
const TROLLEY_COLOR = 0x3a4048; // base / top — dark charcoal
|
|
36
36
|
const CARRIAGE_COLOR = 0xffcc00; // carriage (shuttle) — yellow
|
|
@@ -39,6 +39,25 @@ const FORK_COLOR = 0xffcc00; // fork — yellow (same as carriage)
|
|
|
39
39
|
const RAIL_COLOR = 0x1a1f24; // rail — dark steel
|
|
40
40
|
const LAMP_OFF = 0x222222;
|
|
41
41
|
export class Crane3D extends RealObjectGroup {
|
|
42
|
+
/**
|
|
43
|
+
* carriageHeight 의 실제 최댓값 — *mast 의 carriage 이동 가능 범위* 기반.
|
|
44
|
+
* mastH = D - railH * 2 - baseH - topFrameH - topGuideH (mast 의 실제 길이).
|
|
45
|
+
* carriage 의 *top edge 가 mast top 안에 머물도록* carriageH/2 추가 차감.
|
|
46
|
+
*
|
|
47
|
+
* 이전 D * 0.85 magic 대체 — base/top frame 의 실제 차지 비율과 무관한 추정값
|
|
48
|
+
* 이라 crane.depth = rack.depth 동일 모델에서도 carriage 가 rack 상위 cell
|
|
49
|
+
* 도달 못 함. 정확한 mastH 사용으로 *동일 size 모델에서 정상 도달* 보장.
|
|
50
|
+
*/
|
|
51
|
+
static _maxCarriageHeight(D, width, height) {
|
|
52
|
+
const S = Math.min(width, height);
|
|
53
|
+
const railH = S * 0.04;
|
|
54
|
+
const baseH = S * 0.18;
|
|
55
|
+
const topFrameH = S * 0.1;
|
|
56
|
+
const topGuideH = S * 0.1;
|
|
57
|
+
const carriageH = S * 0.12;
|
|
58
|
+
const mastH = Math.max(D - railH * 2 - baseH - topFrameH - topGuideH, S * 0.5);
|
|
59
|
+
return Math.max(0, mastH - carriageH / 2);
|
|
60
|
+
}
|
|
42
61
|
_forkGroup;
|
|
43
62
|
_carrierBaseY = 0;
|
|
44
63
|
_bladeMidZ = 0;
|
|
@@ -72,7 +91,12 @@ export class Crane3D extends RealObjectGroup {
|
|
|
72
91
|
// Actuators
|
|
73
92
|
const D = numOr(depth, Math.max(width, height) * 4);
|
|
74
93
|
const carriageRaw = numOr(this.component.state.carriageHeight, this.component._canonicalDefault?.('carriageHeight') ?? D * 0.4);
|
|
75
|
-
|
|
94
|
+
// carriageHeight clamp — 실제 mast 의 carriage 이동 가능 범위 기반. helper 사용
|
|
95
|
+
// (render + onchange partial update 둘 다 동일 식). 이전엔 D * 0.85 magic 사용 —
|
|
96
|
+
// base/top frame 의 실제 차지 비율과 무관한 추정. crane.depth = rack.depth 동일
|
|
97
|
+
// 모델에서도 carriage 가 rack 상위 cell 도달 불가 → 14 levels rack 의 *마지막
|
|
98
|
+
// shelf 만 한 층 아래에서 포킹* 증상의 root cause 였음.
|
|
99
|
+
const carriageHeight = Math.max(0, Math.min(carriageRaw, Crane3D._maxCarriageHeight(D, width, height)));
|
|
76
100
|
const forkLength = numOr(this.component.state.forkLength, height * 0.6);
|
|
77
101
|
const forkExtensionRaw = numOr(this.component.state.forkExtension, 0);
|
|
78
102
|
const forkExtension = Math.max(-forkLength, Math.min(forkLength, forkExtensionRaw));
|
|
@@ -322,9 +346,11 @@ export class Crane3D extends RealObjectGroup {
|
|
|
322
346
|
let meshUpdated = false;
|
|
323
347
|
if (('carriageHeight' in after || 'forkLiftRT' in after) && this._carriageLiftGroup) {
|
|
324
348
|
const state = this.component.state;
|
|
325
|
-
const
|
|
349
|
+
const W = numOr(state.width, 100);
|
|
350
|
+
const H = numOr(state.height, 100);
|
|
351
|
+
const D = numOr(state.depth, Math.max(W, H) * 4);
|
|
326
352
|
const carriageRaw = numOr(state.carriageHeight, D * 0.4);
|
|
327
|
-
const carriageHeight = Math.max(0, Math.min(carriageRaw, D
|
|
353
|
+
const carriageHeight = Math.max(0, Math.min(carriageRaw, Crane3D._maxCarriageHeight(D, W, H)));
|
|
328
354
|
const forkLift = numOr(state.forkLiftRT, 0);
|
|
329
355
|
this._carriageLiftGroup.position.y = this._computeLiftGroupY(carriageHeight, forkLift);
|
|
330
356
|
meshUpdated = true;
|
|
@@ -415,7 +441,10 @@ export class Crane3D extends RealObjectGroup {
|
|
|
415
441
|
if (this._forkGroup) {
|
|
416
442
|
for (const child of this._forkGroup.children) {
|
|
417
443
|
const ctx = child.userData?.context;
|
|
418
|
-
|
|
444
|
+
// `ctx instanceof RealObject` 는 *cross-module identity 실패* 가능
|
|
445
|
+
// (Parcel3D 가 다른 things-scene 인스턴스의 RealObject 를 extend 한 경우
|
|
446
|
+
// instanceof 가 false 반환). duck-type 으로 완화 — *crane 자신만 제외*.
|
|
447
|
+
if (ctx && ctx !== this) {
|
|
419
448
|
child.position.z = this._bladeMidZ;
|
|
420
449
|
}
|
|
421
450
|
}
|
package/dist/crane-3d.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crane-3d.js","sourceRoot":"","sources":["../src/crane-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Crane 3D — heavy-duty pallet AS/RS stacker crane (twin-mast).\n *\n * Axis convention (things-scene 3D):\n * +X = aisle 진행 방향 (Crane 좌우 이동, *옆으로 길게* 축)\n * +Y = 수직 (Crane 키, carriage 상하 이동)\n * +Z = aisle 폭 방향 (Rack cell 쪽 / fork 신축 방향)\n *\n * **스케일 룰**:\n * - 모든 가로(X,Z) dimension 과 Y 두께 → 2D footprint (state.width, state.height) 비례\n * - Mast Y 길이만 → state.depth 사용 (수직 키)\n * - Y 두께는 절대 depth (크레인 키) 에 비례 안 함 → 키워도 뭉치 안 두꺼워짐\n * - mm/cm/scene-unit 무관 — 사용자의 footprint 스케일에 자동 맞춤\n *\n * 부품:\n * [Ceiling rail]\n * [Top guide trolley] — 천장 rail 위\n * [Top frame] — 두 mast 상부 연결\n * [Mast L][Mast R] — twin yellow column (X 축으로 떨어져 있음)\n * [Carriage] — 두 mast 사이 가로 frame (carriageHeight 로 상하)\n * └── [Two forks] — carriage 에서 ±Z 신축 (forkExtension)\n * [Base trolley] — 바닥 motor housing (X 길게)\n * └── [Cabinet] — base 한쪽 red 컨트롤박스\n * └── [Status lamp]\n * [Floor rail]\n *\n * Actuators (state):\n * carriageHeight, forkExtension (±), forkLift\n */\n\nimport * as THREE from 'three'\nimport { RealObject, RealObjectGroup } from '@hatiolab/things-scene'\n\nconst MAST_COLOR = 0xff7a00 // mast — orange\nconst TROLLEY_COLOR = 0x3a4048 // base / top — dark charcoal\nconst CARRIAGE_COLOR = 0xffcc00 // carriage (shuttle) — yellow\nconst CONTROLLER_COLOR = 0xc63333 // cabinet — red\nconst FORK_COLOR = 0xffcc00 // fork — yellow (same as carriage)\nconst RAIL_COLOR = 0x1a1f24 // rail — dark steel\nconst LAMP_OFF = 0x222222\n\nexport class Crane3D extends RealObjectGroup {\n private _forkGroup?: THREE.Group\n private _carrierBaseY: number = 0\n private _bladeMidZ: number = 0\n /** floor rail 만 제외한 나머지 (trolley + masts + carriage + fork) 의 movable parent. */\n private _trolleyGroup?: THREE.Group\n /** carriage + fork 의 lift parent (carriageHeight + forkLiftRT 변경 시 Y 만 update). */\n private _carriageLiftGroup?: THREE.Group\n /** Fork active extension mesh — scale.z 와 position.z 로 lerp (rebuild 없이). */\n private _extLeftMesh?: THREE.Mesh\n private _extRightMesh?: THREE.Mesh\n private _extBaseParams?: { bladeSpacing: number; carriageZ: number; stubL: number }\n /** Fork mesh 의 group-local Y center (carriage 위 + bladeH/2). _applyForkExtensionMeshes 가 ext mesh Y 결정. */\n private _forkOffsetY: number = 0\n /** liftGroup.position.y 재계산용 base parameters. */\n private _liftBaseParams?: { baseTrolleyY: number; baseH: number; carriageH: number; bladeH: number }\n\n build() {\n super.build()\n\n this._forkGroup = undefined\n this._carrierBaseY = 0\n this._bladeMidZ = 0\n this._trolleyGroup = undefined\n this._carriageLiftGroup = undefined\n this._extLeftMesh = undefined\n this._extRightMesh = undefined\n this._extBaseParams = undefined\n this._liftBaseParams = undefined\n\n const { width, height, depth } = this.component.state\n const emissiveColor = (this.component.state.lampEmissive as string) || '#222222'\n const status = this.component.state.status\n const lampOn = status && status !== 'idle'\n\n // Actuators\n const D = numOr(depth, Math.max(width, height) * 4)\n const carriageRaw = numOr((this.component.state as any).carriageHeight, (this.component as any)._canonicalDefault?.('carriageHeight') ?? D * 0.4)\n const carriageHeight = Math.max(0, Math.min(carriageRaw, D * 0.85))\n\n const forkLength = numOr((this.component.state as any).forkLength, height * 0.6)\n const forkExtensionRaw = numOr((this.component.state as any).forkExtension, 0)\n const forkExtension = Math.max(-forkLength, Math.min(forkLength, forkExtensionRaw))\n // forkLiftRT — 시뮬 runtime current 들림. state.forkLift 는 *configured 진폭*\n // (사용자 설정) 라 안 건드림. 3D carriage Y 는 runtime 값 사용.\n const forkLift = numOr((this.component.state as any).forkLiftRT, 0)\n\n // ── Axis convention (FIXED): ─────────────────────────────────────\n // Rail = X (state.left = 2D X = 3D X). Crane 좌우 이동.\n // Fork = Z (2D Y = 3D Z). Fork 앞뒤 신축.\n // 사용자: \"포크는 앞뒤로, 크레인 본체는 좌우로.\"\n // width = rail-direction 풋프린트 (좁음)\n // height = cross-aisle (fork 방향) 풋프린트 (깊음, fork 가 cell 까지 닿는 거리)\n const S = Math.min(width, height)\n const railH = S * 0.04\n const baseH = S * 0.18\n const topFrameH = S * 0.1\n const topGuideH = S * 0.1\n const carriageH = S * 0.12\n // Carriage assembly 크기 — state.carriageWidth 기반 (rail 길이 = crane.width\n // 와 독립). 미명시 시 rail 의 10%.\n const carriageAssemblyW = numOr((this.component.state as any).carriageWidth, width * 0.1)\n const mastW = carriageAssemblyW * 0.15 // mast X 단면 (along rail)\n const mastD = height * 0.25 // mast Z 단면 (cross-rail)\n const mastSpacing = carriageAssemblyW * 0.85 // 두 mast X 간격\n const bladeW = S * 0.1\n // bladeH — fork 두께. carriage 보다 얇게 (실 fork prong 의 가는 모양).\n // carriage 와 *같은 Y center (0)* 이고 두께만 carriageH * 0.35.\n const bladeH = carriageH * 0.35\n const bladeL = forkLength\n const bladeSpacing = mastSpacing * 0.45\n const carriageW = mastSpacing - mastW * 0.2\n const carriageZ = height * 0.55\n // Cabinet — 존재감만. 작게.\n const cabW = S * 0.18\n const cabH = S * 0.18\n const cabD = S * 0.15\n\n const baseY = -D / 2\n const mastH = Math.max(D - railH * 2 - baseH - topFrameH - topGuideH, S * 0.5)\n\n // ── Materials ─────────────────────────────────────────────────────\n const mastMat = new THREE.MeshStandardMaterial({ color: MAST_COLOR, metalness: 0.3, roughness: 0.5 })\n const trolleyMat = new THREE.MeshStandardMaterial({ color: TROLLEY_COLOR, metalness: 0.6, roughness: 0.5 })\n const carriageMat = new THREE.MeshStandardMaterial({ color: CARRIAGE_COLOR, metalness: 0.85, roughness: 0.35 })\n const cabinetMat = new THREE.MeshStandardMaterial({ color: CONTROLLER_COLOR, metalness: 0.2, roughness: 0.6 })\n const forkMat = new THREE.MeshStandardMaterial({ color: FORK_COLOR, metalness: 0.85, roughness: 0.3 })\n const railMat = new THREE.MeshStandardMaterial({ color: RAIL_COLOR, metalness: 0.9, roughness: 0.3 })\n\n // ── Floor rail (고정 — crane 본체 안 움직임). 폭 = crane.width (overhang 제거).\n {\n const railThin = railH * 0.5\n const geo = new THREE.BoxGeometry(width, railThin, height * 0.15)\n const mesh = new THREE.Mesh(geo, railMat)\n mesh.position.set(0, baseY + railThin / 2, 0)\n mesh.receiveShadow = true\n this.object3d.add(mesh)\n }\n\n // ── Trolley group — carriage assembly (rail 위 X 만 이동) ─────────\n // 모든 movable 부품 (base trolley, masts, carriage, fork, cabinet, lamp)\n // 의 parent. carriagePosition 변경 시 group 의 local X 만 변경.\n const trolleyGroup = new THREE.Group()\n this._trolleyGroup = trolleyGroup\n this.object3d.add(trolleyGroup)\n // 초기 carriagePosition 적용 — rail-local X (0 ~ width) → object3d-local X (-W/2 ~ +W/2)\n const carriagePos = numOr((this.component.state as any).carriagePosition, (this.component as any)._canonicalDefault?.('carriagePosition') ?? width / 2)\n trolleyGroup.position.x = carriagePos - width / 2\n\n // ── Base trolley ──────────────────────────────────────────────────\n // Cabinet 이 mast 바깥쪽에 자연스럽게 놓이도록 *trolley 폭을 mast + cabinet\n // padding 까지 확장*. carriage assembly width 보다 양 옆으로 (cabW + gap) × 2.\n const trolleyPad = cabW + S * 0.04\n const baseTrolleyW = carriageAssemblyW + trolleyPad * 2\n const baseTrolleyY = baseY + railH + baseH / 2\n {\n const geo = new THREE.BoxGeometry(baseTrolleyW, baseH, height * 0.7)\n const mesh = new THREE.Mesh(geo, trolleyMat)\n mesh.position.set(0, baseTrolleyY, 0)\n mesh.castShadow = true\n mesh.receiveShadow = true\n trolleyGroup.add(mesh)\n }\n\n // ── Control cabinet — mast 바깥쪽 (trolley 의 *확장 padding 영역* 위) ─────\n {\n const geo = new THREE.BoxGeometry(cabW, cabH, cabD)\n const cab = new THREE.Mesh(geo, cabinetMat)\n cab.position.set(\n -(carriageAssemblyW / 2 + cabW / 2 + S * 0.02), // mast 왼쪽 바깥\n baseTrolleyY + baseH / 2 + cabH / 2,\n -height * 0.25 + cabD / 2\n )\n cab.castShadow = true\n trolleyGroup.add(cab)\n }\n\n // ── Status lamp ───────────────────────────────────────────────────\n {\n const lampR = S * 0.04\n const lampH = S * 0.1\n const lampMat = new THREE.MeshStandardMaterial({\n color: lampOn ? emissiveColor : LAMP_OFF,\n emissive: lampOn ? emissiveColor : LAMP_OFF,\n emissiveIntensity: lampOn ? 1.5 : 0.2,\n metalness: 0,\n roughness: 0.3\n })\n const geo = new THREE.CylinderGeometry(lampR, lampR * 0.8, lampH, 12)\n const lamp = new THREE.Mesh(geo, lampMat)\n lamp.position.set(carriageAssemblyW / 2 + lampR * 1.5 + S * 0.02, baseTrolleyY + baseH / 2 + lampH / 2, 0)\n trolleyGroup.add(lamp)\n }\n\n // ── Twin masts ────────────────────────────────────────────────────\n const mastY = baseTrolleyY + baseH / 2 + mastH / 2\n for (const xOff of [-mastSpacing / 2, +mastSpacing / 2]) {\n const geo = new THREE.BoxGeometry(mastW, mastH, mastD)\n const mesh = new THREE.Mesh(geo, mastMat)\n mesh.position.set(xOff, mastY, 0)\n mesh.castShadow = true\n mesh.receiveShadow = true\n trolleyGroup.add(mesh)\n }\n\n // ── Carriage + Fork lift group (carriageHeight + forkLiftRT 따라 Y 이동) ─\n // _carriageLiftGroup 안에 carriage + forkGroup. forkLiftRT / carriageHeight\n // 변경 시 *그룹 Y 만 update* (mesh rebuild X). _forkGroup 의 child carrier 가\n // *dispose 없이 그대로* 함께 따라 움직임.\n const stubL = Math.min(carriageZ * 0.2, Math.max(bladeL * 0.05, 6))\n const liftGroup = new THREE.Group()\n this._carriageLiftGroup = liftGroup\n this._liftBaseParams = { baseTrolleyY, baseH, carriageH, bladeH }\n liftGroup.position.set(0, this._computeLiftGroupY(carriageHeight, forkLift), 0)\n trolleyGroup.add(liftGroup)\n\n // Carriage — liftGroup local center\n {\n const geo = new THREE.BoxGeometry(carriageW, carriageH, carriageZ)\n const mesh = new THREE.Mesh(geo, carriageMat)\n mesh.position.set(0, 0, 0)\n mesh.castShadow = true\n mesh.receiveShadow = true\n liftGroup.add(mesh)\n }\n\n // ── Two-prong forks ───────────────────────────────────────────────\n // stub (4 box, fixed) + active extension (2 box, scale.z 로 lerp).\n // active mesh 는 *unit-length* 로 생성. _applyForkExtension 이 scale.z 와\n // position.z 로 길이/방향 update — rebuild 없이 매 frame 부드러운 변형.\n const absExt = Math.abs(forkExtension)\n const sign = forkExtension >= 0 ? 1 : -1\n {\n const group = new THREE.Group()\n // _forkGroup 은 liftGroup-local center (0,0,0) — frame 일치 단순화.\n // attach localPosition (carrier 자식) 도 *group-local = liftGroup-local* 동일 frame.\n // Fork mesh 자체가 group-local 안에서 carriage *위* 로 (mesh.position.y).\n group.position.set(0, 0, 0)\n\n // Fork mesh 가 carriage 와 *수평 (같은 Y 평면)* — carriage 안에 embed.\n // fork mesh group-local Y center = 0 (carriage center 와 같음).\n // fork blade *bottom* 면 group-local Y = -bladeH/2.\n // fork blade *top* 면 group-local Y = +bladeH/2.\n const forkOffsetY = 0\n\n // 양옆 stub — 두 prong × 두 측면 = 4 box\n const stubGeo = new THREE.BoxGeometry(bladeW, bladeH, stubL)\n for (const xOff of [-bladeSpacing / 2, +bladeSpacing / 2]) {\n for (const zSide of [-1, +1]) {\n const mesh = new THREE.Mesh(stubGeo, forkMat)\n mesh.position.set(xOff, forkOffsetY, zSide * (carriageZ / 2 + stubL / 2))\n mesh.castShadow = true\n mesh.receiveShadow = true\n group.add(mesh)\n }\n }\n\n // Active extension — unit length, scale.z + position.z 로 변형\n const extGeo = new THREE.BoxGeometry(bladeW, bladeH, 1)\n const extLeft = new THREE.Mesh(extGeo, forkMat)\n const extRight = new THREE.Mesh(extGeo, forkMat)\n this._forkOffsetY = forkOffsetY\n extLeft.castShadow = true\n extLeft.receiveShadow = true\n extRight.castShadow = true\n extRight.receiveShadow = true\n group.add(extLeft)\n group.add(extRight)\n this._extLeftMesh = extLeft\n this._extRightMesh = extRight\n this._extBaseParams = { bladeSpacing, carriageZ, stubL }\n this._applyForkExtensionMeshes(absExt, sign)\n\n // carrier 의 초기 Z = sign * absExt (= _applyForkExtensionMeshes 의 공식 동일).\n const carrierZ = sign * absExt\n\n liftGroup.add(group)\n this._forkGroup = group\n // Carrier 외부 bottom 정렬점 (liftGroup-local Y) = fork blade *bottom* =\n // -bladeH/2 (fork mesh group-local center = 0, 두께 bladeH).\n // 사용자 모델: \"fork 의 아랫면 ≈ carrier 의 아랫면\" — fork blade 가 carrier\n // 의 bottom 부분 안으로 *찔러 들어감* (겹친 자세).\n this._carrierBaseY = -bladeH / 2\n this._bladeMidZ = carrierZ\n }\n\n // ── Top frame (connects mast tops) — trolley 함께 이동 ─────────────\n const topFrameY = mastY + mastH / 2 + topFrameH / 2\n {\n const geo = new THREE.BoxGeometry(mastSpacing + mastW, topFrameH, height * 0.35)\n const mesh = new THREE.Mesh(geo, trolleyMat)\n mesh.position.set(0, topFrameY, 0)\n mesh.castShadow = true\n trolleyGroup.add(mesh)\n }\n\n // ── Top guide trolley — trolley 함께 이동 (ceiling rail 위 미끄러짐) ─\n const topGuideY = topFrameY + topFrameH / 2 + topGuideH / 2\n {\n const geo = new THREE.BoxGeometry(mastSpacing + mastW * 2, topGuideH, height * 0.3)\n const mesh = new THREE.Mesh(geo, trolleyMat)\n mesh.position.set(0, topGuideY, 0)\n mesh.castShadow = true\n trolleyGroup.add(mesh)\n }\n\n // Ceiling rail 생략 — 상단은 top guide trolley 만으로 충분. 사용자 의도.\n\n }\n\n getCarriageFrame(): THREE.Object3D | undefined {\n // Fallback chain — carrier 가 *carriage transform (X 이동, Y lift)* 따라오도록.\n // _forkGroup 미존재 시 _carriageLiftGroup (X+Y 따라옴) → _trolleyGroup (X 만) →\n // root (no follow). 절대 root 로 떨어지지 않도록 lift/trolley 우선.\n return this._forkGroup ?? this._carriageLiftGroup ?? this._trolleyGroup ?? this.object3d\n }\n\n /**\n * Fork blade *bottom* 의 liftGroup-local Y. *carrier 외부 bottom 정렬점*.\n *\n * 모델: carrier 의 외부 bottom 과 fork blade 의 bottom 이 *거의 일치*. fork 가\n * carrier 의 bottom 부분을 *찔러 들어가* carrier 와 *겹친 자세* (pallet pocket\n * 안 fork 진입). attachPointFor 가 `carrierBaseY + carrier.depth/2` 로 carrier\n * center 를 정렬 → carrier bottom = fork blade bottom.\n */\n get carrierBaseY(): number {\n return this._carrierBaseY\n }\n\n get bladeMidZ(): number {\n return this._bladeMidZ\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n // carriagePosition — trolleyGroup.position.x (mesh-level update).\n if ('carriagePosition' in after && this._trolleyGroup) {\n const W = numOr(this.component.state.width, 100)\n const pos = numOr(after.carriagePosition, W / 2)\n this._trolleyGroup.position.x = pos - W / 2\n }\n\n // Mesh-level updates — fork extension / lift / carriage height. *rebuild 없이*\n // mesh 의 scale / position 만 변경. _forkGroup 의 child carrier 가 dispose\n // 없이 그대로 따라 움직임 (fork 작업 시 시각 자연스러움).\n //\n // status / bodyColor / lampEmissive 는 *cosmetic* — full rebuild 회피. 별도\n // 처리 없음 시 status 변경 시 carrier dispose → 사라짐 결함의 원인. 향후\n // material color 만 update 하는 path 추가 가능.\n const needsFullRebuild =\n 'width' in after ||\n 'height' in after ||\n 'depth' in after ||\n 'forkLength' in after ||\n 'carriageWidth' in after\n\n if (!needsFullRebuild) {\n let meshUpdated = false\n if (('carriageHeight' in after || 'forkLiftRT' in after) && this._carriageLiftGroup) {\n const state = this.component.state as any\n const D = numOr(state.depth, Math.max(numOr(state.width, 100), numOr(state.height, 100)) * 4)\n const carriageRaw = numOr(state.carriageHeight, D * 0.4)\n const carriageHeight = Math.max(0, Math.min(carriageRaw, D * 0.85))\n const forkLift = numOr(state.forkLiftRT, 0)\n this._carriageLiftGroup.position.y = this._computeLiftGroupY(carriageHeight, forkLift)\n meshUpdated = true\n }\n if ('forkExtension' in after && this._extLeftMesh && this._extRightMesh) {\n const state = this.component.state as any\n const forkLength = numOr(state.forkLength, numOr(state.height, 100) * 0.6)\n const forkExtensionRaw = numOr(state.forkExtension, 0)\n const forkExtension = Math.max(-forkLength, Math.min(forkLength, forkExtensionRaw))\n const absExt = Math.abs(forkExtension)\n const sign = forkExtension >= 0 ? 1 : -1\n this._applyForkExtensionMeshes(absExt, sign)\n meshUpdated = true\n }\n if (meshUpdated) return\n }\n\n if (needsFullRebuild) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n /** carriageHeight + forkLiftRT 를 liftGroup.position.y 로 변환. */\n private _computeLiftGroupY(carriageHeight: number, forkLift: number): number {\n const p = this._liftBaseParams\n if (!p) return 0\n return p.baseTrolleyY + p.baseH / 2 + carriageHeight + forkLift + p.carriageH / 2\n }\n\n /**\n * Carrier 외부 bottom 의 world Y → carriageHeight state 값 inverse-solve.\n *\n * Forward 공식 (build):\n * liftGroup.y crane-local = baseTrolleyY + baseH/2 + carriageHeight + forkLift + carriageH/2\n * carrier 외부 bottom crane-local = liftGroup.y + carrierBaseY (= -bladeH/2)\n * = baseTrolleyY + baseH/2 + carriageH/2 - bladeH/2 + carriageHeight + forkLift\n *\n * Inverse:\n * carriageHeight = worldY − craneCenterY − (baseTrolleyY + baseH/2 + carriageH/2 − bladeH/2) − forkLift\n */\n solveCarriageHeightForCarrierBaseWorldY(worldY: number, forkLift: number = 0): number {\n const p = this._liftBaseParams\n if (!p) return 0\n this.object3d.updateWorldMatrix(true, false)\n const v = new THREE.Vector3()\n this.object3d.matrixWorld.decompose(v, new THREE.Quaternion(), new THREE.Vector3())\n const craneCenterWorldY = v.y\n return worldY - craneCenterWorldY - (p.baseTrolleyY + p.baseH / 2 + p.carriageH / 2 - p.bladeH / 2) - forkLift\n }\n\n /**\n * target 의 *crane-local Z* → fork extension 값 inverse-solve.\n *\n * Forward (_applyForkExtensionMeshes): `_bladeMidZ = sign * absExt`\n * (carrier 가 ext 만큼 fork 따라 진출). Inverse: `ext = |localZ|, sign = sign(localZ)`.\n *\n * forkLength 로 clamp — localZ 가 forkLength 보다 멀면 carrier 가 fork tip 까지만.\n */\n solveForkExtensionForLocalZ(localZ: number): number {\n const sign = localZ >= 0 ? 1 : -1\n const state = this.component.state as any\n const forkLen = numOr(state.forkLength, numOr(state.height, 100) * 0.6)\n const clamped = Math.max(0, Math.min(forkLen, Math.abs(localZ)))\n return sign * clamped\n }\n\n /** Fork active extension mesh 의 scale.z + position.z + visibility update. */\n private _applyForkExtensionMeshes(absExt: number, sign: number) {\n if (!this._extLeftMesh || !this._extRightMesh || !this._extBaseParams) return\n const { bladeSpacing, carriageZ, stubL } = this._extBaseParams\n const visible = absExt > 0.5\n const len = Math.max(0.001, absExt)\n const posZ = sign * (carriageZ / 2 + stubL + absExt / 2)\n this._extLeftMesh.scale.z = len\n this._extRightMesh.scale.z = len\n this._extLeftMesh.position.set(-bladeSpacing / 2, this._forkOffsetY, posZ)\n this._extRightMesh.position.set(+bladeSpacing / 2, this._forkOffsetY, posZ)\n this._extLeftMesh.visible = visible\n this._extRightMesh.visible = visible\n\n // _bladeMidZ = carrier 의 Z 위치 = sign * absExt (= fork extension 만큼 직접).\n // ext=0 → 0 (carriage 정중앙 — retract 끝 자세)\n // ext=L → ±L (fork 가 L 만큼 진출한 위치 = carrier 도 그 위치)\n // 단순 linear — solveForkExtensionForLocalZ 의 inverse 도 단순 (ext = |localZ|).\n this._bladeMidZ = sign * absExt\n\n // _forkGroup 의 child carrier 의 Z 도 동기 — fork tip 위치 따라 carrier 가\n // 함께 끌려와야 retract 시각 자연.\n if (this._forkGroup) {\n for (const child of this._forkGroup.children) {\n const ctx = (child as any).userData?.context\n if (ctx && ctx !== this && ctx instanceof RealObject) {\n child.position.z = this._bladeMidZ\n }\n }\n }\n\n }\n\n updateAlpha() {}\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-3d.js","sourceRoot":"","sources":["../src/crane-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Crane 3D — heavy-duty pallet AS/RS stacker crane (twin-mast).\n *\n * Axis convention (things-scene 3D):\n * +X = aisle 진행 방향 (Crane 좌우 이동, *옆으로 길게* 축)\n * +Y = 수직 (Crane 키, carriage 상하 이동)\n * +Z = aisle 폭 방향 (Rack cell 쪽 / fork 신축 방향)\n *\n * **스케일 룰**:\n * - 모든 가로(X,Z) dimension 과 Y 두께 → 2D footprint (state.width, state.height) 비례\n * - Mast Y 길이만 → state.depth 사용 (수직 키)\n * - Y 두께는 절대 depth (크레인 키) 에 비례 안 함 → 키워도 뭉치 안 두꺼워짐\n * - mm/cm/scene-unit 무관 — 사용자의 footprint 스케일에 자동 맞춤\n *\n * 부품:\n * [Ceiling rail]\n * [Top guide trolley] — 천장 rail 위\n * [Top frame] — 두 mast 상부 연결\n * [Mast L][Mast R] — twin yellow column (X 축으로 떨어져 있음)\n * [Carriage] — 두 mast 사이 가로 frame (carriageHeight 로 상하)\n * └── [Two forks] — carriage 에서 ±Z 신축 (forkExtension)\n * [Base trolley] — 바닥 motor housing (X 길게)\n * └── [Cabinet] — base 한쪽 red 컨트롤박스\n * └── [Status lamp]\n * [Floor rail]\n *\n * Actuators (state):\n * carriageHeight, forkExtension (±), forkLift\n */\n\nimport * as THREE from 'three'\nimport { RealObject, RealObjectGroup } from '@hatiolab/things-scene'\n\nconst MAST_COLOR = 0xff7a00 // mast — orange\nconst TROLLEY_COLOR = 0x3a4048 // base / top — dark charcoal\nconst CARRIAGE_COLOR = 0xffcc00 // carriage (shuttle) — yellow\nconst CONTROLLER_COLOR = 0xc63333 // cabinet — red\nconst FORK_COLOR = 0xffcc00 // fork — yellow (same as carriage)\nconst RAIL_COLOR = 0x1a1f24 // rail — dark steel\nconst LAMP_OFF = 0x222222\n\nexport class Crane3D extends RealObjectGroup {\n /**\n * carriageHeight 의 실제 최댓값 — *mast 의 carriage 이동 가능 범위* 기반.\n * mastH = D - railH * 2 - baseH - topFrameH - topGuideH (mast 의 실제 길이).\n * carriage 의 *top edge 가 mast top 안에 머물도록* carriageH/2 추가 차감.\n *\n * 이전 D * 0.85 magic 대체 — base/top frame 의 실제 차지 비율과 무관한 추정값\n * 이라 crane.depth = rack.depth 동일 모델에서도 carriage 가 rack 상위 cell\n * 도달 못 함. 정확한 mastH 사용으로 *동일 size 모델에서 정상 도달* 보장.\n */\n static _maxCarriageHeight(D: number, width: number, height: number): number {\n const S = Math.min(width, height)\n const railH = S * 0.04\n const baseH = S * 0.18\n const topFrameH = S * 0.1\n const topGuideH = S * 0.1\n const carriageH = S * 0.12\n const mastH = Math.max(D - railH * 2 - baseH - topFrameH - topGuideH, S * 0.5)\n return Math.max(0, mastH - carriageH / 2)\n }\n\n private _forkGroup?: THREE.Group\n private _carrierBaseY: number = 0\n private _bladeMidZ: number = 0\n /** floor rail 만 제외한 나머지 (trolley + masts + carriage + fork) 의 movable parent. */\n private _trolleyGroup?: THREE.Group\n /** carriage + fork 의 lift parent (carriageHeight + forkLiftRT 변경 시 Y 만 update). */\n private _carriageLiftGroup?: THREE.Group\n /** Fork active extension mesh — scale.z 와 position.z 로 lerp (rebuild 없이). */\n private _extLeftMesh?: THREE.Mesh\n private _extRightMesh?: THREE.Mesh\n private _extBaseParams?: { bladeSpacing: number; carriageZ: number; stubL: number }\n /** Fork mesh 의 group-local Y center (carriage 위 + bladeH/2). _applyForkExtensionMeshes 가 ext mesh Y 결정. */\n private _forkOffsetY: number = 0\n /** liftGroup.position.y 재계산용 base parameters. */\n private _liftBaseParams?: { baseTrolleyY: number; baseH: number; carriageH: number; bladeH: number }\n\n build() {\n super.build()\n\n this._forkGroup = undefined\n this._carrierBaseY = 0\n this._bladeMidZ = 0\n this._trolleyGroup = undefined\n this._carriageLiftGroup = undefined\n this._extLeftMesh = undefined\n this._extRightMesh = undefined\n this._extBaseParams = undefined\n this._liftBaseParams = undefined\n\n const { width, height, depth } = this.component.state\n const emissiveColor = (this.component.state.lampEmissive as string) || '#222222'\n const status = this.component.state.status\n const lampOn = status && status !== 'idle'\n\n // Actuators\n const D = numOr(depth, Math.max(width, height) * 4)\n const carriageRaw = numOr((this.component.state as any).carriageHeight, (this.component as any)._canonicalDefault?.('carriageHeight') ?? D * 0.4)\n // carriageHeight clamp — 실제 mast 의 carriage 이동 가능 범위 기반. helper 사용\n // (render + onchange partial update 둘 다 동일 식). 이전엔 D * 0.85 magic 사용 —\n // base/top frame 의 실제 차지 비율과 무관한 추정. crane.depth = rack.depth 동일\n // 모델에서도 carriage 가 rack 상위 cell 도달 불가 → 14 levels rack 의 *마지막\n // shelf 만 한 층 아래에서 포킹* 증상의 root cause 였음.\n const carriageHeight = Math.max(0, Math.min(carriageRaw, Crane3D._maxCarriageHeight(D, width, height)))\n\n const forkLength = numOr((this.component.state as any).forkLength, height * 0.6)\n const forkExtensionRaw = numOr((this.component.state as any).forkExtension, 0)\n const forkExtension = Math.max(-forkLength, Math.min(forkLength, forkExtensionRaw))\n // forkLiftRT — 시뮬 runtime current 들림. state.forkLift 는 *configured 진폭*\n // (사용자 설정) 라 안 건드림. 3D carriage Y 는 runtime 값 사용.\n const forkLift = numOr((this.component.state as any).forkLiftRT, 0)\n\n // ── Axis convention (FIXED): ─────────────────────────────────────\n // Rail = X (state.left = 2D X = 3D X). Crane 좌우 이동.\n // Fork = Z (2D Y = 3D Z). Fork 앞뒤 신축.\n // 사용자: \"포크는 앞뒤로, 크레인 본체는 좌우로.\"\n // width = rail-direction 풋프린트 (좁음)\n // height = cross-aisle (fork 방향) 풋프린트 (깊음, fork 가 cell 까지 닿는 거리)\n const S = Math.min(width, height)\n const railH = S * 0.04\n const baseH = S * 0.18\n const topFrameH = S * 0.1\n const topGuideH = S * 0.1\n const carriageH = S * 0.12\n // Carriage assembly 크기 — state.carriageWidth 기반 (rail 길이 = crane.width\n // 와 독립). 미명시 시 rail 의 10%.\n const carriageAssemblyW = numOr((this.component.state as any).carriageWidth, width * 0.1)\n const mastW = carriageAssemblyW * 0.15 // mast X 단면 (along rail)\n const mastD = height * 0.25 // mast Z 단면 (cross-rail)\n const mastSpacing = carriageAssemblyW * 0.85 // 두 mast X 간격\n const bladeW = S * 0.1\n // bladeH — fork 두께. carriage 보다 얇게 (실 fork prong 의 가는 모양).\n // carriage 와 *같은 Y center (0)* 이고 두께만 carriageH * 0.35.\n const bladeH = carriageH * 0.35\n const bladeL = forkLength\n const bladeSpacing = mastSpacing * 0.45\n const carriageW = mastSpacing - mastW * 0.2\n const carriageZ = height * 0.55\n // Cabinet — 존재감만. 작게.\n const cabW = S * 0.18\n const cabH = S * 0.18\n const cabD = S * 0.15\n\n const baseY = -D / 2\n const mastH = Math.max(D - railH * 2 - baseH - topFrameH - topGuideH, S * 0.5)\n\n // ── Materials ─────────────────────────────────────────────────────\n const mastMat = new THREE.MeshStandardMaterial({ color: MAST_COLOR, metalness: 0.3, roughness: 0.5 })\n const trolleyMat = new THREE.MeshStandardMaterial({ color: TROLLEY_COLOR, metalness: 0.6, roughness: 0.5 })\n const carriageMat = new THREE.MeshStandardMaterial({ color: CARRIAGE_COLOR, metalness: 0.85, roughness: 0.35 })\n const cabinetMat = new THREE.MeshStandardMaterial({ color: CONTROLLER_COLOR, metalness: 0.2, roughness: 0.6 })\n const forkMat = new THREE.MeshStandardMaterial({ color: FORK_COLOR, metalness: 0.85, roughness: 0.3 })\n const railMat = new THREE.MeshStandardMaterial({ color: RAIL_COLOR, metalness: 0.9, roughness: 0.3 })\n\n // ── Floor rail (고정 — crane 본체 안 움직임). 폭 = crane.width (overhang 제거).\n {\n const railThin = railH * 0.5\n const geo = new THREE.BoxGeometry(width, railThin, height * 0.15)\n const mesh = new THREE.Mesh(geo, railMat)\n mesh.position.set(0, baseY + railThin / 2, 0)\n mesh.receiveShadow = true\n this.object3d.add(mesh)\n }\n\n // ── Trolley group — carriage assembly (rail 위 X 만 이동) ─────────\n // 모든 movable 부품 (base trolley, masts, carriage, fork, cabinet, lamp)\n // 의 parent. carriagePosition 변경 시 group 의 local X 만 변경.\n const trolleyGroup = new THREE.Group()\n this._trolleyGroup = trolleyGroup\n this.object3d.add(trolleyGroup)\n // 초기 carriagePosition 적용 — rail-local X (0 ~ width) → object3d-local X (-W/2 ~ +W/2)\n const carriagePos = numOr((this.component.state as any).carriagePosition, (this.component as any)._canonicalDefault?.('carriagePosition') ?? width / 2)\n trolleyGroup.position.x = carriagePos - width / 2\n\n // ── Base trolley ──────────────────────────────────────────────────\n // Cabinet 이 mast 바깥쪽에 자연스럽게 놓이도록 *trolley 폭을 mast + cabinet\n // padding 까지 확장*. carriage assembly width 보다 양 옆으로 (cabW + gap) × 2.\n const trolleyPad = cabW + S * 0.04\n const baseTrolleyW = carriageAssemblyW + trolleyPad * 2\n const baseTrolleyY = baseY + railH + baseH / 2\n {\n const geo = new THREE.BoxGeometry(baseTrolleyW, baseH, height * 0.7)\n const mesh = new THREE.Mesh(geo, trolleyMat)\n mesh.position.set(0, baseTrolleyY, 0)\n mesh.castShadow = true\n mesh.receiveShadow = true\n trolleyGroup.add(mesh)\n }\n\n // ── Control cabinet — mast 바깥쪽 (trolley 의 *확장 padding 영역* 위) ─────\n {\n const geo = new THREE.BoxGeometry(cabW, cabH, cabD)\n const cab = new THREE.Mesh(geo, cabinetMat)\n cab.position.set(\n -(carriageAssemblyW / 2 + cabW / 2 + S * 0.02), // mast 왼쪽 바깥\n baseTrolleyY + baseH / 2 + cabH / 2,\n -height * 0.25 + cabD / 2\n )\n cab.castShadow = true\n trolleyGroup.add(cab)\n }\n\n // ── Status lamp ───────────────────────────────────────────────────\n {\n const lampR = S * 0.04\n const lampH = S * 0.1\n const lampMat = new THREE.MeshStandardMaterial({\n color: lampOn ? emissiveColor : LAMP_OFF,\n emissive: lampOn ? emissiveColor : LAMP_OFF,\n emissiveIntensity: lampOn ? 1.5 : 0.2,\n metalness: 0,\n roughness: 0.3\n })\n const geo = new THREE.CylinderGeometry(lampR, lampR * 0.8, lampH, 12)\n const lamp = new THREE.Mesh(geo, lampMat)\n lamp.position.set(carriageAssemblyW / 2 + lampR * 1.5 + S * 0.02, baseTrolleyY + baseH / 2 + lampH / 2, 0)\n trolleyGroup.add(lamp)\n }\n\n // ── Twin masts ────────────────────────────────────────────────────\n const mastY = baseTrolleyY + baseH / 2 + mastH / 2\n for (const xOff of [-mastSpacing / 2, +mastSpacing / 2]) {\n const geo = new THREE.BoxGeometry(mastW, mastH, mastD)\n const mesh = new THREE.Mesh(geo, mastMat)\n mesh.position.set(xOff, mastY, 0)\n mesh.castShadow = true\n mesh.receiveShadow = true\n trolleyGroup.add(mesh)\n }\n\n // ── Carriage + Fork lift group (carriageHeight + forkLiftRT 따라 Y 이동) ─\n // _carriageLiftGroup 안에 carriage + forkGroup. forkLiftRT / carriageHeight\n // 변경 시 *그룹 Y 만 update* (mesh rebuild X). _forkGroup 의 child carrier 가\n // *dispose 없이 그대로* 함께 따라 움직임.\n const stubL = Math.min(carriageZ * 0.2, Math.max(bladeL * 0.05, 6))\n const liftGroup = new THREE.Group()\n this._carriageLiftGroup = liftGroup\n this._liftBaseParams = { baseTrolleyY, baseH, carriageH, bladeH }\n liftGroup.position.set(0, this._computeLiftGroupY(carriageHeight, forkLift), 0)\n trolleyGroup.add(liftGroup)\n\n // Carriage — liftGroup local center\n {\n const geo = new THREE.BoxGeometry(carriageW, carriageH, carriageZ)\n const mesh = new THREE.Mesh(geo, carriageMat)\n mesh.position.set(0, 0, 0)\n mesh.castShadow = true\n mesh.receiveShadow = true\n liftGroup.add(mesh)\n }\n\n // ── Two-prong forks ───────────────────────────────────────────────\n // stub (4 box, fixed) + active extension (2 box, scale.z 로 lerp).\n // active mesh 는 *unit-length* 로 생성. _applyForkExtension 이 scale.z 와\n // position.z 로 길이/방향 update — rebuild 없이 매 frame 부드러운 변형.\n const absExt = Math.abs(forkExtension)\n const sign = forkExtension >= 0 ? 1 : -1\n {\n const group = new THREE.Group()\n // _forkGroup 은 liftGroup-local center (0,0,0) — frame 일치 단순화.\n // attach localPosition (carrier 자식) 도 *group-local = liftGroup-local* 동일 frame.\n // Fork mesh 자체가 group-local 안에서 carriage *위* 로 (mesh.position.y).\n group.position.set(0, 0, 0)\n\n // Fork mesh 가 carriage 와 *수평 (같은 Y 평면)* — carriage 안에 embed.\n // fork mesh group-local Y center = 0 (carriage center 와 같음).\n // fork blade *bottom* 면 group-local Y = -bladeH/2.\n // fork blade *top* 면 group-local Y = +bladeH/2.\n const forkOffsetY = 0\n\n // 양옆 stub — 두 prong × 두 측면 = 4 box\n const stubGeo = new THREE.BoxGeometry(bladeW, bladeH, stubL)\n for (const xOff of [-bladeSpacing / 2, +bladeSpacing / 2]) {\n for (const zSide of [-1, +1]) {\n const mesh = new THREE.Mesh(stubGeo, forkMat)\n mesh.position.set(xOff, forkOffsetY, zSide * (carriageZ / 2 + stubL / 2))\n mesh.castShadow = true\n mesh.receiveShadow = true\n group.add(mesh)\n }\n }\n\n // Active extension — unit length, scale.z + position.z 로 변형\n const extGeo = new THREE.BoxGeometry(bladeW, bladeH, 1)\n const extLeft = new THREE.Mesh(extGeo, forkMat)\n const extRight = new THREE.Mesh(extGeo, forkMat)\n this._forkOffsetY = forkOffsetY\n extLeft.castShadow = true\n extLeft.receiveShadow = true\n extRight.castShadow = true\n extRight.receiveShadow = true\n group.add(extLeft)\n group.add(extRight)\n this._extLeftMesh = extLeft\n this._extRightMesh = extRight\n this._extBaseParams = { bladeSpacing, carriageZ, stubL }\n this._applyForkExtensionMeshes(absExt, sign)\n\n // carrier 의 초기 Z = sign * absExt (= _applyForkExtensionMeshes 의 공식 동일).\n const carrierZ = sign * absExt\n\n liftGroup.add(group)\n this._forkGroup = group\n // Carrier 외부 bottom 정렬점 (liftGroup-local Y) = fork blade *bottom* =\n // -bladeH/2 (fork mesh group-local center = 0, 두께 bladeH).\n // 사용자 모델: \"fork 의 아랫면 ≈ carrier 의 아랫면\" — fork blade 가 carrier\n // 의 bottom 부분 안으로 *찔러 들어감* (겹친 자세).\n this._carrierBaseY = -bladeH / 2\n this._bladeMidZ = carrierZ\n }\n\n // ── Top frame (connects mast tops) — trolley 함께 이동 ─────────────\n const topFrameY = mastY + mastH / 2 + topFrameH / 2\n {\n const geo = new THREE.BoxGeometry(mastSpacing + mastW, topFrameH, height * 0.35)\n const mesh = new THREE.Mesh(geo, trolleyMat)\n mesh.position.set(0, topFrameY, 0)\n mesh.castShadow = true\n trolleyGroup.add(mesh)\n }\n\n // ── Top guide trolley — trolley 함께 이동 (ceiling rail 위 미끄러짐) ─\n const topGuideY = topFrameY + topFrameH / 2 + topGuideH / 2\n {\n const geo = new THREE.BoxGeometry(mastSpacing + mastW * 2, topGuideH, height * 0.3)\n const mesh = new THREE.Mesh(geo, trolleyMat)\n mesh.position.set(0, topGuideY, 0)\n mesh.castShadow = true\n trolleyGroup.add(mesh)\n }\n\n // Ceiling rail 생략 — 상단은 top guide trolley 만으로 충분. 사용자 의도.\n\n }\n\n getCarriageFrame(): THREE.Object3D | undefined {\n // Fallback chain — carrier 가 *carriage transform (X 이동, Y lift)* 따라오도록.\n // _forkGroup 미존재 시 _carriageLiftGroup (X+Y 따라옴) → _trolleyGroup (X 만) →\n // root (no follow). 절대 root 로 떨어지지 않도록 lift/trolley 우선.\n return this._forkGroup ?? this._carriageLiftGroup ?? this._trolleyGroup ?? this.object3d\n }\n\n /**\n * Fork blade *bottom* 의 liftGroup-local Y. *carrier 외부 bottom 정렬점*.\n *\n * 모델: carrier 의 외부 bottom 과 fork blade 의 bottom 이 *거의 일치*. fork 가\n * carrier 의 bottom 부분을 *찔러 들어가* carrier 와 *겹친 자세* (pallet pocket\n * 안 fork 진입). attachPointFor 가 `carrierBaseY + carrier.depth/2` 로 carrier\n * center 를 정렬 → carrier bottom = fork blade bottom.\n */\n get carrierBaseY(): number {\n return this._carrierBaseY\n }\n\n get bladeMidZ(): number {\n return this._bladeMidZ\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n // carriagePosition — trolleyGroup.position.x (mesh-level update).\n if ('carriagePosition' in after && this._trolleyGroup) {\n const W = numOr(this.component.state.width, 100)\n const pos = numOr(after.carriagePosition, W / 2)\n this._trolleyGroup.position.x = pos - W / 2\n }\n\n // Mesh-level updates — fork extension / lift / carriage height. *rebuild 없이*\n // mesh 의 scale / position 만 변경. _forkGroup 의 child carrier 가 dispose\n // 없이 그대로 따라 움직임 (fork 작업 시 시각 자연스러움).\n //\n // status / bodyColor / lampEmissive 는 *cosmetic* — full rebuild 회피. 별도\n // 처리 없음 시 status 변경 시 carrier dispose → 사라짐 결함의 원인. 향후\n // material color 만 update 하는 path 추가 가능.\n const needsFullRebuild =\n 'width' in after ||\n 'height' in after ||\n 'depth' in after ||\n 'forkLength' in after ||\n 'carriageWidth' in after\n\n if (!needsFullRebuild) {\n let meshUpdated = false\n if (('carriageHeight' in after || 'forkLiftRT' in after) && this._carriageLiftGroup) {\n const state = this.component.state as any\n const W = numOr(state.width, 100)\n const H = numOr(state.height, 100)\n const D = numOr(state.depth, Math.max(W, H) * 4)\n const carriageRaw = numOr(state.carriageHeight, D * 0.4)\n const carriageHeight = Math.max(0, Math.min(carriageRaw, Crane3D._maxCarriageHeight(D, W, H)))\n const forkLift = numOr(state.forkLiftRT, 0)\n this._carriageLiftGroup.position.y = this._computeLiftGroupY(carriageHeight, forkLift)\n meshUpdated = true\n }\n if ('forkExtension' in after && this._extLeftMesh && this._extRightMesh) {\n const state = this.component.state as any\n const forkLength = numOr(state.forkLength, numOr(state.height, 100) * 0.6)\n const forkExtensionRaw = numOr(state.forkExtension, 0)\n const forkExtension = Math.max(-forkLength, Math.min(forkLength, forkExtensionRaw))\n const absExt = Math.abs(forkExtension)\n const sign = forkExtension >= 0 ? 1 : -1\n this._applyForkExtensionMeshes(absExt, sign)\n meshUpdated = true\n }\n if (meshUpdated) return\n }\n\n if (needsFullRebuild) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n /** carriageHeight + forkLiftRT 를 liftGroup.position.y 로 변환. */\n private _computeLiftGroupY(carriageHeight: number, forkLift: number): number {\n const p = this._liftBaseParams\n if (!p) return 0\n return p.baseTrolleyY + p.baseH / 2 + carriageHeight + forkLift + p.carriageH / 2\n }\n\n /**\n * Carrier 외부 bottom 의 world Y → carriageHeight state 값 inverse-solve.\n *\n * Forward 공식 (build):\n * liftGroup.y crane-local = baseTrolleyY + baseH/2 + carriageHeight + forkLift + carriageH/2\n * carrier 외부 bottom crane-local = liftGroup.y + carrierBaseY (= -bladeH/2)\n * = baseTrolleyY + baseH/2 + carriageH/2 - bladeH/2 + carriageHeight + forkLift\n *\n * Inverse:\n * carriageHeight = worldY − craneCenterY − (baseTrolleyY + baseH/2 + carriageH/2 − bladeH/2) − forkLift\n */\n solveCarriageHeightForCarrierBaseWorldY(worldY: number, forkLift: number = 0): number {\n const p = this._liftBaseParams\n if (!p) return 0\n this.object3d.updateWorldMatrix(true, false)\n const v = new THREE.Vector3()\n this.object3d.matrixWorld.decompose(v, new THREE.Quaternion(), new THREE.Vector3())\n const craneCenterWorldY = v.y\n return worldY - craneCenterWorldY - (p.baseTrolleyY + p.baseH / 2 + p.carriageH / 2 - p.bladeH / 2) - forkLift\n }\n\n /**\n * target 의 *crane-local Z* → fork extension 값 inverse-solve.\n *\n * Forward (_applyForkExtensionMeshes): `_bladeMidZ = sign * absExt`\n * (carrier 가 ext 만큼 fork 따라 진출). Inverse: `ext = |localZ|, sign = sign(localZ)`.\n *\n * forkLength 로 clamp — localZ 가 forkLength 보다 멀면 carrier 가 fork tip 까지만.\n */\n solveForkExtensionForLocalZ(localZ: number): number {\n const sign = localZ >= 0 ? 1 : -1\n const state = this.component.state as any\n const forkLen = numOr(state.forkLength, numOr(state.height, 100) * 0.6)\n const clamped = Math.max(0, Math.min(forkLen, Math.abs(localZ)))\n return sign * clamped\n }\n\n /** Fork active extension mesh 의 scale.z + position.z + visibility update. */\n private _applyForkExtensionMeshes(absExt: number, sign: number) {\n if (!this._extLeftMesh || !this._extRightMesh || !this._extBaseParams) return\n const { bladeSpacing, carriageZ, stubL } = this._extBaseParams\n const visible = absExt > 0.5\n const len = Math.max(0.001, absExt)\n const posZ = sign * (carriageZ / 2 + stubL + absExt / 2)\n this._extLeftMesh.scale.z = len\n this._extRightMesh.scale.z = len\n this._extLeftMesh.position.set(-bladeSpacing / 2, this._forkOffsetY, posZ)\n this._extRightMesh.position.set(+bladeSpacing / 2, this._forkOffsetY, posZ)\n this._extLeftMesh.visible = visible\n this._extRightMesh.visible = visible\n\n // _bladeMidZ = carrier 의 Z 위치 = sign * absExt (= fork extension 만큼 직접).\n // ext=0 → 0 (carriage 정중앙 — retract 끝 자세)\n // ext=L → ±L (fork 가 L 만큼 진출한 위치 = carrier 도 그 위치)\n // 단순 linear — solveForkExtensionForLocalZ 의 inverse 도 단순 (ext = |localZ|).\n this._bladeMidZ = sign * absExt\n\n // _forkGroup 의 child carrier 의 Z 도 동기 — fork tip 위치 따라 carrier 가\n // 함께 끌려와야 retract 시각 자연.\n if (this._forkGroup) {\n for (const child of this._forkGroup.children) {\n const ctx = (child as any).userData?.context\n // `ctx instanceof RealObject` 는 *cross-module identity 실패* 가능\n // (Parcel3D 가 다른 things-scene 인스턴스의 RealObject 를 extend 한 경우\n // instanceof 가 false 반환). duck-type 으로 완화 — *crane 자신만 제외*.\n if (ctx && ctx !== this) {\n child.position.z = this._bladeMidZ\n }\n }\n }\n\n }\n\n updateAlpha() {}\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n"]}
|
package/dist/crane.d.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { Component, ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
2
|
import type { SlotDef, State, Material3D } from '@hatiolab/things-scene';
|
|
3
|
-
import { type AttachFrame, type Alignment, type Heights, type LegendBinding, type MoveOptions, type PlacementArchetype } from '@operato/scene-base';
|
|
3
|
+
import { type AttachFrame, type Alignment, type Heights, type LegendBinding, type MoveOptions, type PlacementArchetype, type SlottedHolder } from '@operato/scene-base';
|
|
4
|
+
import * as THREE from 'three';
|
|
4
5
|
import { Crane3D } from './crane-3d.js';
|
|
6
|
+
/**
|
|
7
|
+
* AdjacentSlot — crane 의 작업 reach 영역 안에 있는 slot 의 정보. simulate /
|
|
8
|
+
* 자동 transfer 의 source/dest 후보.
|
|
9
|
+
*/
|
|
10
|
+
export interface AdjacentSlot {
|
|
11
|
+
holder: SlottedHolder & {
|
|
12
|
+
state?: any;
|
|
13
|
+
};
|
|
14
|
+
slotId: string;
|
|
15
|
+
anchor: THREE.Object3D;
|
|
16
|
+
world: THREE.Vector3;
|
|
17
|
+
}
|
|
5
18
|
/**
|
|
6
19
|
* Crane status — the operating state of a stacker crane.
|
|
7
20
|
*
|
|
@@ -48,12 +61,23 @@ export interface CraneState extends State {
|
|
|
48
61
|
*/
|
|
49
62
|
forkLiftRT?: number;
|
|
50
63
|
/**
|
|
51
|
-
* 자동 random simulate
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
64
|
+
* 자동 random simulate 시작 여부. capability 기반 — crane.findAdjacentSlots()
|
|
65
|
+
* 으로 인접 SlottedHolder 발견 후 random source/dest transfer.
|
|
66
|
+
* - `undefined` 또는 `false` (default): 자동 시작 안 함. application 이 crane.
|
|
67
|
+
* pickAndPlace 등 직접 제어 시 사용.
|
|
68
|
+
* - `true`: added() 시 자동 시작 + state runtime toggle 가능.
|
|
55
69
|
*/
|
|
56
70
|
simulate?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* 1:N binding — 이 crane 이 *작업 대상으로 인지할 holder id 들*. 모델링 시
|
|
73
|
+
* 사용자가 명시. 명시 시 *findAdjacentSlots 가 그 list 의 holder 만 traverse*
|
|
74
|
+
* → broad scene scan 회피. 미명시 시 *scene 전체 SlottedHolder fallback*
|
|
75
|
+
* (BC, 기존 모델 호환).
|
|
76
|
+
*
|
|
77
|
+
* 입력 형식 — comma-separated id string ("rack-1, rack-2") 또는 array.
|
|
78
|
+
* editor 의 *id-list-input* widget 부재 시 string 으로.
|
|
79
|
+
*/
|
|
80
|
+
boundHolders?: string | string[];
|
|
57
81
|
/**
|
|
58
82
|
* Carriage 의 rail-local X 위치 (0 ~ crane.width). Crane.moveTo / simulate 가
|
|
59
83
|
* lerp. crane 본체 (rail) 는 안 움직임 — carriage assembly (masts + carriage +
|
|
@@ -65,6 +89,15 @@ export interface CraneState extends State {
|
|
|
65
89
|
* Target rack 의 cell 폭과 매칭 권유.
|
|
66
90
|
*/
|
|
67
91
|
carriageWidth?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Carriage 운동 속도 (scene units / sec). 각 crane 별로 다르게 설정 가능 — 같은
|
|
94
|
+
* 거리도 crane 마다 다른 시간. 미명시 시 default 250 u/s.
|
|
95
|
+
*
|
|
96
|
+
* Crane.moveTo 가 *carriage 의 X 및 Y 운동 거리* / speed 로 duration 계산 → 한 칸
|
|
97
|
+
* 이동 vs rail 끝~끝 이동 의 시간 자연 차이. 여러 crane 의 cycle 시간 분산 → 동시
|
|
98
|
+
* 동기화 회귀 차단.
|
|
99
|
+
*/
|
|
100
|
+
speed?: number;
|
|
68
101
|
material3d?: Material3D;
|
|
69
102
|
}
|
|
70
103
|
declare const Crane_base: any;
|
|
@@ -192,6 +225,66 @@ export default class Crane extends Crane_base {
|
|
|
192
225
|
fetch(carrier: Component, options?: MoveOptions): Promise<void>;
|
|
193
226
|
/** Deposit a carrier into a rack cell (semantically = place). */
|
|
194
227
|
deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void>;
|
|
228
|
+
/**
|
|
229
|
+
* scene 의 모든 SlottedHolder 의 *slot 중에서 crane 의 작업 reach 안에 들어
|
|
230
|
+
* 오는 slot 들* 을 반환. 자동 simulate / WCS-less 시나리오의 enumeration entry.
|
|
231
|
+
*
|
|
232
|
+
* 매칭 알고리즘 (공간 reach 박스):
|
|
233
|
+
* - crane.world center (axis-aligned 가정 — rotation 무시)
|
|
234
|
+
* - 각 slot.anchor.world position 이
|
|
235
|
+
* |x - cx| ≤ width/2 + reachXZ AND |z - cz| ≤ height/2 + reachXZ
|
|
236
|
+
* AND |y - cy| ≤ depth/2 + reachY
|
|
237
|
+
* 범위 안인가
|
|
238
|
+
* - reachXZ = `crane.state.height` (fork forward 폭). reachY = `crane.state.depth`/2.
|
|
239
|
+
*
|
|
240
|
+
* RackGrid 의 *isEmpty aisle 통로 자리에 crane 이 배치* 된 케이스도 자동 cover —
|
|
241
|
+
* 통로 양옆 bay 의 slot anchor world 가 reach 안에 자연히 들어옴.
|
|
242
|
+
*
|
|
243
|
+
* @param opts.includeSelf 자기 자신을 holder 로 포함할지 (default false — crane
|
|
244
|
+
* 은 SlottedHolder 아니지만 안전 차원).
|
|
245
|
+
*/
|
|
246
|
+
/**
|
|
247
|
+
* Cycle 단위 cache 없음 — **매 cycle 협의** (capability negotiation 의 본질).
|
|
248
|
+
* crane.carriagePosition 변경 / rack 추가-이동 / 동적 forkLength 등 *runtime
|
|
249
|
+
* 변화 대응*. cost 는 *slotWorldPosition (anchor 0 생성) + canReach (cheap
|
|
250
|
+
* 좌표 비교)* — 매 cycle 호출 부담 작음.
|
|
251
|
+
*
|
|
252
|
+
* 이 field 는 *_oneCycle 사용을 위한 *호출 당 임시 holder grouping** —
|
|
253
|
+
* findAdjacentSlots 결과의 byHolder mapping. caller (_oneCycle) 가 사용 후
|
|
254
|
+
* 폐기.
|
|
255
|
+
*/
|
|
256
|
+
private _adjacentByHolder?;
|
|
257
|
+
/** Cache 없음 — invalidate 호출도 no-op (호환 차원 keep). */
|
|
258
|
+
invalidateAdjacentSlots(): void;
|
|
259
|
+
/**
|
|
260
|
+
* 자동 detect 결과 cache — *boundHolders 미명시 시* 사용. crane / rack 위치가
|
|
261
|
+
* 정적이라 *1회 detect 후 영구 cache 안전*. 위치/회전/차원 변경 시 invalidate.
|
|
262
|
+
*/
|
|
263
|
+
private _autoBoundCache?;
|
|
264
|
+
/** Auto bound cache invalidate — crane state 변경 시 호출. */
|
|
265
|
+
invalidateBoundCache(): void;
|
|
266
|
+
/**
|
|
267
|
+
* Scene traverse + reach 검사 — 어느 slot 1개라도 reach 안인 holder 들 list.
|
|
268
|
+
* boundHolders 미명시 시 *runtime auto-detect* 결과. crane/rack 정적 위치
|
|
269
|
+
* 가정 — 1회 호출 후 cache.
|
|
270
|
+
*/
|
|
271
|
+
detectBoundHolders(): Array<SlottedHolder & {
|
|
272
|
+
state?: any;
|
|
273
|
+
}>;
|
|
274
|
+
/**
|
|
275
|
+
* Bound holder instance resolve.
|
|
276
|
+
* 1. boundHolders state 명시 → 그 id list resolve (explicit binding)
|
|
277
|
+
* 2. 미명시 → detectBoundHolders() 1회 + auto-cache (implicit binding,
|
|
278
|
+
* crane / rack 정적 위치 가정)
|
|
279
|
+
*
|
|
280
|
+
* 둘 다 *binding scope* — findAdjacentSlots 가 그 list 만 traverse.
|
|
281
|
+
*/
|
|
282
|
+
boundHolderInstances(): Array<SlottedHolder & {
|
|
283
|
+
state?: any;
|
|
284
|
+
}>;
|
|
285
|
+
findAdjacentSlots(opts?: {
|
|
286
|
+
includeSelf?: boolean;
|
|
287
|
+
}): AdjacentSlot[];
|
|
195
288
|
/**
|
|
196
289
|
* 2D top-down 표현 — 크레인으로 명확히 인식되도록 핵심 부품 그림:
|
|
197
290
|
*
|
|
@@ -235,8 +328,33 @@ export default class Crane extends Crane_base {
|
|
|
235
328
|
private _railMin;
|
|
236
329
|
private _railMax;
|
|
237
330
|
private _targetSide;
|
|
238
|
-
/**
|
|
331
|
+
/**
|
|
332
|
+
* Main loop — 통합 처리.
|
|
333
|
+
*
|
|
334
|
+
* 1. dispatcher 의 명시 작업 take → execute (priority 큰 작업 우선)
|
|
335
|
+
* 2. dispatcher 작업 없음 + state.simulate=true → 기존 random fallback (_oneCycle)
|
|
336
|
+
* 3. 둘 다 없음 → longer idle (dispatcher 작업 enqueue 대기)
|
|
337
|
+
*
|
|
338
|
+
* Phase Z 통합 — 사용자 application 의 `holder.putaway / picking` 호출이 즉시 자연
|
|
339
|
+
* 처리. simulate=true 시 큐 비면 random visual smoke 도 유지. 작업 사이 자연 idle.
|
|
340
|
+
*/
|
|
239
341
|
simulate(): Promise<void>;
|
|
342
|
+
/**
|
|
343
|
+
* Dispatcher 가 할당한 ticket 처리. lifecycle:
|
|
344
|
+
* assigned (이미 takeNextFor 가 set) → in_progress → completed / failed
|
|
345
|
+
*
|
|
346
|
+
* 흐름:
|
|
347
|
+
* 1. request.from / to 를 resolve (string id → SlotTarget, SlotTarget 그대로 등)
|
|
348
|
+
* 2. carrier obtain — request.carrier 명시 시 그것, 미명시 시 from.holder.obtainCarrier
|
|
349
|
+
* 3. pickAndPlace(carrier, dest)
|
|
350
|
+
* 4. ticket status notify
|
|
351
|
+
*
|
|
352
|
+
* 실패 처리:
|
|
353
|
+
* - target resolve 실패 → failed (resolve-target)
|
|
354
|
+
* - carrier obtain 실패 → failed (no-carrier)
|
|
355
|
+
* - pickAndPlace throw → failed (그 error)
|
|
356
|
+
*/
|
|
357
|
+
private _executeTicket;
|
|
240
358
|
/**
|
|
241
359
|
* state.target bbox 를 crane 의 *로컬 X 축* 으로 projection 해서 rail range 1회 계산.
|
|
242
360
|
* Rotation 적용된 crane 의 local X (= rail) 방향으로 움직이도록 cos/sin 캐시.
|
|
@@ -249,6 +367,18 @@ export default class Crane extends Crane_base {
|
|
|
249
367
|
* 로 잡아 GC 안 됨 + frameClock 콜백 살아남음.
|
|
250
368
|
*/
|
|
251
369
|
dispose(): void;
|
|
370
|
+
/**
|
|
371
|
+
* 1 cycle = *capability 기반 random transfer*.
|
|
372
|
+
*
|
|
373
|
+
* 1. findAdjacentSlots() — scene 의 인접 SlottedHolder 의 slot 들
|
|
374
|
+
* 2. occupied / empty 분리 — hasCarrierAt / canReceiveAt
|
|
375
|
+
* 3. random source / dest 선택 — 같은 slot 자가 transfer 회피
|
|
376
|
+
* 4. obtainCarrier + pickAndPlace — Mover.pickAndPlace 가 pick + place 진행
|
|
377
|
+
*
|
|
378
|
+
* 인접 slot / occupied / empty 어느 하나라도 비어있으면 짧은 delay 후 next.
|
|
379
|
+
* 진짜 carrier 가 fork 위 visible 로 이동 — 시각 simulate 가 *동시에 *진짜
|
|
380
|
+
* 데이타 전환* 도 수행 (Plan A: 데이타 → 실물 → 데이타 atomic 전환).
|
|
381
|
+
*/
|
|
252
382
|
private _oneCycle;
|
|
253
383
|
/**
|
|
254
384
|
* frameClock 기반 tween — things-scene 의 표준 `Component.animate()` 사용.
|