@operato/scene-storage 10.0.0-beta.33 → 10.0.0-beta.35
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 +23 -0
- package/dist/crane-3d.d.ts +14 -0
- package/dist/crane-3d.js +238 -0
- package/dist/crane-3d.js.map +1 -0
- package/dist/crane.d.ts +163 -0
- package/dist/crane.js +459 -0
- package/dist/crane.js.map +1 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/mobile-storage-rack.d.ts +17 -0
- package/dist/mobile-storage-rack.js +55 -0
- package/dist/mobile-storage-rack.js.map +1 -0
- package/dist/rack-column.d.ts +35 -0
- package/dist/rack-column.js +258 -0
- package/dist/rack-column.js.map +1 -0
- package/dist/rack-grid-3d.d.ts +13 -0
- package/dist/rack-grid-3d.js +94 -0
- package/dist/rack-grid-3d.js.map +1 -0
- package/dist/rack-grid-cell.d.ts +341 -0
- package/dist/rack-grid-cell.js +321 -0
- package/dist/rack-grid-cell.js.map +1 -0
- package/dist/rack-grid-helpers.d.ts +28 -0
- package/dist/rack-grid-helpers.js +71 -0
- package/dist/rack-grid-helpers.js.map +1 -0
- package/dist/rack-grid-location.d.ts +37 -0
- package/dist/rack-grid-location.js +227 -0
- package/dist/rack-grid-location.js.map +1 -0
- package/dist/rack-grid.d.ts +80 -0
- package/dist/rack-grid.js +829 -0
- package/dist/rack-grid.js.map +1 -0
- package/dist/stock.d.ts +78 -0
- package/dist/stock.js +333 -0
- package/dist/stock.js.map +1 -0
- package/dist/{rack-cell-3d.d.ts → storage-cell-3d.d.ts} +1 -1
- package/dist/{rack-cell-3d.js → storage-cell-3d.js} +3 -3
- package/dist/storage-cell-3d.js.map +1 -0
- package/dist/{rack-cell.d.ts → storage-cell.d.ts} +12 -6
- package/dist/{rack-cell.js → storage-cell.js} +9 -9
- package/dist/storage-cell.js.map +1 -0
- package/dist/{asrs-rack-3d.d.ts → storage-rack-3d.d.ts} +1 -1
- package/dist/{asrs-rack-3d.js → storage-rack-3d.js} +4 -4
- package/dist/storage-rack-3d.js.map +1 -0
- package/dist/{asrs-rack.d.ts → storage-rack.d.ts} +22 -16
- package/dist/{asrs-rack.js → storage-rack.js} +32 -26
- package/dist/storage-rack.js.map +1 -0
- package/dist/templates/index.d.ts +60 -0
- package/dist/templates/index.js +59 -17
- package/dist/templates/index.js.map +1 -1
- package/package.json +2 -2
- package/src/crane-3d.ts +273 -0
- package/src/crane.ts +555 -0
- package/src/index.ts +13 -6
- package/src/mobile-storage-rack.ts +56 -0
- package/src/rack-column.ts +340 -0
- package/src/rack-grid-3d.ts +128 -0
- package/src/rack-grid-cell.ts +404 -0
- package/src/rack-grid-helpers.ts +77 -0
- package/src/rack-grid-location.ts +286 -0
- package/src/rack-grid.ts +994 -0
- package/src/stock.ts +426 -0
- package/src/{rack-cell-3d.ts → storage-cell-3d.ts} +2 -2
- package/src/{rack-cell.ts → storage-cell.ts} +19 -13
- package/src/{asrs-rack-3d.ts → storage-rack-3d.ts} +3 -3
- package/src/{asrs-rack.ts → storage-rack.ts} +31 -25
- package/src/templates/index.ts +59 -17
- package/test/test-rack-grid-crane.ts +212 -0
- package/test/test-rack-grid.ts +77 -0
- package/test/{test-asrs-crane.ts → test-storage-rack-crane.ts} +8 -8
- package/translations/en.json +55 -7
- package/translations/ja.json +52 -4
- package/translations/ko.json +52 -4
- package/translations/ms.json +55 -7
- package/translations/zh.json +52 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/asrs-crane-3d.d.ts +0 -17
- package/dist/asrs-crane-3d.js +0 -181
- package/dist/asrs-crane-3d.js.map +0 -1
- package/dist/asrs-crane.d.ts +0 -98
- package/dist/asrs-crane.js +0 -216
- package/dist/asrs-crane.js.map +0 -1
- package/dist/asrs-rack-3d.js.map +0 -1
- package/dist/asrs-rack.js.map +0 -1
- package/dist/rack-cell-3d.js.map +0 -1
- package/dist/rack-cell.js.map +0 -1
- package/src/asrs-crane-3d.ts +0 -211
- package/src/asrs-crane.ts +0 -275
- /package/icons/{asrs-crane.png → crane.png} +0 -0
- /package/icons/{asrs-rack.png → storage-rack.png} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-cell-3d.js","sourceRoot":"","sources":["../src/storage-cell-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * RackCell 3D — invisible anchor group positioned at the cell's location\n * within the parent rack's 3D coordinate space.\n *\n * RackCell has no geometry of its own. Its sole 3D purpose is to provide\n * an Object3D that carriers can be attached to (via Three.js `.attach()`),\n * placed at the exact cell position within the rack. The position is derived\n * from the parent Rack's CellMap (by cellId), not from 2D state fields —\n * rack cells occupy 3D levels that have no 2D analogue.\n *\n * updateTransform() override: things-scene's standard updateTransform\n * reads `component.center` (2D) and flattens it to 3D. For rack cells this\n * is wrong — we need the 3D cell position (bay x, level y, row z) from the\n * parent rack. So we override and read it directly from the CellMap.\n */\n\nimport * as THREE from 'three'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nexport class StorageCell3D extends RealObjectGroup {\n build() {\n super.build()\n this._repositionFromCellMap()\n }\n\n updateDimension() {\n // intentional no-op — size comes from the cell definition, not state\n }\n\n updateTransform() {\n this._repositionFromCellMap()\n }\n\n updateAlpha() {\n // invisible — no materials to update\n }\n\n /**\n * Position this group at the cell's localPosition within the rack's 3D space.\n *\n * CellMap.grid() places cell origins at:\n * x = b * bayWidth, y = l * levelHeight, z = r * rowDepth\n * (starting from 0,0,0 at the rack's bottom-left-front corner).\n *\n * The rack's object3d is centered: X spans [-width/2, +width/2],\n * Y spans [-depth/2, +depth/2], Z spans [-height/2, +height/2].\n *\n * So the cell centre in rack-local 3D space:\n * x3d = cellPos.x + bayWidth/2 - width/2\n * y3d = cellPos.y + levelHeight/2 - rackDepth/2\n * z3d = cellPos.z + rowDepth/2 - rackHeight/2\n */\n _repositionFromCellMap() {\n const rack = (this.component as any).parent\n if (!rack?.cellMap) return\n\n const cellId = this.component.state.cellId as string | undefined\n if (!cellId) return\n\n const cell = rack.cellMap.findById(cellId)\n if (!cell) return\n\n const rs = rack.state as any\n const rackWidth = (rs?.width as number) || 1000\n const rackDepth = (rs?.depth as number) || 3000 // Y dimension (floor→ceiling)\n const rackHeight = (rs?.height as number) || 600 // Z dimension (front→back, 2D height)\n const bays = Math.max(1, Math.floor(rs?.bays || 5))\n const levels = Math.max(1, Math.floor(rs?.levels || 4))\n const rows = 1\n\n const bayWidth = rackWidth / bays\n const levelHeight = rackDepth / levels\n const rowDepth = rackHeight / rows\n\n const x3d = cell.localPosition.x + bayWidth / 2 - rackWidth / 2\n const y3d = cell.localPosition.y + levelHeight / 2 - rackDepth / 2\n const z3d = cell.localPosition.z + rowDepth / 2 - rackHeight / 2\n\n this.object3d.position.set(x3d, y3d, z3d)\n\n // Optionally visualise cells in debug mode (outline box, very faint)\n if (process.env.NODE_ENV !== 'production' && (rack.state as any)?.debugCells) {\n this._addDebugBox(bayWidth, levelHeight, rowDepth)\n }\n }\n\n private _debugBox?: THREE.LineSegments\n\n private _addDebugBox(w: number, h: number, d: number) {\n if (this._debugBox) return\n const geo = new THREE.BoxGeometry(w * 0.98, h * 0.98, d * 0.98)\n const edges = new THREE.EdgesGeometry(geo)\n this._debugBox = new THREE.LineSegments(\n edges,\n new THREE.LineBasicMaterial({ color: 0x00ff88, transparent: true, opacity: 0.2 })\n )\n this.object3d.add(this._debugBox)\n }\n}\n"]}
|
|
@@ -7,16 +7,22 @@ import { type AttachFrame } from '@operato/scene-base';
|
|
|
7
7
|
* - multi: small stack (up to 4, e.g. a multi-deep tray)
|
|
8
8
|
* - bulk: unlimited (e.g. a floor area measured in slots)
|
|
9
9
|
*/
|
|
10
|
-
export type
|
|
10
|
+
export type StorageCellType = 'single' | 'multi' | 'bulk';
|
|
11
11
|
/** RackCell 컴포넌트 state */
|
|
12
|
-
export interface
|
|
12
|
+
export interface StorageCellState extends State {
|
|
13
13
|
cellId?: string;
|
|
14
|
-
cellType?:
|
|
14
|
+
cellType?: StorageCellType;
|
|
15
|
+
/**
|
|
16
|
+
* 자동 할당된 location ID — RackTable.assignLocations() 가 set. 외부 시스템
|
|
17
|
+
* (WMS, picker 명령 등) 이 cell 을 지칭하는 사람-친화 ID. RackTable 없이
|
|
18
|
+
* 단독으로 Rack 을 사용할 땐 unset.
|
|
19
|
+
*/
|
|
20
|
+
locationId?: string;
|
|
15
21
|
material3d?: Material3D;
|
|
16
22
|
}
|
|
17
23
|
declare const RackCell_base: any;
|
|
18
24
|
/**
|
|
19
|
-
* RackCell — single-slot storage cell inside an
|
|
25
|
+
* RackCell — single-slot storage cell inside an Rack.
|
|
20
26
|
*
|
|
21
27
|
* Mixin chain: CarrierHolder(ContainerAbstract)
|
|
22
28
|
* - CarrierHolder: publishes attachPointFor(), gates containable() to Carriables
|
|
@@ -27,9 +33,9 @@ declare const RackCell_base: any;
|
|
|
27
33
|
* 2D→3D coordinate mapping which cannot express 3D levels.
|
|
28
34
|
*/
|
|
29
35
|
export default class RackCell extends RackCell_base {
|
|
30
|
-
state:
|
|
36
|
+
state: StorageCellState;
|
|
31
37
|
get cellId(): string;
|
|
32
|
-
get cellType():
|
|
38
|
+
get cellType(): StorageCellType;
|
|
33
39
|
/** Maximum carrier count for this cell based on cellType. */
|
|
34
40
|
get capacity(): number;
|
|
35
41
|
get nature(): ComponentNature;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
3
3
|
*
|
|
4
|
-
* RackCell — a single storage slot within an
|
|
4
|
+
* RackCell — a single storage slot within an Rack.
|
|
5
5
|
*
|
|
6
6
|
* A RackCell is a virtual component: it occupies a specific (bay, row, level)
|
|
7
7
|
* coordinate within the parent rack and acts as a CarrierHolder for one carrier
|
|
8
8
|
* (or several, depending on `cellType`).
|
|
9
9
|
*
|
|
10
|
-
* The crane (
|
|
10
|
+
* The crane (or any picker) navigates toward a RackCell as its `place()` destination —
|
|
11
11
|
* because the rack cell is a discrete component, Mover.moveTo() can target it
|
|
12
12
|
* directly and arrive at exactly the right bay × level position.
|
|
13
13
|
*
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* positioned within the rack's coordinate space (RackCell3D handles
|
|
17
17
|
* this via updateTransform override).
|
|
18
18
|
*
|
|
19
|
-
* Lifecycle:
|
|
19
|
+
* Lifecycle: Rack._buildCells() instantiates RackCell children.
|
|
20
20
|
* Do not create RackCell components manually — they are managed by the rack.
|
|
21
21
|
*
|
|
22
22
|
* Domain aliases:
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
import { __decorate } from "tslib";
|
|
27
27
|
import { ContainerAbstract, TRANSFER_SLOT_KEY, sceneComponent } from '@hatiolab/things-scene';
|
|
28
28
|
import { CarrierHolder } from '@operato/scene-base';
|
|
29
|
-
import {
|
|
29
|
+
import { StorageCell3D } from './storage-cell-3d.js';
|
|
30
30
|
const NATURE = {
|
|
31
31
|
mutable: false,
|
|
32
32
|
resizable: false,
|
|
@@ -51,10 +51,10 @@ const NATURE = {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
],
|
|
54
|
-
help: 'scene/component/
|
|
54
|
+
help: 'scene/component/storage-cell'
|
|
55
55
|
};
|
|
56
56
|
/**
|
|
57
|
-
* RackCell — single-slot storage cell inside an
|
|
57
|
+
* RackCell — single-slot storage cell inside an Rack.
|
|
58
58
|
*
|
|
59
59
|
* Mixin chain: CarrierHolder(ContainerAbstract)
|
|
60
60
|
* - CarrierHolder: publishes attachPointFor(), gates containable() to Carriables
|
|
@@ -178,11 +178,11 @@ let RackCell = class RackCell extends CarrierHolder(ContainerAbstract) {
|
|
|
178
178
|
}
|
|
179
179
|
// ── 3D ───────────────────────────────────────────────────────────────────
|
|
180
180
|
buildRealObject() {
|
|
181
|
-
return new
|
|
181
|
+
return new StorageCell3D(this);
|
|
182
182
|
}
|
|
183
183
|
};
|
|
184
184
|
RackCell = __decorate([
|
|
185
|
-
sceneComponent('
|
|
185
|
+
sceneComponent('storage-cell')
|
|
186
186
|
], RackCell);
|
|
187
187
|
export default RackCell;
|
|
188
188
|
function resolveCarrierDepth(c) {
|
|
@@ -194,4 +194,4 @@ function resolveCarrierDepth(c) {
|
|
|
194
194
|
function numOr(v, dflt) {
|
|
195
195
|
return typeof v === 'number' && Number.isFinite(v) ? v : dflt;
|
|
196
196
|
}
|
|
197
|
-
//# sourceMappingURL=
|
|
197
|
+
//# sourceMappingURL=storage-cell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-cell.js","sourceRoot":"","sources":["../src/storage-cell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;AAEH,OAAO,EAGL,iBAAiB,EAEjB,iBAAiB,EACjB,cAAc,EACf,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAA;AAErE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AA0BpD,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,KAAK;IAChB,SAAS,EAAE,KAAK;IAChB,UAAU,EAAE;QACV;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,YAAY;SAC1B;QACD;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE;gBACR,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;oBACtC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;oBACpC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;iBACnC;aACF;SACF;KACF;IACD,IAAI,EAAE,8BAA8B;CACrC,CAAA;AAED;;;;;;;;;;GAUG;AAEY,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,aAAa,CAAC,iBAAiB,CAAC;IAGpE,6EAA6E;IAE7E,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAA;IAChC,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAA;IACxC,CAAC;IAED,6DAA6D;IAC7D,IAAI,QAAQ;QACV,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtB,KAAK,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAA;YACvB,KAAK,OAAO,CAAC,CAAC,OAAO,CAAC,CAAA;YACtB,KAAK,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAA;QAC9B,CAAC;IACH,CAAC;IAED,6EAA6E;IAE7E,IAAI,MAAM;QACR,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,OAAO;QACT,OAAO,EAAE,CAAA;IACX,CAAC;IAED,6EAA6E;IAE7E,iEAAiE;IACjE,UAAU,CAAC,UAAgB;QACzB,MAAM,QAAQ,GAAI,IAAI,CAAC,UAAsC,EAAE,MAAM,IAAI,CAAC,CAAA;QAC1E,OAAO,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;IACjC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,OAAY,EAAE,UAAe,EAAE;QAC3C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE;gBAChC,IAAI,EAAE,mBAAmB;gBACzB,SAAS,EAAE,OAAO;gBAClB,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,SAAS;aAClB,CAAC,CAAA;YACF,OAAM;QACR,CAAC;QACD,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC,MAAM,CAAA;QACxC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAC/B,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE;YAChC,IAAI,EAAE,mBAAmB;YACzB,SAAS,EAAE,OAAO;YAClB,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAA;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,OAAY,EAAE,MAAW,EAAE,UAAe,EAAE;QACzD,IAAI,MAAM,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE;gBAChC,IAAI,EAAE,mBAAmB;gBACzB,SAAS,EAAE,OAAO;gBAClB,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,aAAa;aACtB,CAAC,CAAA;YACF,OAAM;QACR,CAAC;QACD,OAAO,OAAO,CAAC,iBAAiB,CAAC,CAAA;QACjC,IAAI,OAAO,MAAM,EAAE,OAAO,KAAK,UAAU,EAAE,CAAC;YAC1C,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACxC,CAAC;aAAM,CAAC;YACN,CAAC;YAAC,MAAc,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAC/C,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE;YAClC,IAAI,EAAE,qBAAqB;YAC3B,SAAS,EAAE,OAAO;YAClB,SAAS,EAAE,IAAI;YACf,MAAM;SACP,CAAC,CAAA;IACJ,CAAC;IAED,6EAA6E;IAE7E,mEAAmE;IACnE,KAAK,CAAC,OAAY,EAAE,OAAa;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACvC,CAAC;IAED,oEAAoE;IACpE,QAAQ,CAAC,OAAY,EAAE,MAAW,EAAE,OAAa;QAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;IAChD,CAAC;IAED,6EAA6E;IAE7E;;;;OAIG;IACH,cAAc,CAAC,OAAkB;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAA;QACvC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAA;QACtB,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;QACjD,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,YAAY,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;SACnD,CAAA;IACH,CAAC;IAED,6EAA6E;IAE7E,oEAAoE;IACpE,MAAM,CAAC,IAA8B;QACnC,oBAAoB;IACtB,CAAC;IAED,4EAA4E;IAE5E,eAAe;QACb,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAA;IAChC,CAAC;CACF,CAAA;AAtIoB,QAAQ;IAD5B,cAAc,CAAC,cAAc,CAAC;GACV,QAAQ,CAsI5B;eAtIoB,QAAQ;AAwI7B,SAAS,mBAAmB,CAAC,CAAY;IACvC,MAAM,GAAG,GAAI,CAAS,CAAC,WAAW,EAAE,cAAc,CAAA;IAClD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAA;IAC/D,OAAO,KAAK,CAAE,CAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;AAC3C,CAAC;AAED,SAAS,KAAK,CAAC,CAAU,EAAE,IAAY;IACrC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAC/D,CAAC","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * RackCell — a single storage slot within an Rack.\n *\n * A RackCell is a virtual component: it occupies a specific (bay, row, level)\n * coordinate within the parent rack and acts as a CarrierHolder for one carrier\n * (or several, depending on `cellType`).\n *\n * The crane (or any picker) navigates toward a RackCell as its `place()` destination —\n * because the rack cell is a discrete component, Mover.moveTo() can target it\n * directly and arrive at exactly the right bay × level position.\n *\n * Visual: invisible in 2D (no visible 2D footprint — rack cells don't make\n * sense as 2D top-down boxes). In 3D, each cell is an invisible Group\n * positioned within the rack's coordinate space (RackCell3D handles\n * this via updateTransform override).\n *\n * Lifecycle: Rack._buildCells() instantiates RackCell children.\n * Do not create RackCell components manually — they are managed by the rack.\n *\n * Domain aliases:\n * cell.store(carrier) ← cell.receive(carrier)\n * cell.retrieve(carrier, target) ← cell.dispatch(carrier, target)\n */\n\nimport {\n Component,\n ComponentNature,\n ContainerAbstract,\n RealObject,\n TRANSFER_SLOT_KEY,\n sceneComponent\n} from '@hatiolab/things-scene'\nimport type { State, Material3D } from '@hatiolab/things-scene'\nimport { CarrierHolder, type AttachFrame } from '@operato/scene-base'\n\nimport { StorageCell3D } from './storage-cell-3d.js'\n\n/**\n * How many carriers a cell can hold simultaneously.\n * - single: exactly 1 (typical pallet bay)\n * - multi: small stack (up to 4, e.g. a multi-deep tray)\n * - bulk: unlimited (e.g. a floor area measured in slots)\n */\nexport type StorageCellType = 'single' | 'multi' | 'bulk'\n\n/** RackCell 컴포넌트 state */\nexport interface StorageCellState extends State {\n // ── 식별 ──\n cellId?: string\n cellType?: StorageCellType\n /**\n * 자동 할당된 location ID — RackTable.assignLocations() 가 set. 외부 시스템\n * (WMS, picker 명령 등) 이 cell 을 지칭하는 사람-친화 ID. RackTable 없이\n * 단독으로 Rack 을 사용할 땐 unset.\n */\n locationId?: string\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: false,\n rotatable: false,\n properties: [\n {\n type: 'string',\n label: 'cell-id',\n name: 'cellId',\n placeholder: 'e.g. 0-0-0'\n },\n {\n type: 'select',\n label: 'cell-type',\n name: 'cellType',\n property: {\n options: [\n { display: 'Single', value: 'single' },\n { display: 'Multi', value: 'multi' },\n { display: 'Bulk', value: 'bulk' }\n ]\n }\n }\n ],\n help: 'scene/component/storage-cell'\n}\n\n/**\n * RackCell — single-slot storage cell inside an Rack.\n *\n * Mixin chain: CarrierHolder(ContainerAbstract)\n * - CarrierHolder: publishes attachPointFor(), gates containable() to Carriables\n * - ContainerAbstract: manages child carrier components\n *\n * No Placeable mixin — RackCell3D self-positions from the parent rack's\n * CellMap (via updateTransform override), bypassing things-scene's standard\n * 2D→3D coordinate mapping which cannot express 3D levels.\n */\n@sceneComponent('storage-cell')\nexport default class RackCell extends CarrierHolder(ContainerAbstract) {\n declare state: StorageCellState\n\n // ── Identification ────────────────────────────────────────────────────────\n\n get cellId(): string {\n return this.state.cellId ?? ''\n }\n\n get cellType(): StorageCellType {\n return this.state.cellType ?? 'single'\n }\n\n /** Maximum carrier count for this cell based on cellType. */\n get capacity(): number {\n switch (this.cellType) {\n case 'single': return 1\n case 'multi': return 4\n case 'bulk': return Infinity\n }\n }\n\n // ── Interface ─────────────────────────────────────────────────────────────\n\n get nature(): ComponentNature {\n return NATURE\n }\n\n get anchors(): [] {\n return []\n }\n\n // ── Transfer protocol ─────────────────────────────────────────────────────\n\n /** True when fewer carriers are currently held than capacity. */\n canReceive(_component?: any): boolean {\n const occupied = (this.components as Component[] | undefined)?.length ?? 0\n return occupied < this.capacity\n }\n\n /**\n * Accept a carrier into this cell.\n * Sets TRANSFER_SLOT_KEY = cellId on the carrier, then reparents.\n * Fires 'transfer-received' so monitors can react.\n */\n async receive(carrier: any, options: any = {}): Promise<void> {\n if (!this.canReceive(carrier)) {\n this.trigger('transfer-rejected', {\n type: 'transfer-rejected',\n component: carrier,\n container: this,\n reason: 'no-slot'\n })\n return\n }\n carrier[TRANSFER_SLOT_KEY] = this.cellId\n this.reparent(carrier, options)\n this.trigger('transfer-received', {\n type: 'transfer-received',\n component: carrier,\n container: this,\n slotId: this.cellId\n })\n }\n\n /**\n * Release a carrier from this cell to `target`.\n * Delegates to `target.receive()` if available, otherwise `target.reparent()`.\n */\n async dispatch(carrier: any, target: any, options: any = {}): Promise<void> {\n if (target?.canReceive && !target.canReceive(carrier)) {\n this.trigger('transfer-rejected', {\n type: 'transfer-rejected',\n component: carrier,\n container: this,\n reason: 'target-full'\n })\n return\n }\n delete carrier[TRANSFER_SLOT_KEY]\n if (typeof target?.receive === 'function') {\n await target.receive(carrier, options)\n } else {\n ;(target as any).reparent?.(carrier, options)\n }\n this.trigger('transfer-dispatched', {\n type: 'transfer-dispatched',\n component: carrier,\n container: this,\n target\n })\n }\n\n // ── Domain aliases ────────────────────────────────────────────────────────\n\n /** Alias for receive() — semantic sugar for the storage domain. */\n store(carrier: any, options?: any): Promise<void> {\n return this.receive(carrier, options)\n }\n\n /** Alias for dispatch() — semantic sugar for the storage domain. */\n retrieve(carrier: any, target: any, options?: any): Promise<void> {\n return this.dispatch(carrier, target, options)\n }\n\n // ── 3D attach frame ───────────────────────────────────────────────────────\n\n /**\n * Return the 3D attach frame for carriers placed in this cell.\n * Carriers are lifted by their own halfDepth so the bottom face\n * rests at the cell's Y-center (which is levelHeight/2 above the beam).\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const root = this._realObject?.object3d\n if (!root) return null\n const carrierDepth = resolveCarrierDepth(carrier)\n return {\n attach: root,\n localPosition: { x: 0, y: carrierDepth / 2, z: 0 }\n }\n }\n\n // ── 2D rendering ──────────────────────────────────────────────────────────\n\n /** RackCell has no 2D visual — the rack draws its own structure. */\n render(_ctx: CanvasRenderingContext2D) {\n // intentional no-op\n }\n\n // ── 3D ───────────────────────────────────────────────────────────────────\n\n buildRealObject(): RealObject | undefined {\n return new StorageCell3D(this)\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 numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RealObjectGroup } from '@hatiolab/things-scene';
|
|
2
|
-
export declare class
|
|
2
|
+
export declare class StorageRack3D extends RealObjectGroup {
|
|
3
3
|
build(): void;
|
|
4
4
|
updateDimension(): void;
|
|
5
5
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Rack 3D — multi-level storage shelf system.
|
|
5
5
|
*
|
|
6
6
|
* LO-POLY but visually unambiguous as a rack. The signature parts:
|
|
7
7
|
*
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* this is a load-bearing storage rack, not just a generic frame)
|
|
14
14
|
*
|
|
15
15
|
* No floor / ceiling panels — the rack is open by design (cells are accessed
|
|
16
|
-
* by
|
|
16
|
+
* by a picker from the front side).
|
|
17
17
|
*
|
|
18
18
|
* Cargo (pallets, boxes) added as children render at their own z position.
|
|
19
19
|
* The rack itself is purely structural geometry.
|
|
@@ -24,7 +24,7 @@ import { RealObjectGroup } from '@hatiolab/things-scene';
|
|
|
24
24
|
const POST_COLOR = 0x6a7080;
|
|
25
25
|
const BEAM_COLOR = 0x556070;
|
|
26
26
|
const BRACE_COLOR = 0x556070;
|
|
27
|
-
export class
|
|
27
|
+
export class StorageRack3D extends RealObjectGroup {
|
|
28
28
|
build() {
|
|
29
29
|
super.build();
|
|
30
30
|
const { width, height, depth = 3000 } = this.component.state;
|
|
@@ -126,4 +126,4 @@ export class AsrsRack3D extends RealObjectGroup {
|
|
|
126
126
|
}
|
|
127
127
|
updateAlpha() { }
|
|
128
128
|
}
|
|
129
|
-
//# sourceMappingURL=
|
|
129
|
+
//# sourceMappingURL=storage-rack-3d.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-rack-3d.js","sourceRoot":"","sources":["../src/storage-rack-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Rack 3D — multi-level storage shelf system.\n *\n * LO-POLY but visually unambiguous as a rack. The signature parts:\n *\n * - 4 corner uprights (vertical posts running floor → top)\n * - intermediate uprights between bays (one between each adjacent bay pair)\n * - horizontal beams at each level on both front and back faces (defining\n * the cell decks)\n * - diagonal cross-bracing on the back face (the \"X\" pattern that says\n * this is a load-bearing storage rack, not just a generic frame)\n *\n * No floor / ceiling panels — the rack is open by design (cells are accessed\n * by a picker from the front side).\n *\n * Cargo (pallets, boxes) added as children render at their own z position.\n * The rack itself is purely structural geometry.\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nconst POST_COLOR = 0x6a7080\nconst BEAM_COLOR = 0x556070\nconst BRACE_COLOR = 0x556070\n\nexport class StorageRack3D extends RealObjectGroup {\n build() {\n super.build()\n\n const { width, height, depth = 3000 } = this.component.state\n const levels = Math.max(1, Math.floor((this.component.state.levels as number) || 4))\n const bays = Math.max(1, Math.floor((this.component.state.bays as number) || 5))\n\n const baseY = -depth / 2\n const postW = Math.min(width / bays, height) * 0.06\n const beamH = depth * 0.025\n const braceT = postW * 0.6\n\n const postMaterial = new THREE.MeshStandardMaterial({\n color: POST_COLOR,\n metalness: 0.7,\n roughness: 0.4\n })\n const beamMaterial = new THREE.MeshStandardMaterial({\n color: BEAM_COLOR,\n metalness: 0.7,\n roughness: 0.4\n })\n const braceMaterial = new THREE.MeshStandardMaterial({\n color: BRACE_COLOR,\n metalness: 0.7,\n roughness: 0.4\n })\n\n // ── Uprights (vertical posts at every bay boundary) ──────────────\n // bays + 1 vertical positions; for each, one front post + one back post.\n const postGeos: THREE.BufferGeometry[] = []\n for (let i = 0; i <= bays; i++) {\n const xFrac = i / bays - 0.5\n const x = xFrac * width\n // Front + back posts\n for (const zSign of [-1, 1]) {\n const post = new THREE.BoxGeometry(postW, depth, postW)\n post.translate(x, 0, zSign * (height / 2 - postW / 2))\n postGeos.push(post)\n }\n }\n const postMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(postGeos), postMaterial)\n postMesh.castShadow = true\n postMesh.receiveShadow = true\n this.object3d.add(postMesh)\n\n // ── Horizontal beams (front + back faces at each level) ──────────\n // levels + 1 vertical positions (level 0 = ground, level N = top).\n const beamGeos: THREE.BufferGeometry[] = []\n for (let lv = 0; lv <= levels; lv++) {\n const yFrac = lv / levels\n const y = baseY + yFrac * depth - beamH / 2 + (lv === 0 ? beamH : 0)\n\n for (const zSign of [-1, 1]) {\n const beam = new THREE.BoxGeometry(width, beamH, beamH)\n beam.translate(0, y, zSign * (height / 2 - beamH / 2))\n beamGeos.push(beam)\n }\n }\n const beamMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(beamGeos), beamMaterial)\n beamMesh.castShadow = true\n beamMesh.receiveShadow = true\n this.object3d.add(beamMesh)\n\n // ── Diagonal cross-bracing on the back face (the \"X\" pattern) ────\n // Two diagonals per level — \"/\" and \"\\\" — making an X across each\n // bay-tall cell. Visual signature of a load-bearing rack.\n const braceGeos: THREE.BufferGeometry[] = []\n const cellW = width / bays\n const cellH = depth / levels\n const braceLen = Math.sqrt(cellW * cellW + cellH * cellH)\n const braceAngle = Math.atan2(cellH, cellW)\n const backZ = height / 2 - postW * 0.6\n\n for (let bay = 0; bay < bays; bay++) {\n // Brace only every other bay to keep things visually open\n if (bay % 2 !== 0) continue\n\n const cellCenterX = (bay - bays / 2 + 0.5) * cellW\n\n for (let lv = 0; lv < levels; lv++) {\n const cellCenterY = baseY + (lv + 0.5) * cellH\n\n for (const sign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(braceLen, braceT, braceT)\n brace.rotateZ(sign * braceAngle)\n brace.translate(cellCenterX, cellCenterY, backZ)\n braceGeos.push(brace)\n }\n }\n }\n if (braceGeos.length > 0) {\n const braceMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(braceGeos), braceMaterial)\n braceMesh.castShadow = true\n this.object3d.add(braceMesh)\n }\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'levels' in after ||\n 'bays' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
|
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
import { Component, ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
2
|
import type { State, Material3D } from '@hatiolab/things-scene';
|
|
3
3
|
import { CellMap, type AttachFrame, type Alignment, type Heights, type PlacementArchetype } from '@operato/scene-base';
|
|
4
|
-
/**
|
|
5
|
-
export interface
|
|
4
|
+
/** Rack 컴포넌트 state */
|
|
5
|
+
export interface StorageRackState extends State {
|
|
6
6
|
bays?: number;
|
|
7
7
|
levels?: number;
|
|
8
8
|
debugCells?: boolean;
|
|
9
9
|
material3d?: Material3D;
|
|
10
10
|
}
|
|
11
|
-
declare const
|
|
11
|
+
declare const Rack_base: any;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Rack — a multi-level storage shelf system. A *Storage* whose role is to hold
|
|
14
|
+
* carriers in a (bay × level) grid of cells.
|
|
15
15
|
*
|
|
16
16
|
* `levels` × `bays` cells form a vertical grid. Each cell holds one logistics
|
|
17
|
-
* package (Pallet / Box / Parcel). A
|
|
18
|
-
*
|
|
17
|
+
* package (Pallet / Box / Parcel). A picker (Crane / Forklift / robot arm)
|
|
18
|
+
* accesses individual cells via Phase G/H Pickable contract — the picker is
|
|
19
|
+
* Rack-agnostic, knowing only how to interact with a cell.
|
|
19
20
|
*
|
|
20
|
-
* **Monitoring mode** (default):
|
|
21
|
-
* placed by
|
|
21
|
+
* **Monitoring mode** (default): carriers are direct children of the rack,
|
|
22
|
+
* placed by external data binding. No RackCell children are created.
|
|
22
23
|
*
|
|
23
24
|
* **Simulation mode**: call `rack._buildCells()` after placing the rack on the
|
|
24
|
-
* scene. This creates RackCell children at the correct 3D positions.
|
|
25
|
-
*
|
|
25
|
+
* scene. This creates RackCell children at the correct 3D positions. A picker
|
|
26
|
+
* (Crane / Forklift / ...) then navigates to individual RackCells for
|
|
27
|
+
* pick-and-place.
|
|
26
28
|
*
|
|
27
29
|
* **Placement**: `floor` archetype, full ceiling depth by default.
|
|
30
|
+
*
|
|
31
|
+
* **Mobility**: this Rack is stationary. A `MobileRack = Mover(Rack)` mixin
|
|
32
|
+
* extension can be added later for AGV-mounted or cart-mounted variants —
|
|
33
|
+
* the cell topology and pickable contract stay the same.
|
|
28
34
|
*/
|
|
29
|
-
export default class
|
|
30
|
-
state:
|
|
35
|
+
export default class Rack extends Rack_base {
|
|
36
|
+
state: StorageRackState;
|
|
31
37
|
static placement: PlacementArchetype;
|
|
32
38
|
static align: Alignment;
|
|
33
39
|
static defaultDepth: (h: Heights) => number;
|
|
@@ -48,7 +54,7 @@ export default class AsrsRack extends AsrsRack_base {
|
|
|
48
54
|
* Create RackCell child components for each cell in the CellMap.
|
|
49
55
|
*
|
|
50
56
|
* Called explicitly to enter simulation mode — monitoring-mode racks
|
|
51
|
-
* never call this (
|
|
57
|
+
* never call this (carriers are direct children, no explicit cells).
|
|
52
58
|
*
|
|
53
59
|
* Idempotent: removes existing rack-cell children first.
|
|
54
60
|
*/
|
|
@@ -64,7 +70,7 @@ export default class AsrsRack extends AsrsRack_base {
|
|
|
64
70
|
containable(component: Component): boolean;
|
|
65
71
|
/**
|
|
66
72
|
* Attach frame for carriers that are DIRECT children of the rack
|
|
67
|
-
* (monitoring mode, where
|
|
73
|
+
* (monitoring mode, where carriers go directly into the rack without
|
|
68
74
|
* explicit RackCell components).
|
|
69
75
|
*
|
|
70
76
|
* In simulation mode, carriers become children of their RackCell,
|
|
@@ -75,7 +81,7 @@ export default class AsrsRack extends AsrsRack_base {
|
|
|
75
81
|
attachPointFor(_carrier: Component): AttachFrame | null;
|
|
76
82
|
/**
|
|
77
83
|
* 2D — top-down rectangle showing the rack footprint, with subdivisions
|
|
78
|
-
* suggesting the bay layout
|
|
84
|
+
* suggesting the bay layout.
|
|
79
85
|
*/
|
|
80
86
|
render(ctx: CanvasRenderingContext2D): void;
|
|
81
87
|
get fillStyle(): string;
|
|
@@ -4,7 +4,7 @@ import { __decorate } from "tslib";
|
|
|
4
4
|
*/
|
|
5
5
|
import { Component, ContainerAbstract, sceneComponent } from '@hatiolab/things-scene';
|
|
6
6
|
import { CellContainer, CellMap, CarrierHolder, Placeable } from '@operato/scene-base';
|
|
7
|
-
import {
|
|
7
|
+
import { StorageRack3D } from './storage-rack-3d.js';
|
|
8
8
|
const NATURE = {
|
|
9
9
|
mutable: false,
|
|
10
10
|
resizable: true,
|
|
@@ -23,11 +23,11 @@ const NATURE = {
|
|
|
23
23
|
placeholder: '# of horizontal bays (default 5)'
|
|
24
24
|
}
|
|
25
25
|
],
|
|
26
|
-
help: 'scene/component/
|
|
26
|
+
help: 'scene/component/rack'
|
|
27
27
|
};
|
|
28
28
|
// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),
|
|
29
29
|
// which forces `isHTMLElement(): true` and trips the 3D pipeline's
|
|
30
|
-
// addObject DOM-skip gate.
|
|
30
|
+
// addObject DOM-skip gate. Rack lives only in the 3D scene graph.
|
|
31
31
|
//
|
|
32
32
|
// Mixin chain: CellContainer → CarrierHolder → Placeable → ContainerAbstract
|
|
33
33
|
// CellContainer: cell topology (cellMap, cell(), findAvailableCell(), occupiedCellIds())
|
|
@@ -35,23 +35,29 @@ const NATURE = {
|
|
|
35
35
|
// Placeable: floor-archetype positioning
|
|
36
36
|
// ContainerAbstract: child component management
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
38
|
+
* Rack — a multi-level storage shelf system. A *Storage* whose role is to hold
|
|
39
|
+
* carriers in a (bay × level) grid of cells.
|
|
40
40
|
*
|
|
41
41
|
* `levels` × `bays` cells form a vertical grid. Each cell holds one logistics
|
|
42
|
-
* package (Pallet / Box / Parcel). A
|
|
43
|
-
*
|
|
42
|
+
* package (Pallet / Box / Parcel). A picker (Crane / Forklift / robot arm)
|
|
43
|
+
* accesses individual cells via Phase G/H Pickable contract — the picker is
|
|
44
|
+
* Rack-agnostic, knowing only how to interact with a cell.
|
|
44
45
|
*
|
|
45
|
-
* **Monitoring mode** (default):
|
|
46
|
-
* placed by
|
|
46
|
+
* **Monitoring mode** (default): carriers are direct children of the rack,
|
|
47
|
+
* placed by external data binding. No RackCell children are created.
|
|
47
48
|
*
|
|
48
49
|
* **Simulation mode**: call `rack._buildCells()` after placing the rack on the
|
|
49
|
-
* scene. This creates RackCell children at the correct 3D positions.
|
|
50
|
-
*
|
|
50
|
+
* scene. This creates RackCell children at the correct 3D positions. A picker
|
|
51
|
+
* (Crane / Forklift / ...) then navigates to individual RackCells for
|
|
52
|
+
* pick-and-place.
|
|
51
53
|
*
|
|
52
54
|
* **Placement**: `floor` archetype, full ceiling depth by default.
|
|
55
|
+
*
|
|
56
|
+
* **Mobility**: this Rack is stationary. A `MobileRack = Mover(Rack)` mixin
|
|
57
|
+
* extension can be added later for AGV-mounted or cart-mounted variants —
|
|
58
|
+
* the cell topology and pickable contract stay the same.
|
|
53
59
|
*/
|
|
54
|
-
let
|
|
60
|
+
let Rack = class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbstract))) {
|
|
55
61
|
static placement = 'floor';
|
|
56
62
|
static align = 'bottom';
|
|
57
63
|
static defaultDepth = (h) => h.ceiling - h.floor;
|
|
@@ -91,7 +97,7 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
91
97
|
* Create RackCell child components for each cell in the CellMap.
|
|
92
98
|
*
|
|
93
99
|
* Called explicitly to enter simulation mode — monitoring-mode racks
|
|
94
|
-
* never call this (
|
|
100
|
+
* never call this (carriers are direct children, no explicit cells).
|
|
95
101
|
*
|
|
96
102
|
* Idempotent: removes existing rack-cell children first.
|
|
97
103
|
*/
|
|
@@ -99,20 +105,20 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
99
105
|
// Remove existing rack-cell children
|
|
100
106
|
const existing = this.components ?? [];
|
|
101
107
|
for (const child of [...existing]) {
|
|
102
|
-
if (child.state?.type === '
|
|
108
|
+
if (child.state?.type === 'storage-cell') {
|
|
103
109
|
this.removeComponent(child);
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
// Create a RackCell for each cell in the map
|
|
107
|
-
const RackCellClass = Component.register('
|
|
113
|
+
const RackCellClass = Component.register('storage-cell');
|
|
108
114
|
if (!RackCellClass) {
|
|
109
|
-
console.warn('
|
|
115
|
+
console.warn('Rack._buildCells: rack-cell type not registered. Import rack-cell.ts first.');
|
|
110
116
|
return;
|
|
111
117
|
}
|
|
112
118
|
const context = this._app;
|
|
113
119
|
for (const cell of this.cellMap.cells) {
|
|
114
120
|
const model = {
|
|
115
|
-
type: '
|
|
121
|
+
type: 'storage-cell',
|
|
116
122
|
cellId: cell.id,
|
|
117
123
|
width: cell.size.width,
|
|
118
124
|
height: cell.size.depth, // 2D height = 3D Z depth
|
|
@@ -132,7 +138,7 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
132
138
|
* - Everything else (sensors, labels, etc. can be siblings of the rack, not children).
|
|
133
139
|
*/
|
|
134
140
|
containable(component) {
|
|
135
|
-
if (component.state?.type === '
|
|
141
|
+
if (component.state?.type === 'storage-cell')
|
|
136
142
|
return true;
|
|
137
143
|
const archetype = component.constructor.placement;
|
|
138
144
|
if (archetype === 'operation')
|
|
@@ -142,7 +148,7 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
142
148
|
// ── CarrierHolder — attach frame for direct carrier children ─────────────
|
|
143
149
|
/**
|
|
144
150
|
* Attach frame for carriers that are DIRECT children of the rack
|
|
145
|
-
* (monitoring mode, where
|
|
151
|
+
* (monitoring mode, where carriers go directly into the rack without
|
|
146
152
|
* explicit RackCell components).
|
|
147
153
|
*
|
|
148
154
|
* In simulation mode, carriers become children of their RackCell,
|
|
@@ -159,7 +165,7 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
159
165
|
// ── 2D rendering ─────────────────────────────────────────────────────────
|
|
160
166
|
/**
|
|
161
167
|
* 2D — top-down rectangle showing the rack footprint, with subdivisions
|
|
162
|
-
* suggesting the bay layout
|
|
168
|
+
* suggesting the bay layout.
|
|
163
169
|
*/
|
|
164
170
|
render(ctx) {
|
|
165
171
|
const { width, height, left, top } = this.state;
|
|
@@ -179,11 +185,11 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
179
185
|
}
|
|
180
186
|
// ── 3D ───────────────────────────────────────────────────────────────────
|
|
181
187
|
buildRealObject() {
|
|
182
|
-
return new
|
|
188
|
+
return new StorageRack3D(this);
|
|
183
189
|
}
|
|
184
190
|
};
|
|
185
|
-
|
|
186
|
-
sceneComponent('
|
|
187
|
-
],
|
|
188
|
-
export default
|
|
189
|
-
//# sourceMappingURL=
|
|
191
|
+
Rack = __decorate([
|
|
192
|
+
sceneComponent('storage-rack')
|
|
193
|
+
], Rack);
|
|
194
|
+
export default Rack;
|
|
195
|
+
//# sourceMappingURL=storage-rack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-rack.js","sourceRoot":"","sources":["../src/storage-rack.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, ContainerAbstract, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { State, Material3D } from '@hatiolab/things-scene'\nimport {\n CellContainer,\n CellMap,\n CarrierHolder,\n Placeable,\n type AttachFrame,\n type Alignment,\n type Heights,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { StorageRack3D } from './storage-rack-3d.js'\n\n/** Rack 컴포넌트 state */\nexport interface StorageRackState extends State {\n // ── 토폴로지 ──\n bays?: number\n levels?: number\n\n // ── 디버그 ──\n debugCells?: boolean\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'number',\n label: 'levels',\n name: 'levels',\n placeholder: '# of vertical levels (default 4)'\n },\n {\n type: 'number',\n label: 'bays',\n name: 'bays',\n placeholder: '# of horizontal bays (default 5)'\n }\n ],\n help: 'scene/component/rack'\n}\n\n// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),\n// which forces `isHTMLElement(): true` and trips the 3D pipeline's\n// addObject DOM-skip gate. Rack lives only in the 3D scene graph.\n//\n// Mixin chain: CellContainer → CarrierHolder → Placeable → ContainerAbstract\n// CellContainer: cell topology (cellMap, cell(), findAvailableCell(), occupiedCellIds())\n// CarrierHolder: 3D attach-point protocol (attachPointFor, containable gates)\n// Placeable: floor-archetype positioning\n// ContainerAbstract: child component management\n/**\n * Rack — a multi-level storage shelf system. A *Storage* whose role is to hold\n * carriers in a (bay × level) grid of cells.\n *\n * `levels` × `bays` cells form a vertical grid. Each cell holds one logistics\n * package (Pallet / Box / Parcel). A picker (Crane / Forklift / robot arm)\n * accesses individual cells via Phase G/H Pickable contract — the picker is\n * Rack-agnostic, knowing only how to interact with a cell.\n *\n * **Monitoring mode** (default): carriers are direct children of the rack,\n * placed by external data binding. No RackCell children are created.\n *\n * **Simulation mode**: call `rack._buildCells()` after placing the rack on the\n * scene. This creates RackCell children at the correct 3D positions. A picker\n * (Crane / Forklift / ...) then navigates to individual RackCells for\n * pick-and-place.\n *\n * **Placement**: `floor` archetype, full ceiling depth by default.\n *\n * **Mobility**: this Rack is stationary. A `MobileRack = Mover(Rack)` mixin\n * extension can be added later for AGV-mounted or cart-mounted variants —\n * the cell topology and pickable contract stay the same.\n */\n@sceneComponent('storage-rack')\nexport default class Rack extends CellContainer(CarrierHolder(Placeable(ContainerAbstract))) {\n declare state: StorageRackState\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.ceiling - h.floor\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n // ── CellContainer ─────────────────────────────────────────────────────────\n\n /**\n * Derive the cell topology from the rack's current dimensions and bay/level\n * counts. The CellMap is rebuilt fresh each time (state changes trigger\n * re-reads via things-scene's invalidation pipeline).\n *\n * Coordinate convention (matches things-scene 3D):\n * X = bay axis (left → right)\n * Y = level axis (floor → ceiling, the rack's `depth` state property)\n * Z = row axis (front → back, the rack's `height` state property)\n */\n get cellMap(): CellMap {\n const bays = Math.max(1, Math.floor((this.state.bays as number) || 5))\n const levels = Math.max(1, Math.floor((this.state.levels as number) || 4))\n const width = (this.state.width as number) || 1000\n const rackDepth = (this.state.depth as number) || 3000 // Y: floor→ceiling\n const rackHeight = (this.state.height as number) || 600 // Z: front→back\n\n return CellMap.grid({\n bays,\n rows: 1,\n levels,\n bayWidth: width / bays,\n rowDepth: rackHeight,\n levelHeight: rackDepth / levels\n })\n }\n\n /**\n * Create RackCell child components for each cell in the CellMap.\n *\n * Called explicitly to enter simulation mode — monitoring-mode racks\n * never call this (carriers are direct children, no explicit cells).\n *\n * Idempotent: removes existing rack-cell children first.\n */\n _buildCells(): void {\n // Remove existing rack-cell children\n const existing = (this.components as Component[] | undefined) ?? []\n for (const child of [...existing]) {\n if ((child as any).state?.type === 'storage-cell') {\n this.removeComponent(child)\n }\n }\n\n // Create a RackCell for each cell in the map\n const RackCellClass = (Component as any).register('storage-cell') as (new (...args: any[]) => Component) | undefined\n if (!RackCellClass) {\n console.warn('Rack._buildCells: rack-cell type not registered. Import rack-cell.ts first.')\n return\n }\n\n const context = this._app\n for (const cell of this.cellMap.cells) {\n const model = {\n type: 'storage-cell',\n cellId: cell.id,\n width: cell.size.width,\n height: cell.size.depth, // 2D height = 3D Z depth\n depth: cell.size.height // 3D Y = level height\n }\n const rackCell = new RackCellClass(model, context)\n this.addComponent(rackCell)\n }\n }\n\n // ── Container gates ───────────────────────────────────────────────────────\n\n /**\n * Allow:\n * - Carriable components (pallets, boxes, parcels) — direct children in monitoring mode.\n * - RackCell — created by _buildCells() in simulation mode.\n *\n * Block:\n * - Everything else (sensors, labels, etc. can be siblings of the rack, not children).\n */\n containable(component: Component): boolean {\n if ((component as any).state?.type === 'storage-cell') return true\n const archetype = (component.constructor as any).placement\n if (archetype === 'operation') return true\n return component.isDescendible(this)\n }\n\n // ── CarrierHolder — attach frame for direct carrier children ─────────────\n\n /**\n * Attach frame for carriers that are DIRECT children of the rack\n * (monitoring mode, where carriers go directly into the rack without\n * explicit RackCell components).\n *\n * In simulation mode, carriers become children of their RackCell,\n * and each RackCell provides its own attachPointFor(). So this method\n * is only invoked on direct-child carriers in monitoring mode — it\n * returns the rack's own object3d as the attach frame (default behavior).\n */\n attachPointFor(_carrier: Component): AttachFrame | null {\n const root = this._realObject?.object3d\n if (!root) return null\n return { attach: root }\n }\n\n // ── 2D rendering ─────────────────────────────────────────────────────────\n\n /**\n * 2D — top-down rectangle showing the rack footprint, with subdivisions\n * suggesting the bay layout.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n const bays = Math.max(1, Math.floor((this.state.bays as number) || 5))\n\n ctx.beginPath()\n // Outer rectangle\n ctx.rect(left, top, width, height)\n // Bay subdivisions (vertical lines)\n for (let i = 1; i < bays; i++) {\n const x = left + (width * i) / bays\n ctx.moveTo(x, top)\n ctx.lineTo(x, top + height)\n }\n }\n\n get fillStyle() {\n return '#a0a0a8'\n }\n\n // ── 3D ───────────────────────────────────────────────────────────────────\n\n buildRealObject(): RealObject | undefined {\n return new StorageRack3D(this)\n }\n}\n"]}
|
|
@@ -34,6 +34,13 @@ declare const _default: ({
|
|
|
34
34
|
material: string;
|
|
35
35
|
levels?: undefined;
|
|
36
36
|
bays?: undefined;
|
|
37
|
+
rows?: undefined;
|
|
38
|
+
columns?: undefined;
|
|
39
|
+
shelves?: undefined;
|
|
40
|
+
zone?: undefined;
|
|
41
|
+
locPattern?: undefined;
|
|
42
|
+
sectionDigits?: undefined;
|
|
43
|
+
unitDigits?: undefined;
|
|
37
44
|
status?: undefined;
|
|
38
45
|
carriageHeight?: undefined;
|
|
39
46
|
components?: undefined;
|
|
@@ -52,6 +59,13 @@ declare const _default: ({
|
|
|
52
59
|
material?: undefined;
|
|
53
60
|
levels?: undefined;
|
|
54
61
|
bays?: undefined;
|
|
62
|
+
rows?: undefined;
|
|
63
|
+
columns?: undefined;
|
|
64
|
+
shelves?: undefined;
|
|
65
|
+
zone?: undefined;
|
|
66
|
+
locPattern?: undefined;
|
|
67
|
+
sectionDigits?: undefined;
|
|
68
|
+
unitDigits?: undefined;
|
|
55
69
|
status?: undefined;
|
|
56
70
|
carriageHeight?: undefined;
|
|
57
71
|
components?: undefined;
|
|
@@ -70,6 +84,38 @@ declare const _default: ({
|
|
|
70
84
|
levels: number;
|
|
71
85
|
bays: number;
|
|
72
86
|
material?: undefined;
|
|
87
|
+
rows?: undefined;
|
|
88
|
+
columns?: undefined;
|
|
89
|
+
shelves?: undefined;
|
|
90
|
+
zone?: undefined;
|
|
91
|
+
locPattern?: undefined;
|
|
92
|
+
sectionDigits?: undefined;
|
|
93
|
+
unitDigits?: undefined;
|
|
94
|
+
status?: undefined;
|
|
95
|
+
carriageHeight?: undefined;
|
|
96
|
+
components?: undefined;
|
|
97
|
+
};
|
|
98
|
+
} | {
|
|
99
|
+
type: string;
|
|
100
|
+
description: string;
|
|
101
|
+
group: string;
|
|
102
|
+
icon: string;
|
|
103
|
+
model: {
|
|
104
|
+
type: string;
|
|
105
|
+
top: number;
|
|
106
|
+
left: number;
|
|
107
|
+
width: number;
|
|
108
|
+
height: number;
|
|
109
|
+
rows: number;
|
|
110
|
+
columns: number;
|
|
111
|
+
shelves: number;
|
|
112
|
+
zone: string;
|
|
113
|
+
locPattern: string;
|
|
114
|
+
sectionDigits: number;
|
|
115
|
+
unitDigits: number;
|
|
116
|
+
material?: undefined;
|
|
117
|
+
levels?: undefined;
|
|
118
|
+
bays?: undefined;
|
|
73
119
|
status?: undefined;
|
|
74
120
|
carriageHeight?: undefined;
|
|
75
121
|
components?: undefined;
|
|
@@ -90,6 +136,13 @@ declare const _default: ({
|
|
|
90
136
|
material?: undefined;
|
|
91
137
|
levels?: undefined;
|
|
92
138
|
bays?: undefined;
|
|
139
|
+
rows?: undefined;
|
|
140
|
+
columns?: undefined;
|
|
141
|
+
shelves?: undefined;
|
|
142
|
+
zone?: undefined;
|
|
143
|
+
locPattern?: undefined;
|
|
144
|
+
sectionDigits?: undefined;
|
|
145
|
+
unitDigits?: undefined;
|
|
93
146
|
components?: undefined;
|
|
94
147
|
};
|
|
95
148
|
} | {
|
|
@@ -127,6 +180,13 @@ declare const _default: ({
|
|
|
127
180
|
material?: undefined;
|
|
128
181
|
levels?: undefined;
|
|
129
182
|
bays?: undefined;
|
|
183
|
+
rows?: undefined;
|
|
184
|
+
columns?: undefined;
|
|
185
|
+
shelves?: undefined;
|
|
186
|
+
zone?: undefined;
|
|
187
|
+
locPattern?: undefined;
|
|
188
|
+
sectionDigits?: undefined;
|
|
189
|
+
unitDigits?: undefined;
|
|
130
190
|
status?: undefined;
|
|
131
191
|
carriageHeight?: undefined;
|
|
132
192
|
};
|