@operato/scene-storage 10.0.0-beta.27 → 10.0.0-beta.30
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 +24 -0
- package/dist/asrs-crane-3d.d.ts +10 -0
- package/dist/asrs-crane-3d.js +17 -0
- package/dist/asrs-crane-3d.js.map +1 -1
- package/dist/asrs-crane.d.ts +49 -13
- package/dist/asrs-crane.js +120 -16
- package/dist/asrs-crane.js.map +1 -1
- package/dist/asrs-rack.d.ts +49 -19
- package/dist/asrs-rack.js +108 -20
- package/dist/asrs-rack.js.map +1 -1
- package/dist/box.d.ts +3 -3
- package/dist/box.js +1 -2
- package/dist/box.js.map +1 -1
- package/dist/generic-container.d.ts +2 -2
- package/dist/generic-container.js +1 -2
- package/dist/generic-container.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pallet.d.ts +2 -2
- package/dist/pallet.js +1 -2
- package/dist/pallet.js.map +1 -1
- package/dist/parcel.d.ts +3 -3
- package/dist/parcel.js +1 -2
- package/dist/parcel.js.map +1 -1
- package/dist/rack-cell-3d.d.ts +25 -0
- package/dist/rack-cell-3d.js +88 -0
- package/dist/rack-cell-3d.js.map +1 -0
- package/dist/rack-cell.d.ts +56 -0
- package/dist/rack-cell.js +200 -0
- package/dist/rack-cell.js.map +1 -0
- package/dist/spot.d.ts +4 -11
- package/dist/spot.js +2 -3
- package/dist/spot.js.map +1 -1
- package/dist/templates/index.d.ts +42 -0
- package/dist/templates/index.js +49 -9
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/spot.js +1 -1
- package/dist/templates/spot.js.map +1 -1
- package/icons/asrs-crane.png +0 -0
- package/icons/asrs-rack.png +0 -0
- package/icons/box.png +0 -0
- package/icons/pallet.png +0 -0
- package/icons/parcel.png +0 -0
- package/icons/spot.png +0 -0
- package/package.json +9 -4
- package/src/asrs-crane-3d.ts +20 -0
- package/src/asrs-crane.ts +137 -16
- package/src/asrs-rack.ts +119 -20
- package/src/box.ts +2 -4
- package/src/generic-container.ts +1 -3
- package/src/index.ts +3 -0
- package/src/pallet.ts +1 -3
- package/src/parcel.ts +2 -4
- package/src/rack-cell-3d.ts +101 -0
- package/src/rack-cell.ts +228 -0
- package/src/spot.ts +4 -5
- package/src/templates/index.ts +49 -9
- package/src/templates/spot.ts +1 -1
- package/test/setup.js +279 -0
- package/test/test-asrs-crane.ts +319 -0
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/icons/box-plastic.png +0 -0
- package/icons/box-wood.png +0 -0
- package/icons/pallet-plastic.png +0 -0
- package/icons/pallet-wood.png +0 -0
package/dist/asrs-rack.js
CHANGED
|
@@ -2,8 +2,8 @@ import { __decorate } from "tslib";
|
|
|
2
2
|
/*
|
|
3
3
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
4
4
|
*/
|
|
5
|
-
import { ContainerAbstract, sceneComponent } from '@hatiolab/things-scene';
|
|
6
|
-
import { Placeable } from '@operato/scene-base';
|
|
5
|
+
import { Component, ContainerAbstract, sceneComponent } from '@hatiolab/things-scene';
|
|
6
|
+
import { CellContainer, CellMap, CarrierHolder, Placeable } from '@operato/scene-base';
|
|
7
7
|
import { AsrsRack3D } from './asrs-rack-3d.js';
|
|
8
8
|
const NATURE = {
|
|
9
9
|
mutable: false,
|
|
@@ -28,33 +28,30 @@ const NATURE = {
|
|
|
28
28
|
// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),
|
|
29
29
|
// which forces `isHTMLElement(): true` and trips the 3D pipeline's
|
|
30
30
|
// addObject DOM-skip gate. ASRS rack lives only in the 3D scene graph.
|
|
31
|
-
|
|
31
|
+
//
|
|
32
|
+
// Mixin chain: CellContainer → CarrierHolder → Placeable → ContainerAbstract
|
|
33
|
+
// CellContainer: cell topology (cellMap, cell(), findAvailableCell(), occupiedCellIds())
|
|
34
|
+
// CarrierHolder: 3D attach-point protocol (attachPointFor, containable gates)
|
|
35
|
+
// Placeable: floor-archetype positioning
|
|
36
|
+
// ContainerAbstract: child component management
|
|
32
37
|
/**
|
|
33
38
|
* AsrsRack — a multi-level high-bay storage rack, the structural backbone of
|
|
34
39
|
* an AS/RS (Automated Storage / Retrieval System).
|
|
35
40
|
*
|
|
36
41
|
* `levels` × `bays` cells form a vertical grid. Each cell holds one logistics
|
|
37
42
|
* package (Pallet / Box / Parcel). A pair of AsrsRacks separated by an aisle
|
|
38
|
-
* (where an AsrsCrane runs) is the typical AS/RS configuration
|
|
39
|
-
* single-rack unit and lets users compose multi-rack systems by placing them
|
|
40
|
-
* side by side. A future `AsrsAisle` composite may bundle the pair + crane.
|
|
43
|
+
* (where an AsrsCrane runs) is the typical AS/RS configuration.
|
|
41
44
|
*
|
|
42
|
-
* **
|
|
43
|
-
*
|
|
44
|
-
* pallet load. Users can shorten via explicit `state.depth` for warehouses
|
|
45
|
-
* with smaller envelopes.
|
|
45
|
+
* **Monitoring mode** (default): pallets/boxes are direct children of the rack,
|
|
46
|
+
* placed by the WCS data binding. No RackCell children are created.
|
|
46
47
|
*
|
|
47
|
-
* **
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* z lands on the rack's overall bottom (parent.zPos + parent.depth = ceiling),
|
|
51
|
-
* which isn't quite cell-level resolution — true per-cell z positioning is
|
|
52
|
-
* a v3 concern (the cargo would need to know which cell-row it's in).
|
|
48
|
+
* **Simulation mode**: call `rack._buildCells()` after placing the rack on the
|
|
49
|
+
* scene. This creates RackCell children at the correct 3D positions. The
|
|
50
|
+
* AsrsCrane then navigates to individual RackCells for pick-and-place.
|
|
53
51
|
*
|
|
54
|
-
*
|
|
55
|
-
* occupancy state is implicit in the children, not a status flag.
|
|
52
|
+
* **Placement**: `floor` archetype, full ceiling depth by default.
|
|
56
53
|
*/
|
|
57
|
-
let AsrsRack = class AsrsRack extends
|
|
54
|
+
let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(ContainerAbstract))) {
|
|
58
55
|
static placement = 'floor';
|
|
59
56
|
static align = 'bottom';
|
|
60
57
|
static defaultDepth = (h) => h.ceiling - h.floor;
|
|
@@ -64,13 +61,103 @@ let AsrsRack = class AsrsRack extends Base {
|
|
|
64
61
|
get anchors() {
|
|
65
62
|
return [];
|
|
66
63
|
}
|
|
67
|
-
|
|
64
|
+
// ── CellContainer ─────────────────────────────────────────────────────────
|
|
65
|
+
/**
|
|
66
|
+
* Derive the cell topology from the rack's current dimensions and bay/level
|
|
67
|
+
* counts. The CellMap is rebuilt fresh each time (state changes trigger
|
|
68
|
+
* re-reads via things-scene's invalidation pipeline).
|
|
69
|
+
*
|
|
70
|
+
* Coordinate convention (matches things-scene 3D):
|
|
71
|
+
* X = bay axis (left → right)
|
|
72
|
+
* Y = level axis (floor → ceiling, the rack's `depth` state property)
|
|
73
|
+
* Z = row axis (front → back, the rack's `height` state property)
|
|
74
|
+
*/
|
|
75
|
+
get cellMap() {
|
|
76
|
+
const bays = Math.max(1, Math.floor(this.state.bays || 5));
|
|
77
|
+
const levels = Math.max(1, Math.floor(this.state.levels || 4));
|
|
78
|
+
const width = this.state.width || 1000;
|
|
79
|
+
const rackDepth = this.state.depth || 3000; // Y: floor→ceiling
|
|
80
|
+
const rackHeight = this.state.height || 600; // Z: front→back
|
|
81
|
+
return CellMap.grid({
|
|
82
|
+
bays,
|
|
83
|
+
rows: 1,
|
|
84
|
+
levels,
|
|
85
|
+
bayWidth: width / bays,
|
|
86
|
+
rowDepth: rackHeight,
|
|
87
|
+
levelHeight: rackDepth / levels
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Create RackCell child components for each cell in the CellMap.
|
|
92
|
+
*
|
|
93
|
+
* Called explicitly to enter simulation mode — monitoring-mode racks
|
|
94
|
+
* never call this (pallets are direct children, no explicit cells).
|
|
95
|
+
*
|
|
96
|
+
* Idempotent: removes existing rack-cell children first.
|
|
97
|
+
*/
|
|
98
|
+
_buildCells() {
|
|
99
|
+
// Remove existing rack-cell children
|
|
100
|
+
const existing = this.components ?? [];
|
|
101
|
+
for (const child of [...existing]) {
|
|
102
|
+
if (child.state?.type === 'rack-cell') {
|
|
103
|
+
;
|
|
104
|
+
this.removeComponent?.(child);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Create a RackCell for each cell in the map
|
|
108
|
+
const RackCellClass = Component.register('rack-cell');
|
|
109
|
+
if (!RackCellClass) {
|
|
110
|
+
console.warn('AsrsRack._buildCells: rack-cell type not registered. Import rack-cell.ts first.');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const context = this._app;
|
|
114
|
+
for (const cell of this.cellMap.cells) {
|
|
115
|
+
const model = {
|
|
116
|
+
type: 'rack-cell',
|
|
117
|
+
cellId: cell.id,
|
|
118
|
+
width: cell.size.width,
|
|
119
|
+
height: cell.size.depth, // 2D height = 3D Z depth
|
|
120
|
+
depth: cell.size.height // 3D Y = level height
|
|
121
|
+
};
|
|
122
|
+
const rackCell = new RackCellClass(model, context);
|
|
123
|
+
this.addComponent?.(rackCell);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// ── Container gates ───────────────────────────────────────────────────────
|
|
127
|
+
/**
|
|
128
|
+
* Allow:
|
|
129
|
+
* - Carriable components (pallets, boxes, parcels) — direct children in monitoring mode.
|
|
130
|
+
* - RackCell — created by _buildCells() in simulation mode.
|
|
131
|
+
*
|
|
132
|
+
* Block:
|
|
133
|
+
* - Everything else (sensors, labels, etc. can be siblings of the rack, not children).
|
|
134
|
+
*/
|
|
68
135
|
containable(component) {
|
|
136
|
+
if (component.state?.type === 'rack-cell')
|
|
137
|
+
return true;
|
|
69
138
|
const archetype = component.constructor.placement;
|
|
70
139
|
if (archetype === 'operation')
|
|
71
140
|
return true;
|
|
72
141
|
return component.isDescendible(this);
|
|
73
142
|
}
|
|
143
|
+
// ── CarrierHolder — attach frame for direct carrier children ─────────────
|
|
144
|
+
/**
|
|
145
|
+
* Attach frame for carriers that are DIRECT children of the rack
|
|
146
|
+
* (monitoring mode, where pallets go directly into the rack without
|
|
147
|
+
* explicit RackCell components).
|
|
148
|
+
*
|
|
149
|
+
* In simulation mode, carriers become children of their RackCell,
|
|
150
|
+
* and each RackCell provides its own attachPointFor(). So this method
|
|
151
|
+
* is only invoked on direct-child carriers in monitoring mode — it
|
|
152
|
+
* returns the rack's own object3d as the attach frame (default behavior).
|
|
153
|
+
*/
|
|
154
|
+
attachPointFor(_carrier) {
|
|
155
|
+
const root = this._realObject?.object3d;
|
|
156
|
+
if (!root)
|
|
157
|
+
return null;
|
|
158
|
+
return { attach: root };
|
|
159
|
+
}
|
|
160
|
+
// ── 2D rendering ─────────────────────────────────────────────────────────
|
|
74
161
|
/**
|
|
75
162
|
* 2D — top-down rectangle showing the rack footprint, with subdivisions
|
|
76
163
|
* suggesting the bay layout (lines parallel to the aisle).
|
|
@@ -91,6 +178,7 @@ let AsrsRack = class AsrsRack extends Base {
|
|
|
91
178
|
get fillStyle() {
|
|
92
179
|
return '#a0a0a8';
|
|
93
180
|
}
|
|
181
|
+
// ── 3D ───────────────────────────────────────────────────────────────────
|
|
94
182
|
buildRealObject() {
|
|
95
183
|
return new AsrsRack3D(this);
|
|
96
184
|
}
|
package/dist/asrs-rack.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asrs-rack.js","sourceRoot":"","sources":["../src/asrs-rack.ts"],"names":[],"mappings":";AAAA;;GAEG;AACH,OAAO,EAA8B,iBAAiB,EAAc,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAClH,OAAO,EACL,SAAS,EAIV,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE;QACV;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,kCAAkC;SAChD;QACD;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,MAAM;YACZ,WAAW,EAAE,kCAAkC;SAChD;KACF;IACD,IAAI,EAAE,2BAA2B;CAClC,CAAA;AAED,2FAA2F;AAC3F,mEAAmE;AACnE,uEAAuE;AACvE,MAAM,IAAI,GAAG,SAAS,CAAC,iBAAiB,CAAgC,CAAA;AAExE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEY,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,IAAI;IACxC,MAAM,CAAC,SAAS,GAAuB,OAAO,CAAA;IAC9C,MAAM,CAAC,KAAK,GAAc,QAAQ,CAAA;IAClC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,KAAK,CAAA;IAEzD,IAAI,MAAM;QACR,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,OAAO;QACT,OAAO,EAAE,CAAA;IACX,CAAC;IAED,4EAA4E;IAC5E,WAAW,CAAC,SAAoB;QAC9B,MAAM,SAAS,GAAI,SAAS,CAAC,WAAmB,CAAC,SAAS,CAAA;QAC1D,IAAI,SAAS,KAAK,WAAW;YAAE,OAAO,IAAI,CAAA;QAC1C,OAAO,SAAS,CAAC,aAAa,CAAC,IAAW,CAAC,CAAA;IAC7C,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,GAA6B;QAClC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAE,IAAI,CAAC,KAAK,CAAC,IAAe,IAAI,CAAC,CAAC,CAAC,CAAA;QAEtE,GAAG,CAAC,SAAS,EAAE,CAAA;QACf,kBAAkB;QAClB,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QAClC,oCAAoC;QACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAA;YACnC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YAClB,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,IAAI,SAAS;QACX,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,eAAe;QACb,OAAO,IAAI,UAAU,CAAC,IAAW,CAAC,CAAA;IACpC,CAAC;;AA7CkB,QAAQ;IAD5B,cAAc,CAAC,WAAW,CAAC;GACP,QAAQ,CA8C5B;eA9CoB,QAAQ","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, ContainerAbstract, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport {\n Placeable,\n type Alignment,\n type Heights,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { AsrsRack3D } from './asrs-rack-3d.js'\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/asrs-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. ASRS rack lives only in the 3D scene graph.\nconst Base = Placeable(ContainerAbstract) as unknown as typeof Component\n\n/**\n * AsrsRack — a multi-level high-bay storage rack, the structural backbone of\n * an AS/RS (Automated Storage / Retrieval System).\n *\n * `levels` × `bays` cells form a vertical grid. Each cell holds one logistics\n * package (Pallet / Box / Parcel). A pair of AsrsRacks separated by an aisle\n * (where an AsrsCrane runs) is the typical AS/RS configuration; v1 ships the\n * single-rack unit and lets users compose multi-rack systems by placing them\n * side by side. A future `AsrsAisle` composite may bundle the pair + crane.\n *\n * **Placement**: `floor` archetype, full ceiling depth by default — AS/RS\n * racks typically span floor to ceiling, with levels sized to fit the tallest\n * pallet load. Users can shorten via explicit `state.depth` for warehouses\n * with smaller envelopes.\n *\n * **Container-based**. Cells host stored cargo as children — each child's\n * left/top within the rack's bounds determines which cell it occupies. The\n * stacking pass in `Placeable.computeDefaultZPos` ensures each child cargo's\n * z lands on the rack's overall bottom (parent.zPos + parent.depth = ceiling),\n * which isn't quite cell-level resolution — true per-cell z positioning is\n * a v3 concern (the cargo would need to know which cell-row it's in).\n *\n * No Legendable for v1 — racks are passive structures; their per-cell\n * occupancy state is implicit in the children, not a status flag.\n */\n@sceneComponent('asrs-rack')\nexport default class AsrsRack extends Base {\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 /** Operation cargo (pallets / boxes / parcels) goes in the rack's cells. */\n containable(component: Component) {\n const archetype = (component.constructor as any).placement\n if (archetype === 'operation') return true\n return component.isDescendible(this as any)\n }\n\n /**\n * 2D — top-down rectangle showing the rack footprint, with subdivisions\n * suggesting the bay layout (lines parallel to the aisle).\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 buildRealObject(): RealObject | undefined {\n return new AsrsRack3D(this as any)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"asrs-rack.js","sourceRoot":"","sources":["../src/asrs-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 {\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 { AsrsRack3D } from './asrs-rack-3d.js'\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/asrs-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. ASRS 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 * AsrsRack — a multi-level high-bay storage rack, the structural backbone of\n * an AS/RS (Automated Storage / Retrieval System).\n *\n * `levels` × `bays` cells form a vertical grid. Each cell holds one logistics\n * package (Pallet / Box / Parcel). A pair of AsrsRacks separated by an aisle\n * (where an AsrsCrane runs) is the typical AS/RS configuration.\n *\n * **Monitoring mode** (default): pallets/boxes are direct children of the rack,\n * placed by the WCS 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. The\n * AsrsCrane then navigates to individual RackCells for pick-and-place.\n *\n * **Placement**: `floor` archetype, full ceiling depth by default.\n */\n@sceneComponent('asrs-rack')\nexport default class AsrsRack extends CellContainer(CarrierHolder(Placeable(ContainerAbstract))) {\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 (pallets 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 as any).components as Component[] | undefined) ?? []\n for (const child of [...existing]) {\n if ((child as any).state?.type === 'rack-cell') {\n ;(this as any).removeComponent?.(child)\n }\n }\n\n // Create a RackCell for each cell in the map\n const RackCellClass = (Component as any).register('rack-cell') as (new (...args: any[]) => Component) | undefined\n if (!RackCellClass) {\n console.warn('AsrsRack._buildCells: rack-cell type not registered. Import rack-cell.ts first.')\n return\n }\n\n const context = (this as any)._app\n for (const cell of this.cellMap.cells) {\n const model = {\n type: 'rack-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 as any).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 === 'rack-cell') return true\n const archetype = (component.constructor as any).placement\n if (archetype === 'operation') return true\n return component.isDescendible(this as any)\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 pallets 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 as any)._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 (lines parallel to the aisle).\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 AsrsRack3D(this as any)\n }\n}\n"]}
|
package/dist/box.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
2
|
import { type Alignment, type LegendBinding, type PlacementArchetype } from '@operato/scene-base';
|
|
3
3
|
/**
|
|
4
4
|
* Box material — drives 3D structure and color.
|
|
@@ -12,7 +12,7 @@ import { type Alignment, type LegendBinding, type PlacementArchetype } from '@op
|
|
|
12
12
|
* different proportions, taping, and labels that warrant a distinct class.
|
|
13
13
|
*/
|
|
14
14
|
export type BoxMaterial = 'wood' | 'plastic';
|
|
15
|
-
declare const
|
|
15
|
+
declare const Box_base: any;
|
|
16
16
|
/**
|
|
17
17
|
* Box — a generic stackable container for goods. Wood crate or plastic tote
|
|
18
18
|
* variants distinguished by `material` prop.
|
|
@@ -21,7 +21,7 @@ declare const Base: typeof Component;
|
|
|
21
21
|
* logistics visualization (a *case* of items inside a box is data, not
|
|
22
22
|
* scene-tree). If a future use case needs nested boxes, extend Container.
|
|
23
23
|
*/
|
|
24
|
-
export default class Box extends
|
|
24
|
+
export default class Box extends Box_base {
|
|
25
25
|
static legends: Record<string, LegendBinding>;
|
|
26
26
|
static placement: PlacementArchetype;
|
|
27
27
|
static align: Alignment;
|
package/dist/box.js
CHANGED
|
@@ -31,7 +31,6 @@ const NATURE = {
|
|
|
31
31
|
};
|
|
32
32
|
// Carriable: a box can be a child of any CarrierHolder (Pallet for stacking,
|
|
33
33
|
// AGV deck, robot-arm gripper, Spot for staging).
|
|
34
|
-
const Base = Carriable(Legendable(Placeable(RectPath(Shape))));
|
|
35
34
|
/**
|
|
36
35
|
* Box — a generic stackable container for goods. Wood crate or plastic tote
|
|
37
36
|
* variants distinguished by `material` prop.
|
|
@@ -40,7 +39,7 @@ const Base = Carriable(Legendable(Placeable(RectPath(Shape))));
|
|
|
40
39
|
* logistics visualization (a *case* of items inside a box is data, not
|
|
41
40
|
* scene-tree). If a future use case needs nested boxes, extend Container.
|
|
42
41
|
*/
|
|
43
|
-
let Box = class Box extends
|
|
42
|
+
let Box = class Box extends Carriable(Legendable(Placeable(RectPath(Shape)))) {
|
|
44
43
|
static legends = {
|
|
45
44
|
bodyColor: { from: 'material', legend: BODY_LEGEND }
|
|
46
45
|
};
|
package/dist/box.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.js","sourceRoot":"","sources":["../src/box.ts"],"names":[],"mappings":";AAAA;;GAEG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"box.js","sourceRoot":"","sources":["../src/box.ts"],"names":[],"mappings":";AAAA;;GAEG;AACH,OAAO,EAA+B,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACrG,OAAO,EACL,SAAS,EACT,UAAU,EACV,SAAS,EAIV,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAenC,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACnB,CAAA;AAED,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE;QACV;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE;gBACR,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;oBAClC,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;iBACzC;aACF;SACF;KACF;IACD,IAAI,EAAE,qBAAqB;CAC5B,CAAA;AAED,6EAA6E;AAC7E,kDAAkD;AAClD;;;;;;;GAOG;AAEY,IAAM,GAAG,GAAT,MAAM,GAAI,SAAQ,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChF,MAAM,CAAC,OAAO,GAAkC;QAC9C,SAAS,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE;KACrD,CAAA;IAED,MAAM,CAAC,SAAS,GAAuB,WAAW,CAAA;IAClD,MAAM,CAAC,KAAK,GAAc,QAAQ,CAAA;IAClC,MAAM,CAAC,YAAY,GAAG,GAAG,CAAA;IAEzB,IAAI,MAAM;QACR,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,OAAO;QACT,OAAO,EAAE,CAAA;IACX,CAAC;IAED,+BAA+B;IAC/B,MAAM,CAAC,GAA6B;QAClC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAC/C,GAAG,CAAC,SAAS,EAAE,CAAA;QACf,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;IACpC,CAAC;IAED,IAAI,SAAS;QACX,OAAQ,IAAI,CAAC,KAAK,CAAC,SAAoB,IAAI,SAAS,CAAA;IACtD,CAAC;IAED,eAAe;QACb,OAAO,IAAI,KAAK,CAAC,IAAW,CAAC,CAAA;IAC/B,CAAC;;AA9BkB,GAAG;IADvB,cAAc,CAAC,KAAK,CAAC;GACD,GAAG,CA+BvB;eA/BoB,GAAG","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { ComponentNature, RealObject, RectPath, Shape, sceneComponent } from '@hatiolab/things-scene'\nimport {\n Carriable,\n Legendable,\n Placeable,\n type Alignment,\n type LegendBinding,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { Box3D } from './box-3d.js'\n\n/**\n * Box material — drives 3D structure and color.\n *\n * - `wood` — wood crate: visible vertical slats, gaps between, open or\n * semi-open top. Used for heavy / industrial parts.\n * - `plastic` — plastic tote / bin: solid molded walls with stackable lip\n * at top. Used for fulfillment, parts kitting.\n *\n * Cardboard parcels are a separate component (see `parcel.ts`) — they have\n * different proportions, taping, and labels that warrant a distinct class.\n */\nexport type BoxMaterial = 'wood' | 'plastic'\n\nconst BODY_LEGEND = {\n wood: '#a87644',\n plastic: '#3a5078',\n default: '#a87644'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'material',\n name: 'material',\n property: {\n options: [\n { display: 'Wood', value: 'wood' },\n { display: 'Plastic', value: 'plastic' }\n ]\n }\n }\n ],\n help: 'scene/component/box'\n}\n\n// Carriable: a box can be a child of any CarrierHolder (Pallet for stacking,\n// AGV deck, robot-arm gripper, Spot for staging).\n/**\n * Box — a generic stackable container for goods. Wood crate or plastic tote\n * variants distinguished by `material` prop.\n *\n * Shape-based (not Container) — boxes nesting other components is rare in\n * logistics visualization (a *case* of items inside a box is data, not\n * scene-tree). If a future use case needs nested boxes, extend Container.\n */\n@sceneComponent('box')\nexport default class Box extends Carriable(Legendable(Placeable(RectPath(Shape)))) {\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'material', legend: BODY_LEGEND }\n }\n\n static placement: PlacementArchetype = 'operation'\n static align: Alignment = 'bottom'\n static defaultDepth = 300\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n /** 2D — top-down rectangle. */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n }\n\n get fillStyle() {\n return (this.state.bodyColor as string) || '#a87644'\n }\n\n buildRealObject(): RealObject | undefined {\n return new Box3D(this as any)\n }\n}\n"]}
|
|
@@ -3,8 +3,8 @@ import { type Alignment, type Heights, type LegendBinding, type PlacementArchety
|
|
|
3
3
|
import { GenericContainer3D } from './generic-container-3d.js';
|
|
4
4
|
import type { ActuatorDef } from '@operato/scene-base';
|
|
5
5
|
export type ContainerStatus = 'empty' | 'partial' | 'full' | 'error';
|
|
6
|
-
declare const
|
|
7
|
-
export default class GenericContainer extends
|
|
6
|
+
declare const GenericContainer_base: any;
|
|
7
|
+
export default class GenericContainer extends GenericContainer_base {
|
|
8
8
|
static legends: Record<string, LegendBinding>;
|
|
9
9
|
static placement: PlacementArchetype;
|
|
10
10
|
static align: Alignment;
|
|
@@ -65,8 +65,7 @@ const NATURE = {
|
|
|
65
65
|
};
|
|
66
66
|
// 합성 순서: 안쪽부터 → ContainerAbstract → Placeable → Legendable → GltfComponent
|
|
67
67
|
// (GenericFacility 와 동일 패턴)
|
|
68
|
-
|
|
69
|
-
let GenericContainer = class GenericContainer extends Base {
|
|
68
|
+
let GenericContainer = class GenericContainer extends GltfComponent(Legendable(Placeable(ContainerAbstract))) {
|
|
70
69
|
static legends = {
|
|
71
70
|
bodyColor: { from: 'fill', legend: BODY_LEGEND },
|
|
72
71
|
lampEmissive: { from: 'fill', legend: LAMP_EMISSIVE_LEGEND }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic-container.js","sourceRoot":"","sources":["../src/generic-container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;AAEH,OAAO,EAGL,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,cAAc,EACf,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,UAAU,EACV,SAAS,EAKV,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAK9D,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;CACnB,CAAA;AAED,MAAM,oBAAoB,GAAG;IAC3B,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;CACnB,CAAA;AAED,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE;QACV,GAAG,oBAAoB;QACvB;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE;gBACR,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;oBACpC,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;oBACxC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;oBAClC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;iBACrC;aACF;SACF;KACF;IACD,IAAI,EAAE,mCAAmC;CAC1C,CAAA;AAED,2EAA2E;AAC3E,4BAA4B;
|
|
1
|
+
{"version":3,"file":"generic-container.js","sourceRoot":"","sources":["../src/generic-container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;AAEH,OAAO,EAGL,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,cAAc,EACf,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,UAAU,EACV,SAAS,EAKV,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAK9D,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;CACnB,CAAA;AAED,MAAM,oBAAoB,GAAG;IAC3B,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;CACnB,CAAA;AAED,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE;QACV,GAAG,oBAAoB;QACvB;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE;gBACR,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;oBACpC,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;oBACxC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;oBAClC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;iBACrC;aACF;SACF;KACF;IACD,IAAI,EAAE,mCAAmC;CAC1C,CAAA;AAED,2EAA2E;AAC3E,4BAA4B;AAEb,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,aAAa,CAAC,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACnG,MAAM,CAAC,OAAO,GAAkC;QAC9C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE;QAChD,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE;KAC7D,CAAA;IAED,MAAM,CAAC,SAAS,GAAuB,OAAO,CAAA;IAC9C,MAAM,CAAC,KAAK,GAAc,QAAQ,CAAA;IAClC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,CAAA;IAE3D,IAAI,MAAM;QACR,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,OAAO;QACT,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,SAAS;QACX,OAAS,IAAI,CAAC,KAAa,CAAC,SAAqD,IAAI,EAAE,CAAA;IACzF,CAAC;IAED,IAAI,cAAc;QAChB,OAAS,IAAI,CAAC,KAAa,CAAC,cAAqD,IAAI,EAAE,CAAA;IACzF,CAAC;IAED,WAAW,CAAC,SAAoB;QAC9B,OAAO,SAAS,CAAC,aAAa,CAAC,IAAW,CAAC,CAAA;IAC7C,CAAC;IAED,eAAe;QACb,OAAO,IAAI,kBAAkB,CAAC,IAAW,CAAC,CAAA;IAC5C,CAAC;;AAhCkB,gBAAgB;IADpC,cAAc,CAAC,WAAW,CAAC;GACP,gBAAgB,CAiCpC;eAjCoB,gBAAgB","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * GenericContainer — GLB 모델 기반 범용 보관소 컴포넌트.\n *\n * 합성 구조:\n * GltfComponent (things-scene) — GLB 컨벤션 (src, 2D 스냅샷, ratioLock,\n * gltf-* 에디터, RealObjectGLTF)\n * ⊕ ContainerAbstract (things-scene) — 자식 보유 (DOM-free 컨테이너)\n * ⊕ Placeable (scene-base) — floor placement, depth=operation\n * ⊕ Legendable (scene-base) — fill 별 bodyColor / lampEmissive\n *\n * 추가 (이 클래스 고유):\n * - state.actuators / actuatorValues — 동적 노드 transform (rack lift, shutter 열림 등)\n *\n * 동일 컴포넌트 하나로 stocker / buffer / shelf / rack 모두 표현 가능 — 각 보관소별로\n * 다른 GLB + 다른 actuator 매핑만 다름.\n *\n * GenericTransport / GenericFacility 와의 차이:\n * - cargo holder 가 아님 — 사용자가 구체 type (e.g. AsrsRack) 으로 변환 후에 cargo 부여\n * - container 카테고리 (정적 보관소). 분류는 동일 floor placement 지만 의미는 다름\n *\n * board-import 의 categoryFallback 에서 container 카테고리의 default placeholder. 사용자가\n * 모델러에서 src 부착하거나 type 변경으로 도메인 type (Stocker / Buffer / AsrsRack 등) 으로 변환.\n */\n\nimport {\n Component,\n ComponentNature,\n ContainerAbstract,\n GltfComponent,\n gltfNatureProperties,\n sceneComponent\n} from '@hatiolab/things-scene'\nimport {\n Legendable,\n Placeable,\n type Alignment,\n type Heights,\n type LegendBinding,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { GenericContainer3D } from './generic-container-3d.js'\nimport type { ActuatorDef } from '@operato/scene-base'\n\nexport type ContainerStatus = 'empty' | 'partial' | 'full' | 'error'\n\nconst BODY_LEGEND = {\n empty: '#a8b8c4',\n partial: '#7d96b0',\n full: '#5a7c9a',\n error: '#e9746b',\n default: '#a8b8c4'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n empty: '#222222',\n partial: '#3388cc',\n full: '#2266aa',\n error: '#ff3333',\n default: '#222222'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n ...gltfNatureProperties,\n {\n type: 'select',\n label: 'fill',\n name: 'fill',\n property: {\n options: [\n { display: 'Empty', value: 'empty' },\n { display: 'Partial', value: 'partial' },\n { display: 'Full', value: 'full' },\n { display: 'Error', value: 'error' }\n ]\n }\n }\n ],\n help: 'scene/component/generic-container'\n}\n\n// 합성 순서: 안쪽부터 → ContainerAbstract → Placeable → Legendable → GltfComponent\n// (GenericFacility 와 동일 패턴)\n@sceneComponent('container')\nexport default class GenericContainer extends GltfComponent(Legendable(Placeable(ContainerAbstract))) {\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'fill', legend: BODY_LEGEND },\n lampEmissive: { from: 'fill', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.operation - h.floor\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n get actuators(): Record<string, ActuatorDef> {\n return ((this.state as any).actuators as Record<string, ActuatorDef> | undefined) ?? {}\n }\n\n get actuatorValues(): Record<string, number> {\n return ((this.state as any).actuatorValues as Record<string, number> | undefined) ?? {}\n }\n\n containable(component: Component): boolean {\n return component.isDescendible(this as any)\n }\n\n buildRealObject() {\n return new GenericContainer3D(this as any)\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ export { default as Box } from './box.js';
|
|
|
4
4
|
export type { BoxMaterial } from './box.js';
|
|
5
5
|
export { default as Parcel } from './parcel.js';
|
|
6
6
|
export { default as AsrsRack } from './asrs-rack.js';
|
|
7
|
+
export { default as RackCell } from './rack-cell.js';
|
|
8
|
+
export type { RackCellType } from './rack-cell.js';
|
|
9
|
+
export { RackCell3D } from './rack-cell-3d.js';
|
|
7
10
|
export { default as AsrsCrane } from './asrs-crane.js';
|
|
8
11
|
export type { AsrsCraneStatus } from './asrs-crane.js';
|
|
9
12
|
export { default as Spot } from './spot.js';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export { default as Pallet } from './pallet.js';
|
|
|
5
5
|
export { default as Box } from './box.js';
|
|
6
6
|
export { default as Parcel } from './parcel.js';
|
|
7
7
|
export { default as AsrsRack } from './asrs-rack.js';
|
|
8
|
+
export { default as RackCell } from './rack-cell.js';
|
|
9
|
+
export { RackCell3D } from './rack-cell-3d.js';
|
|
8
10
|
export { default as AsrsCrane } from './asrs-crane.js';
|
|
9
11
|
export { default as Spot } from './spot.js';
|
|
10
12
|
export { Spot3D } from './spot-3d.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAG/C,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,UAAU,CAAA;AAGzC,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAGtD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nexport { default as Pallet } from './pallet.js'\nexport type { PalletMaterial } from './pallet.js'\n\nexport { default as Box } from './box.js'\nexport type { BoxMaterial } from './box.js'\n\nexport { default as Parcel } from './parcel.js'\n\nexport { default as AsrsRack } from './asrs-rack.js'\nexport { default as AsrsCrane } from './asrs-crane.js'\nexport type { AsrsCraneStatus } from './asrs-crane.js'\n\nexport { default as Spot } from './spot.js'\nexport { Spot3D } from './spot-3d.js'\n\nexport { default as GenericContainer } from './generic-container.js'\nexport type { ContainerStatus } from './generic-container.js'\nexport { GenericContainer3D } from './generic-container-3d.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAG/C,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,UAAU,CAAA;AAGzC,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAGtD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nexport { default as Pallet } from './pallet.js'\nexport type { PalletMaterial } from './pallet.js'\n\nexport { default as Box } from './box.js'\nexport type { BoxMaterial } from './box.js'\n\nexport { default as Parcel } from './parcel.js'\n\nexport { default as AsrsRack } from './asrs-rack.js'\nexport { default as RackCell } from './rack-cell.js'\nexport type { RackCellType } from './rack-cell.js'\nexport { RackCell3D } from './rack-cell-3d.js'\nexport { default as AsrsCrane } from './asrs-crane.js'\nexport type { AsrsCraneStatus } from './asrs-crane.js'\n\nexport { default as Spot } from './spot.js'\nexport { Spot3D } from './spot-3d.js'\n\nexport { default as GenericContainer } from './generic-container.js'\nexport type { ContainerStatus } from './generic-container.js'\nexport { GenericContainer3D } from './generic-container-3d.js'\n"]}
|
package/dist/pallet.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { type Alignment, type LegendBinding, type PlacementArchetype } from '@op
|
|
|
12
12
|
* legend + a 3D variant in pallet-3d.ts.
|
|
13
13
|
*/
|
|
14
14
|
export type PalletMaterial = 'wood' | 'plastic';
|
|
15
|
-
declare const
|
|
15
|
+
declare const Pallet_base: any;
|
|
16
16
|
/**
|
|
17
17
|
* Pallet — a flat transport structure that goods are stacked and stored on.
|
|
18
18
|
*
|
|
@@ -39,7 +39,7 @@ declare const Base: typeof Component;
|
|
|
39
39
|
* (machine-3d.ts:113-122). v1 accepts the visual overlap; v2 will add the
|
|
40
40
|
* detection.
|
|
41
41
|
*/
|
|
42
|
-
export default class Pallet extends
|
|
42
|
+
export default class Pallet extends Pallet_base {
|
|
43
43
|
static legends: Record<string, LegendBinding>;
|
|
44
44
|
static placement: PlacementArchetype;
|
|
45
45
|
static align: Alignment;
|
package/dist/pallet.js
CHANGED
|
@@ -37,7 +37,6 @@ const NATURE = {
|
|
|
37
37
|
// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),
|
|
38
38
|
// which forces `isHTMLElement(): true` and trips the 3D pipeline's
|
|
39
39
|
// addObject DOM-skip gate. Pallet renders only as a 3D mesh.
|
|
40
|
-
const Base = Carriable(Legendable(Placeable(ContainerAbstract)));
|
|
41
40
|
/**
|
|
42
41
|
* Pallet — a flat transport structure that goods are stacked and stored on.
|
|
43
42
|
*
|
|
@@ -64,7 +63,7 @@ const Base = Carriable(Legendable(Placeable(ContainerAbstract)));
|
|
|
64
63
|
* (machine-3d.ts:113-122). v1 accepts the visual overlap; v2 will add the
|
|
65
64
|
* detection.
|
|
66
65
|
*/
|
|
67
|
-
let Pallet = class Pallet extends
|
|
66
|
+
let Pallet = class Pallet extends Carriable(Legendable(Placeable(ContainerAbstract))) {
|
|
68
67
|
static legends = {
|
|
69
68
|
bodyColor: { from: 'material', legend: BODY_LEGEND }
|
|
70
69
|
};
|
package/dist/pallet.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pallet.js","sourceRoot":"","sources":["../src/pallet.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 {\n Carriable,\n Legendable,\n Placeable,\n type Alignment,\n type LegendBinding,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { Pallet3D } from './pallet-3d.js'\n\n/**\n * Pallet material — drives both 2D fill color and 3D structure.\n *\n * - `wood` — traditional EUR / EPAL pallet: parallel slats on top and\n * bottom, three perpendicular stringers between them.\n * - `plastic` — molded one-piece pallet: solid top deck with cutouts,\n * hollow underside with feet. Distinct ribbed underside.\n *\n * Adding a third material (e.g. metal, composite) is a one-line change to the\n * legend + a 3D variant in pallet-3d.ts.\n */\nexport type PalletMaterial = 'wood' | 'plastic'\n\nconst BODY_LEGEND = {\n wood: '#a87644',\n plastic: '#5a6a78',\n default: '#a87644'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'material',\n name: 'material',\n property: {\n options: [\n { display: 'Wood', value: 'wood' },\n { display: 'Plastic', value: 'plastic' }\n ]\n }\n }\n ],\n help: 'scene/component/pallet'\n}\n\n// Carriable: a pallet can sit on AGV / Forklift / robot-arm gripper / Spot\n// and also accept boxes / parcels as children (ContainerAbstract base\n// provides the child-container behavior; Carriable only adds the\n// holder-mount hook).\n//\n// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),\n// which forces `isHTMLElement(): true` and trips the 3D pipeline's\n// addObject DOM-skip gate. Pallet renders only as a 3D mesh.\nconst Base = Carriable(Legendable(Placeable(ContainerAbstract))) as unknown as typeof Component\n\n/**\n * Pallet — a flat transport structure that goods are stacked and stored on.\n *\n * Standard EUR pallet is 1200 × 800mm × 144mm; we don't enforce these\n * dimensions but they're a good starting point for the catalog templates.\n *\n * **Container-based.** Boxes / parcels stacked on the pallet are added as\n * children — same `containable()` archetype-filter pattern as Forklift / Agv.\n * Visual stacking (children rendering on top of the pallet rather than at\n * absolute operation level) is a v2 concern; see ARCHITECTURE NOTES below.\n *\n * **Placement = `operation`.** A pallet's *normal* state is loaded and in\n * transit on a conveyor / AGV / forklift fork — at operation level. Empty\n * pallets in a floor-storage area are an exceptional state where the user\n * sets `state.zPos = 0` explicitly. Default to the common case.\n *\n * ## ARCHITECTURE NOTES — visual stacking\n *\n * When a Box (also `placement: 'operation'`) is added as a child of a\n * Pallet, both default to z = operation_height. They overlap visually\n * rather than the box sitting on top of the pallet. Solving this cleanly\n * (parent-relative z derivation when the parent is a structural carrier)\n * is a follow-up — fmsim's pattern is to detect parent type at render time\n * (machine-3d.ts:113-122). v1 accepts the visual overlap; v2 will add the\n * detection.\n */\n@sceneComponent('pallet')\nexport default class Pallet extends Base {\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'material', legend: BODY_LEGEND }\n }\n\n static placement: PlacementArchetype = 'operation'\n static align: Alignment = 'bottom'\n static defaultDepth = 150 // EUR pallet is 144mm; 150 is the round number convention\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n /** Accept other operation-archetype cargo (boxes, parcels, smaller pallets) as stacked children. */\n containable(component: Component) {\n const archetype = (component.constructor as any).placement\n if (archetype === 'operation') return true\n return component.isDescendible(this as any)\n }\n\n /**\n * 2D — top-down silhouette. Body is a flat rectangle (wood/plastic deck);\n * `postrender()` adds the deck pattern + edge stroke so the pallet reads\n * as a pallet instead of a featureless rectangle.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n }\n\n /**\n * Deck pattern + edge stroke. Wood: parallel slats with darker grooves\n * between (typical EUR pallet deck). Plastic: cross-cutout pattern\n * suggesting the molded reinforcement ribs.\n *\n * Slats run along the *short* axis of the rectangle (= along the longer\n * stringer direction in real life), so a 1200×800 pallet shows multiple\n * narrow slats across the 1200mm dimension — matching the EUR layout.\n */\n postrender(ctx: CanvasRenderingContext2D) {\n super.postrender?.(ctx)\n\n const { width, height, left, top } = this.state\n const isPlastic = ((this.state as any).material as PalletMaterial) === 'plastic'\n\n ctx.save()\n\n if (!isPlastic) {\n // Wood — slats. Run them along the longer axis so the grooves are\n // perpendicular to the longer side (typical pallet appearance).\n const longAxisHorizontal = width >= height\n const slatCount = 5\n const grooveColor = '#7a4f25'\n ctx.fillStyle = grooveColor\n if (longAxisHorizontal) {\n // grooves vertical (X direction across the width)\n const grooveW = Math.max(1, width * 0.012)\n const slatW = (width - grooveW * (slatCount - 1)) / slatCount\n for (let i = 1; i < slatCount; i++) {\n const x = left + i * slatW + (i - 1) * grooveW\n ctx.fillRect(x, top + height * 0.05, grooveW, height * 0.9)\n }\n } else {\n const grooveH = Math.max(1, height * 0.012)\n const slatH = (height - grooveH * (slatCount - 1)) / slatCount\n for (let i = 1; i < slatCount; i++) {\n const y = top + i * slatH + (i - 1) * grooveH\n ctx.fillRect(left + width * 0.05, y, width * 0.9, grooveH)\n }\n }\n } else {\n // Plastic — cross + corner cutouts hint\n ctx.strokeStyle = '#3a4956'\n ctx.lineWidth = Math.max(1, Math.min(width, height) * 0.012)\n ctx.beginPath()\n ctx.moveTo(left + width * 0.5, top + height * 0.1)\n ctx.lineTo(left + width * 0.5, top + height * 0.9)\n ctx.moveTo(left + width * 0.1, top + height * 0.5)\n ctx.lineTo(left + width * 0.9, top + height * 0.5)\n ctx.stroke()\n }\n\n // Edge stroke\n ctx.strokeStyle = isPlastic ? '#2a3946' : '#5e3818'\n ctx.lineWidth = 1\n ctx.strokeRect(left, top, width, height)\n\n ctx.restore()\n }\n\n get fillStyle() {\n return (this.state.bodyColor as string) || '#a87644'\n }\n\n buildRealObject(): RealObject | undefined {\n return new Pallet3D(this as any)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pallet.js","sourceRoot":"","sources":["../src/pallet.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 {\n Carriable,\n Legendable,\n Placeable,\n type Alignment,\n type LegendBinding,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { Pallet3D } from './pallet-3d.js'\n\n/**\n * Pallet material — drives both 2D fill color and 3D structure.\n *\n * - `wood` — traditional EUR / EPAL pallet: parallel slats on top and\n * bottom, three perpendicular stringers between them.\n * - `plastic` — molded one-piece pallet: solid top deck with cutouts,\n * hollow underside with feet. Distinct ribbed underside.\n *\n * Adding a third material (e.g. metal, composite) is a one-line change to the\n * legend + a 3D variant in pallet-3d.ts.\n */\nexport type PalletMaterial = 'wood' | 'plastic'\n\nconst BODY_LEGEND = {\n wood: '#a87644',\n plastic: '#5a6a78',\n default: '#a87644'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'material',\n name: 'material',\n property: {\n options: [\n { display: 'Wood', value: 'wood' },\n { display: 'Plastic', value: 'plastic' }\n ]\n }\n }\n ],\n help: 'scene/component/pallet'\n}\n\n// Carriable: a pallet can sit on AGV / Forklift / robot-arm gripper / Spot\n// and also accept boxes / parcels as children (ContainerAbstract base\n// provides the child-container behavior; Carriable only adds the\n// holder-mount hook).\n//\n// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),\n// which forces `isHTMLElement(): true` and trips the 3D pipeline's\n// addObject DOM-skip gate. Pallet renders only as a 3D mesh.\n/**\n * Pallet — a flat transport structure that goods are stacked and stored on.\n *\n * Standard EUR pallet is 1200 × 800mm × 144mm; we don't enforce these\n * dimensions but they're a good starting point for the catalog templates.\n *\n * **Container-based.** Boxes / parcels stacked on the pallet are added as\n * children — same `containable()` archetype-filter pattern as Forklift / Agv.\n * Visual stacking (children rendering on top of the pallet rather than at\n * absolute operation level) is a v2 concern; see ARCHITECTURE NOTES below.\n *\n * **Placement = `operation`.** A pallet's *normal* state is loaded and in\n * transit on a conveyor / AGV / forklift fork — at operation level. Empty\n * pallets in a floor-storage area are an exceptional state where the user\n * sets `state.zPos = 0` explicitly. Default to the common case.\n *\n * ## ARCHITECTURE NOTES — visual stacking\n *\n * When a Box (also `placement: 'operation'`) is added as a child of a\n * Pallet, both default to z = operation_height. They overlap visually\n * rather than the box sitting on top of the pallet. Solving this cleanly\n * (parent-relative z derivation when the parent is a structural carrier)\n * is a follow-up — fmsim's pattern is to detect parent type at render time\n * (machine-3d.ts:113-122). v1 accepts the visual overlap; v2 will add the\n * detection.\n */\n@sceneComponent('pallet')\nexport default class Pallet extends Carriable(Legendable(Placeable(ContainerAbstract))) {\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'material', legend: BODY_LEGEND }\n }\n\n static placement: PlacementArchetype = 'operation'\n static align: Alignment = 'bottom'\n static defaultDepth = 150 // EUR pallet is 144mm; 150 is the round number convention\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n /** Accept other operation-archetype cargo (boxes, parcels, smaller pallets) as stacked children. */\n containable(component: Component) {\n const archetype = (component.constructor as any).placement\n if (archetype === 'operation') return true\n return component.isDescendible(this as any)\n }\n\n /**\n * 2D — top-down silhouette. Body is a flat rectangle (wood/plastic deck);\n * `postrender()` adds the deck pattern + edge stroke so the pallet reads\n * as a pallet instead of a featureless rectangle.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n }\n\n /**\n * Deck pattern + edge stroke. Wood: parallel slats with darker grooves\n * between (typical EUR pallet deck). Plastic: cross-cutout pattern\n * suggesting the molded reinforcement ribs.\n *\n * Slats run along the *short* axis of the rectangle (= along the longer\n * stringer direction in real life), so a 1200×800 pallet shows multiple\n * narrow slats across the 1200mm dimension — matching the EUR layout.\n */\n postrender(ctx: CanvasRenderingContext2D) {\n super.postrender?.(ctx)\n\n const { width, height, left, top } = this.state\n const isPlastic = ((this.state as any).material as PalletMaterial) === 'plastic'\n\n ctx.save()\n\n if (!isPlastic) {\n // Wood — slats. Run them along the longer axis so the grooves are\n // perpendicular to the longer side (typical pallet appearance).\n const longAxisHorizontal = width >= height\n const slatCount = 5\n const grooveColor = '#7a4f25'\n ctx.fillStyle = grooveColor\n if (longAxisHorizontal) {\n // grooves vertical (X direction across the width)\n const grooveW = Math.max(1, width * 0.012)\n const slatW = (width - grooveW * (slatCount - 1)) / slatCount\n for (let i = 1; i < slatCount; i++) {\n const x = left + i * slatW + (i - 1) * grooveW\n ctx.fillRect(x, top + height * 0.05, grooveW, height * 0.9)\n }\n } else {\n const grooveH = Math.max(1, height * 0.012)\n const slatH = (height - grooveH * (slatCount - 1)) / slatCount\n for (let i = 1; i < slatCount; i++) {\n const y = top + i * slatH + (i - 1) * grooveH\n ctx.fillRect(left + width * 0.05, y, width * 0.9, grooveH)\n }\n }\n } else {\n // Plastic — cross + corner cutouts hint\n ctx.strokeStyle = '#3a4956'\n ctx.lineWidth = Math.max(1, Math.min(width, height) * 0.012)\n ctx.beginPath()\n ctx.moveTo(left + width * 0.5, top + height * 0.1)\n ctx.lineTo(left + width * 0.5, top + height * 0.9)\n ctx.moveTo(left + width * 0.1, top + height * 0.5)\n ctx.lineTo(left + width * 0.9, top + height * 0.5)\n ctx.stroke()\n }\n\n // Edge stroke\n ctx.strokeStyle = isPlastic ? '#2a3946' : '#5e3818'\n ctx.lineWidth = 1\n ctx.strokeRect(left, top, width, height)\n\n ctx.restore()\n }\n\n get fillStyle() {\n return (this.state.bodyColor as string) || '#a87644'\n }\n\n buildRealObject(): RealObject | undefined {\n return new Pallet3D(this as any)\n }\n}\n"]}
|
package/dist/parcel.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
2
|
import { type Alignment, type PlacementArchetype } from '@operato/scene-base';
|
|
3
|
-
declare const
|
|
3
|
+
declare const Parcel_base: any;
|
|
4
4
|
/**
|
|
5
5
|
* Parcel — a cardboard package, the typical e-commerce / parcel-sortation unit.
|
|
6
6
|
*
|
|
@@ -16,7 +16,7 @@ declare const Base: typeof Component;
|
|
|
16
16
|
* No Legendable for v1 — parcel color is fixed cardboard. Future damaged /
|
|
17
17
|
* inspected indicators would add a status legend then.
|
|
18
18
|
*/
|
|
19
|
-
export default class Parcel extends
|
|
19
|
+
export default class Parcel extends Parcel_base {
|
|
20
20
|
static placement: PlacementArchetype;
|
|
21
21
|
static align: Alignment;
|
|
22
22
|
static defaultDepth: number;
|
package/dist/parcel.js
CHANGED
|
@@ -21,7 +21,6 @@ const NATURE = {
|
|
|
21
21
|
// Carriable: parcel can be a child of any CarrierHolder (Spot, robot-arm
|
|
22
22
|
// gripper, AGV deck, …). Mixin wraps add() so the parcel's 3D object3d
|
|
23
23
|
// is reattached to the holder's chosen mount frame.
|
|
24
|
-
const Base = Carriable(Placeable(RectPath(Shape)));
|
|
25
24
|
/**
|
|
26
25
|
* Parcel — a cardboard package, the typical e-commerce / parcel-sortation unit.
|
|
27
26
|
*
|
|
@@ -37,7 +36,7 @@ const Base = Carriable(Placeable(RectPath(Shape)));
|
|
|
37
36
|
* No Legendable for v1 — parcel color is fixed cardboard. Future damaged /
|
|
38
37
|
* inspected indicators would add a status legend then.
|
|
39
38
|
*/
|
|
40
|
-
let Parcel = class Parcel extends
|
|
39
|
+
let Parcel = class Parcel extends Carriable(Placeable(RectPath(Shape))) {
|
|
41
40
|
static placement = 'operation';
|
|
42
41
|
static align = 'bottom';
|
|
43
42
|
static defaultDepth = 150;
|
package/dist/parcel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parcel.js","sourceRoot":"","sources":["../src/parcel.ts"],"names":[],"mappings":";AAAA;;GAEG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"parcel.js","sourceRoot":"","sources":["../src/parcel.ts"],"names":[],"mappings":";AAAA;;GAEG;AACH,OAAO,EAA+B,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACrG,OAAO,EACL,SAAS,EACT,SAAS,EAGV,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE;QACV;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,YAAY;SACnB;KACF;IACD,IAAI,EAAE,wBAAwB;CAC/B,CAAA;AAED,yEAAyE;AACzE,uEAAuE;AACvE,oDAAoD;AACpD;;;;;;;;;;;;;;GAcG;AAEY,IAAM,MAAM,GAAZ,MAAM,MAAO,SAAQ,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACvE,MAAM,CAAC,SAAS,GAAuB,WAAW,CAAA;IAClD,MAAM,CAAC,KAAK,GAAc,QAAQ,CAAA;IAClC,MAAM,CAAC,YAAY,GAAG,GAAG,CAAA;IAEzB,IAAI,MAAM;QACR,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,OAAO;QACT,OAAO,EAAE,CAAA;IACX,CAAC;IAED,gDAAgD;IAChD,MAAM,CAAC,GAA6B;QAClC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAC/C,GAAG,CAAC,SAAS,EAAE,CAAA;QACf,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;IACpC,CAAC;IAED,IAAI,SAAS;QACX,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,eAAe;QACb,OAAO,IAAI,QAAQ,CAAC,IAAW,CAAC,CAAA;IAClC,CAAC;;AA1BkB,MAAM;IAD1B,cAAc,CAAC,QAAQ,CAAC;GACJ,MAAM,CA2B1B;eA3BoB,MAAM","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { ComponentNature, RealObject, RectPath, Shape, sceneComponent } from '@hatiolab/things-scene'\nimport {\n Carriable,\n Placeable,\n type Alignment,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { Parcel3D } from './parcel-3d.js'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'string',\n label: 'tracking-id',\n name: 'trackingId'\n }\n ],\n help: 'scene/component/parcel'\n}\n\n// Carriable: parcel can be a child of any CarrierHolder (Spot, robot-arm\n// gripper, AGV deck, …). Mixin wraps add() so the parcel's 3D object3d\n// is reattached to the holder's chosen mount frame.\n/**\n * Parcel — a cardboard package, the typical e-commerce / parcel-sortation unit.\n *\n * Distinct from `Box` because parcels have:\n * - cardboard appearance (tan/brown corrugate, not wood / plastic)\n * - tape line down the center (the visual signature that says \"package\")\n * - typically a label on top (where shipping info goes)\n * - flatter / more elongated proportions in real-world parcel networks\n *\n * No `material` prop — parcels are always cardboard. If a future shipping\n * domain needs metal cases or polybags, those become separate components.\n *\n * No Legendable for v1 — parcel color is fixed cardboard. Future damaged /\n * inspected indicators would add a status legend then.\n */\n@sceneComponent('parcel')\nexport default class Parcel extends Carriable(Placeable(RectPath(Shape))) {\n static placement: PlacementArchetype = 'operation'\n static align: Alignment = 'bottom'\n static defaultDepth = 150\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n /** 2D — top-down rectangle in cardboard tan. */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n }\n\n get fillStyle() {\n return '#c8a878'\n }\n\n buildRealObject(): RealObject | undefined {\n return new Parcel3D(this as any)\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { RealObjectGroup } from '@hatiolab/things-scene';
|
|
2
|
+
export declare class RackCell3D extends RealObjectGroup {
|
|
3
|
+
build(): void;
|
|
4
|
+
updateDimension(): void;
|
|
5
|
+
updateTransform(): void;
|
|
6
|
+
updateAlpha(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Position this group at the cell's localPosition within the rack's 3D space.
|
|
9
|
+
*
|
|
10
|
+
* CellMap.grid() places cell origins at:
|
|
11
|
+
* x = b * bayWidth, y = l * levelHeight, z = r * rowDepth
|
|
12
|
+
* (starting from 0,0,0 at the rack's bottom-left-front corner).
|
|
13
|
+
*
|
|
14
|
+
* The rack's object3d is centered: X spans [-width/2, +width/2],
|
|
15
|
+
* Y spans [-depth/2, +depth/2], Z spans [-height/2, +height/2].
|
|
16
|
+
*
|
|
17
|
+
* So the cell centre in rack-local 3D space:
|
|
18
|
+
* x3d = cellPos.x + bayWidth/2 - width/2
|
|
19
|
+
* y3d = cellPos.y + levelHeight/2 - rackDepth/2
|
|
20
|
+
* z3d = cellPos.z + rowDepth/2 - rackHeight/2
|
|
21
|
+
*/
|
|
22
|
+
_repositionFromCellMap(): void;
|
|
23
|
+
private _debugBox?;
|
|
24
|
+
private _addDebugBox;
|
|
25
|
+
}
|