@operato/scene-storage 10.0.0-beta.30 → 10.0.0-beta.32
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 +25 -0
- package/dist/asrs-crane.d.ts +16 -1
- package/dist/asrs-crane.js +8 -0
- package/dist/asrs-crane.js.map +1 -1
- package/dist/asrs-rack.d.ts +9 -0
- package/dist/asrs-rack.js +2 -3
- package/dist/asrs-rack.js.map +1 -1
- package/dist/box.d.ts +16 -0
- package/dist/box.js +28 -1
- package/dist/box.js.map +1 -1
- package/dist/generic-container-3d.js.map +1 -1
- package/dist/generic-container.d.ts +10 -0
- package/dist/generic-container.js.map +1 -1
- package/dist/pallet.d.ts +20 -0
- package/dist/pallet.js +39 -1
- package/dist/pallet.js.map +1 -1
- package/dist/parcel.d.ts +13 -0
- package/dist/parcel.js +25 -1
- package/dist/parcel.js.map +1 -1
- package/dist/rack-cell-3d.js +1 -1
- package/dist/rack-cell-3d.js.map +1 -1
- package/dist/rack-cell.d.ts +8 -0
- package/dist/rack-cell.js +7 -10
- package/dist/rack-cell.js.map +1 -1
- package/dist/spot-3d.js.map +1 -1
- package/dist/spot.d.ts +8 -0
- package/dist/spot.js.map +1 -1
- package/package.json +3 -3
- package/src/asrs-crane.ts +30 -6
- package/src/asrs-rack.ts +23 -7
- package/src/box.ts +51 -2
- package/src/generic-container-3d.ts +1 -1
- package/src/generic-container.ts +21 -4
- package/src/pallet.ts +67 -4
- package/src/parcel.ts +48 -2
- package/src/rack-cell-3d.ts +1 -1
- package/src/rack-cell.ts +23 -10
- package/src/spot-3d.ts +1 -1
- package/src/spot.ts +13 -2
- package/test/test-phase-h-carrier-pickable.ts +105 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,31 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [10.0.0-beta.32](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.31...v10.0.0-beta.32) (2026-05-09)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
* **manufacturing/storage:** Phase G9 + H — RobotArm SCAFFOLDING 정상 승격 + Parcel Pickable ([05e4d95](https://github.com/things-scene/operato-scene/commit/05e4d954b939dd295927322d683463a3f7bf4e82))
|
|
12
|
+
* **storage:** Phase H — AsrsCrane toolType 'forklift-fork' ([5596848](https://github.com/things-scene/operato-scene/commit/5596848954543888aa3feb80598548fd5f935beb))
|
|
13
|
+
* **storage:** Phase H — Pallet / Box 의 Pickable 구현 ([0149709](https://github.com/things-scene/operato-scene/commit/0149709d20dfa56d27a5823d75c9e2c57bba5890))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [10.0.0-beta.31](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.30...v10.0.0-beta.31) (2026-05-08)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### :house: Code Refactoring
|
|
21
|
+
|
|
22
|
+
* _realObject 캐스트 16개 제거 (Phase C Round 2 후속) ([8616c6c](https://github.com/things-scene/operato-scene/commit/8616c6c45e38a9bbcc244a77cee53479a756df85))
|
|
23
|
+
* 39개 buildRealObject 캐스트 제거 (Phase D Round 4 후속) ([b86681c](https://github.com/things-scene/operato-scene/commit/b86681c07d7bf41cb93ade9855afb21902ed8186))
|
|
24
|
+
* **mfg/transport/storage:** typed state Round 2 — 14개 컴포넌트 적용 ([f1765f6](https://github.com/things-scene/operato-scene/commit/f1765f62d12cf4c20ea26a2297e98069b9621a45))
|
|
25
|
+
* 도메인 mixin 캐스트 추가 정리 7개 (Phase D Round 4 후속) ([1546b71](https://github.com/things-scene/operato-scene/commit/1546b7183969a38952872a5327b8329c2fa1ab7a))
|
|
26
|
+
* 불필요한 (this as any) 캐스트 52개 제거 (Phase C) ([989dd11](https://github.com/things-scene/operato-scene/commit/989dd1147f4c768167fdc8b9a9e98acb0495a408))
|
|
27
|
+
* 함수 인자 (this as any) 13개 제거 (Phase D Round 4 후속) ([b0a400c](https://github.com/things-scene/operato-scene/commit/b0a400c9716fdec22518cc6ad0bd1c1e69bda89d))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
6
31
|
## [10.0.0-beta.30](https://github.com/things-scene/operato-scene/compare/v10.0.0-beta.29...v10.0.0-beta.30) (2026-05-07)
|
|
7
32
|
|
|
8
33
|
|
package/dist/asrs-crane.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Component, ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
|
-
import type { SlotDef } from '@hatiolab/things-scene';
|
|
2
|
+
import type { SlotDef, State, Material3D } from '@hatiolab/things-scene';
|
|
3
3
|
import { type AttachFrame, type Alignment, type Heights, type LegendBinding, type MoveOptions, type PlacementArchetype } from '@operato/scene-base';
|
|
4
|
+
import { AsrsCrane3D } from './asrs-crane-3d.js';
|
|
4
5
|
/**
|
|
5
6
|
* AsrsCrane status — the operating state of a stacker crane in an AS/RS aisle.
|
|
6
7
|
*
|
|
@@ -12,6 +13,12 @@ import { type AttachFrame, type Alignment, type Heights, type LegendBinding, typ
|
|
|
12
13
|
* - `error` — fault / e-stop / collision warning
|
|
13
14
|
*/
|
|
14
15
|
export type AsrsCraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error';
|
|
16
|
+
/** AsrsCrane 컴포넌트 state */
|
|
17
|
+
export interface AsrsCraneState extends State {
|
|
18
|
+
status?: AsrsCraneStatus;
|
|
19
|
+
carriageHeight?: number;
|
|
20
|
+
material3d?: Material3D;
|
|
21
|
+
}
|
|
15
22
|
declare const AsrsCrane_base: any;
|
|
16
23
|
/**
|
|
17
24
|
* AsrsCrane — the stacker / retrieval crane that runs in the aisle of an
|
|
@@ -30,12 +37,20 @@ declare const AsrsCrane_base: any;
|
|
|
30
37
|
* engage + reparent. During transit the carrier IS a child of the crane.
|
|
31
38
|
*/
|
|
32
39
|
export default class AsrsCrane extends AsrsCrane_base {
|
|
40
|
+
state: AsrsCraneState;
|
|
41
|
+
_realObject?: AsrsCrane3D;
|
|
33
42
|
static legends: Record<string, LegendBinding>;
|
|
34
43
|
static placement: PlacementArchetype;
|
|
35
44
|
static align: Alignment;
|
|
36
45
|
static defaultDepth: (h: Heights) => number;
|
|
37
46
|
/** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */
|
|
38
47
|
static yawOffset: number;
|
|
48
|
+
/**
|
|
49
|
+
* Phase H — ASRS crane 은 telescoping forks 로 rack cell 안의 carrier 픽업.
|
|
50
|
+
* forklift-fork 와 동일 mechanism — pallet 의 fork pocket 진입.
|
|
51
|
+
* (Mover mixin chain 이 :any 라 override 키워드 불가, getter 만 정의.)
|
|
52
|
+
*/
|
|
53
|
+
get toolType(): string;
|
|
39
54
|
get nature(): ComponentNature;
|
|
40
55
|
get anchors(): never[];
|
|
41
56
|
/** Stacker crane carries at most one load at a time on its forks. */
|
package/dist/asrs-crane.js
CHANGED
|
@@ -88,6 +88,14 @@ let AsrsCrane = class AsrsCrane extends Mover(CarrierHolder(ContainerCapacity(Le
|
|
|
88
88
|
static defaultDepth = (h) => h.ceiling - h.floor;
|
|
89
89
|
/** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */
|
|
90
90
|
static yawOffset = 0;
|
|
91
|
+
/**
|
|
92
|
+
* Phase H — ASRS crane 은 telescoping forks 로 rack cell 안의 carrier 픽업.
|
|
93
|
+
* forklift-fork 와 동일 mechanism — pallet 의 fork pocket 진입.
|
|
94
|
+
* (Mover mixin chain 이 :any 라 override 키워드 불가, getter 만 정의.)
|
|
95
|
+
*/
|
|
96
|
+
get toolType() {
|
|
97
|
+
return 'forklift-fork';
|
|
98
|
+
}
|
|
91
99
|
get nature() {
|
|
92
100
|
return NATURE;
|
|
93
101
|
}
|
package/dist/asrs-crane.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asrs-crane.js","sourceRoot":"","sources":["../src/asrs-crane.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { SlotDef } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Legendable,\n Mover,\n Placeable,\n type AttachFrame,\n type Alignment,\n type Heights,\n type LegendBinding,\n type MoveOptions,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { AsrsCrane3D } from './asrs-crane-3d.js'\n\n/**\n * AsrsCrane status — the operating state of a stacker crane in an AS/RS aisle.\n *\n * - `idle` — parked at home position\n * - `moving` — translating along the aisle (horizontal) or along the\n * mast (vertical); the two motions are typically combined\n * - `loading` — extracting a pallet from a rack cell\n * - `unloading` — placing a pallet into a rack cell\n * - `error` — fault / e-stop / collision warning\n */\nexport type AsrsCraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error'\n\nconst BODY_LEGEND = {\n idle: '#888',\n moving: '#aabbcc',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#c66',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#222222',\n moving: '#44ff44',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#ff3333',\n default: '#222222'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Loading', value: 'loading' },\n { display: 'Unloading', value: 'unloading' },\n { display: 'Error', value: 'error' }\n ]\n }\n },\n {\n type: 'number',\n label: 'carriage-height',\n name: 'carriageHeight',\n placeholder: 'mm — height of carriage on mast'\n }\n ],\n help: 'scene/component/asrs-crane'\n}\n\n// Mixin chain: Mover → CarrierHolder → ContainerCapacity → Legendable → Placeable → ContainerAbstract\n//\n// Mover: pick / place / pickAndPlace / moveTo / engage primitives\n// CarrierHolder: attachPointFor() — where the carrier sits on the crane (carriage fork)\n// ContainerCapacity: receive() / dispatch() / canReceive() / slots — slot tracking +\n// TRANSFER_SLOT_KEY bookkeeping during transit\n// Legendable: status → bodyColor / lampEmissive colour mapping\n// Placeable: floor-archetype 3D positioning\n// ContainerAbstract: child management — carrier becomes a child while in transit\n//\n// Note: ContainerAbstract replaces Shape. The 2D outline is drawn manually in\n// render() below (a simple top-down rectangle), matching the old Shape output\n// without the Shape base-class overhead.\n/**\n * AsrsCrane — the stacker / retrieval crane that runs in the aisle of an\n * AS/RS, moving cargo between the load port and the rack cells.\n *\n * Structure: a tall vertical mast that translates along a floor + ceiling\n * rail (the aisle), with a carriage that slides up/down the mast carrying a\n * shuttle / forks.\n *\n * **Monitoring mode**: crane status is driven by data binding\n * (`state.status`, `state.carriageHeight`). The carrier is referenced\n * via data binding — it is NOT a child of the crane in monitoring mode.\n *\n * **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, rackCell)`\n * (or `crane.pickAndPlace(carrier, rackCell)`). Mover handles navigation +\n * engage + reparent. During transit the carrier IS a child of the crane.\n */\n@sceneComponent('asrs-crane')\nexport default class AsrsCrane extends Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) {\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.ceiling - h.floor\n\n /** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */\n static yawOffset = 0\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n // ── ContainerCapacity ─────────────────────────────────────────────────────\n\n /** Stacker crane carries at most one load at a time on its forks. */\n get slots(): SlotDef[] {\n return [{ id: 'forks', maxCount: 1 }]\n }\n\n // ── CarrierHolder — attach frame (carriage fork position) ─────────────────\n\n /**\n * Return the 3D attach frame on the crane's carriage (fork tip).\n * Carriers are attached here while the crane is in transit (pick phase).\n *\n * The AsrsCrane3D exposes `getCarriageFrame()` — a sub-Object3D that\n * tracks the carriage height and sits at the fork TCP. If the 3D object\n * isn't built yet (e.g. before scene initialization), fall back to the\n * crane's own object3d centre.\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const ro = (this as any)._realObject as AsrsCrane3D | undefined\n const frame = ro?.getCarriageFrame?.()\n if (frame) {\n const carrierDepth = resolveCarrierDepth(carrier)\n return { attach: frame, localPosition: { x: 0, y: carrierDepth / 2, z: 0 } }\n }\n const root = (this as any)._realObject?.object3d\n if (!root) return null\n return { attach: root }\n }\n\n // ── Mover overrides ───────────────────────────────────────────────────────\n\n /**\n * Domain-specific actuation between arrival and reparent.\n *\n * Simulation sequence for PICK:\n * 1. Mover.pick() navigates crane to carrier position (moveTo).\n * 2. engage('pick') → snap carriage height + status 'loading'.\n * 3. Carrier is reparented to crane (becomes child).\n *\n * For now: set status and snap carriage height. A full ASRS simulation\n * would tween the carriageHeight here (animate AsrsCrane3D).\n *\n * Status lifecycle:\n * idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle\n * The 'moving' state is not set from Mover.moveTo() because TypeScript\n * can't call super.moveTo() on an `: any`-typed mixin. WCS data binding\n * sets 'moving' in monitoring mode; override pick()/place() to set it\n * in full simulation environments.\n */\n async engage(\n target: Component,\n kind: 'pick' | 'place',\n _options: MoveOptions = {}\n ): Promise<void> {\n if (kind === 'pick') {\n this.setState({ status: 'loading' as AsrsCraneStatus })\n const carrierY = resolveCarrierCenterY(target)\n if (carrierY !== null) {\n this.setState({ carriageHeight: carrierY })\n }\n } else {\n this.setState({ status: 'unloading' as AsrsCraneStatus })\n }\n // In a full simulation: await carriage-motion tween here.\n }\n\n // ── Domain aliases ────────────────────────────────────────────────────────\n\n /** Fetch a carrier from a rack cell (semantically = pick). */\n fetch(carrier: Component, options?: MoveOptions): Promise<void> {\n return (this as any).pick(carrier, options)\n }\n\n /** Deposit a carrier into a rack cell (semantically = place). */\n deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void> {\n return (this as any).place(carrier, cell, options)\n }\n\n // ── 2D rendering ─────────────────────────────────────────────────────────\n\n /**\n * 2D — top-down rectangle showing the crane's footprint along the aisle.\n * The crane is much taller than wide, so the 2D mark is small.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n const fillColor = (this.state.bodyColor as string) || '#888'\n ctx.save()\n ctx.fillStyle = fillColor\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n ctx.fill()\n ctx.restore()\n }\n\n // ── 3D ───────────────────────────────────────────────────────────────────\n\n buildRealObject(): RealObject | undefined {\n return new AsrsCrane3D(this as any)\n }\n}\n\nfunction resolveCarrierDepth(c: Component): number {\n const eff = (c as any)._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n return numOr((c as any)?.state?.depth, 0)\n}\n\nfunction resolveCarrierCenterY(c: Component): number | null {\n const pos = (c as any).state\n if (!pos) return null\n // zPos is the 3D Y center of a Placeable component in things-scene\n const zPos = numOr(pos.zPos, NaN)\n if (!Number.isNaN(zPos)) return zPos\n return null\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n"]}
|
|
1
|
+
{"version":3,"file":"asrs-crane.js","sourceRoot":"","sources":["../src/asrs-crane.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { SlotDef, State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Legendable,\n Mover,\n Placeable,\n type AttachFrame,\n type Alignment,\n type Heights,\n type LegendBinding,\n type MoveOptions,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { AsrsCrane3D } from './asrs-crane-3d.js'\n\n/**\n * AsrsCrane status — the operating state of a stacker crane in an AS/RS aisle.\n *\n * - `idle` — parked at home position\n * - `moving` — translating along the aisle (horizontal) or along the\n * mast (vertical); the two motions are typically combined\n * - `loading` — extracting a pallet from a rack cell\n * - `unloading` — placing a pallet into a rack cell\n * - `error` — fault / e-stop / collision warning\n */\nexport type AsrsCraneStatus = 'idle' | 'moving' | 'loading' | 'unloading' | 'error'\n\n/** AsrsCrane 컴포넌트 state */\nexport interface AsrsCraneState extends State {\n // ── 운영 상태 ──\n status?: AsrsCraneStatus\n\n // ── 액추에이터 ──\n carriageHeight?: number\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst BODY_LEGEND = {\n idle: '#888',\n moving: '#aabbcc',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#c66',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#222222',\n moving: '#44ff44',\n loading: '#ffaa00',\n unloading: '#ffaa00',\n error: '#ff3333',\n default: '#222222'\n}\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Loading', value: 'loading' },\n { display: 'Unloading', value: 'unloading' },\n { display: 'Error', value: 'error' }\n ]\n }\n },\n {\n type: 'number',\n label: 'carriage-height',\n name: 'carriageHeight',\n placeholder: 'mm — height of carriage on mast'\n }\n ],\n help: 'scene/component/asrs-crane'\n}\n\n// Mixin chain: Mover → CarrierHolder → ContainerCapacity → Legendable → Placeable → ContainerAbstract\n//\n// Mover: pick / place / pickAndPlace / moveTo / engage primitives\n// CarrierHolder: attachPointFor() — where the carrier sits on the crane (carriage fork)\n// ContainerCapacity: receive() / dispatch() / canReceive() / slots — slot tracking +\n// TRANSFER_SLOT_KEY bookkeeping during transit\n// Legendable: status → bodyColor / lampEmissive colour mapping\n// Placeable: floor-archetype 3D positioning\n// ContainerAbstract: child management — carrier becomes a child while in transit\n//\n// Note: ContainerAbstract replaces Shape. The 2D outline is drawn manually in\n// render() below (a simple top-down rectangle), matching the old Shape output\n// without the Shape base-class overhead.\n/**\n * AsrsCrane — the stacker / retrieval crane that runs in the aisle of an\n * AS/RS, moving cargo between the load port and the rack cells.\n *\n * Structure: a tall vertical mast that translates along a floor + ceiling\n * rail (the aisle), with a carriage that slides up/down the mast carrying a\n * shuttle / forks.\n *\n * **Monitoring mode**: crane status is driven by data binding\n * (`state.status`, `state.carriageHeight`). The carrier is referenced\n * via data binding — it is NOT a child of the crane in monitoring mode.\n *\n * **Simulation mode**: call `crane.pick(carrier)` / `crane.place(carrier, rackCell)`\n * (or `crane.pickAndPlace(carrier, rackCell)`). Mover handles navigation +\n * engage + reparent. During transit the carrier IS a child of the crane.\n */\n@sceneComponent('asrs-crane')\nexport default class AsrsCrane extends Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) {\n declare state: AsrsCraneState\n declare _realObject?: AsrsCrane3D\n\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.ceiling - h.floor\n\n /** Yaw offset: crane model is drawn with the aisle axis along X (right = forward). */\n static yawOffset = 0\n\n /**\n * Phase H — ASRS crane 은 telescoping forks 로 rack cell 안의 carrier 픽업.\n * forklift-fork 와 동일 mechanism — pallet 의 fork pocket 진입.\n * (Mover mixin chain 이 :any 라 override 키워드 불가, getter 만 정의.)\n */\n get toolType(): string {\n return 'forklift-fork'\n }\n\n get nature() {\n return NATURE\n }\n\n get anchors() {\n return []\n }\n\n // ── ContainerCapacity ─────────────────────────────────────────────────────\n\n /** Stacker crane carries at most one load at a time on its forks. */\n get slots(): SlotDef[] {\n return [{ id: 'forks', maxCount: 1 }]\n }\n\n // ── CarrierHolder — attach frame (carriage fork position) ─────────────────\n\n /**\n * Return the 3D attach frame on the crane's carriage (fork tip).\n * Carriers are attached here while the crane is in transit (pick phase).\n *\n * The AsrsCrane3D exposes `getCarriageFrame()` — a sub-Object3D that\n * tracks the carriage height and sits at the fork TCP. If the 3D object\n * isn't built yet (e.g. before scene initialization), fall back to the\n * crane's own object3d centre.\n */\n attachPointFor(carrier: Component): AttachFrame | null {\n const ro = this._realObject\n const frame = ro?.getCarriageFrame?.()\n if (frame) {\n const carrierDepth = resolveCarrierDepth(carrier)\n return { attach: frame, localPosition: { x: 0, y: carrierDepth / 2, z: 0 } }\n }\n const root = this._realObject?.object3d\n if (!root) return null\n return { attach: root }\n }\n\n // ── Mover overrides ───────────────────────────────────────────────────────\n\n /**\n * Domain-specific actuation between arrival and reparent.\n *\n * Simulation sequence for PICK:\n * 1. Mover.pick() navigates crane to carrier position (moveTo).\n * 2. engage('pick') → snap carriage height + status 'loading'.\n * 3. Carrier is reparented to crane (becomes child).\n *\n * For now: set status and snap carriage height. A full ASRS simulation\n * would tween the carriageHeight here (animate AsrsCrane3D).\n *\n * Status lifecycle:\n * idle → (moveTo running) → engage fires → loading/unloading → (reparent) → idle\n * The 'moving' state is not set from Mover.moveTo() because TypeScript\n * can't call super.moveTo() on an `: any`-typed mixin. WCS data binding\n * sets 'moving' in monitoring mode; override pick()/place() to set it\n * in full simulation environments.\n */\n async engage(\n target: Component,\n kind: 'pick' | 'place',\n _options: MoveOptions = {}\n ): Promise<void> {\n if (kind === 'pick') {\n this.setState({ status: 'loading' as AsrsCraneStatus })\n const carrierY = resolveCarrierCenterY(target)\n if (carrierY !== null) {\n this.setState({ carriageHeight: carrierY })\n }\n } else {\n this.setState({ status: 'unloading' as AsrsCraneStatus })\n }\n // In a full simulation: await carriage-motion tween here.\n }\n\n // ── Domain aliases ────────────────────────────────────────────────────────\n\n /** Fetch a carrier from a rack cell (semantically = pick). */\n fetch(carrier: Component, options?: MoveOptions): Promise<void> {\n return this.pick(carrier, options)\n }\n\n /** Deposit a carrier into a rack cell (semantically = place). */\n deposit(carrier: Component, cell: Component, options?: MoveOptions): Promise<void> {\n return this.place(carrier, cell, options)\n }\n\n // ── 2D rendering ─────────────────────────────────────────────────────────\n\n /**\n * 2D — top-down rectangle showing the crane's footprint along the aisle.\n * The crane is much taller than wide, so the 2D mark is small.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { width, height, left, top } = this.state\n const fillColor = (this.state.bodyColor as string) || '#888'\n ctx.save()\n ctx.fillStyle = fillColor\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n ctx.fill()\n ctx.restore()\n }\n\n // ── 3D ───────────────────────────────────────────────────────────────────\n\n buildRealObject(): RealObject | undefined {\n return new AsrsCrane3D(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 resolveCarrierCenterY(c: Component): number | null {\n const pos = (c as any).state\n if (!pos) return null\n // zPos is the 3D Y center of a Placeable component in things-scene\n const zPos = numOr(pos.zPos, NaN)\n if (!Number.isNaN(zPos)) return zPos\n return null\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n"]}
|
package/dist/asrs-rack.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { Component, ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
|
+
import type { State, Material3D } from '@hatiolab/things-scene';
|
|
2
3
|
import { CellMap, type AttachFrame, type Alignment, type Heights, type PlacementArchetype } from '@operato/scene-base';
|
|
4
|
+
/** AsrsRack 컴포넌트 state */
|
|
5
|
+
export interface AsrsRackState extends State {
|
|
6
|
+
bays?: number;
|
|
7
|
+
levels?: number;
|
|
8
|
+
debugCells?: boolean;
|
|
9
|
+
material3d?: Material3D;
|
|
10
|
+
}
|
|
3
11
|
declare const AsrsRack_base: any;
|
|
4
12
|
/**
|
|
5
13
|
* AsrsRack — a multi-level high-bay storage rack, the structural backbone of
|
|
@@ -19,6 +27,7 @@ declare const AsrsRack_base: any;
|
|
|
19
27
|
* **Placement**: `floor` archetype, full ceiling depth by default.
|
|
20
28
|
*/
|
|
21
29
|
export default class AsrsRack extends AsrsRack_base {
|
|
30
|
+
state: AsrsRackState;
|
|
22
31
|
static placement: PlacementArchetype;
|
|
23
32
|
static align: Alignment;
|
|
24
33
|
static defaultDepth: (h: Heights) => number;
|
package/dist/asrs-rack.js
CHANGED
|
@@ -100,8 +100,7 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
100
100
|
const existing = this.components ?? [];
|
|
101
101
|
for (const child of [...existing]) {
|
|
102
102
|
if (child.state?.type === 'rack-cell') {
|
|
103
|
-
;
|
|
104
|
-
this.removeComponent?.(child);
|
|
103
|
+
this.removeComponent(child);
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
// Create a RackCell for each cell in the map
|
|
@@ -120,7 +119,7 @@ let AsrsRack = class AsrsRack extends CellContainer(CarrierHolder(Placeable(Cont
|
|
|
120
119
|
depth: cell.size.height // 3D Y = level height
|
|
121
120
|
};
|
|
122
121
|
const rackCell = new RackCellClass(model, context);
|
|
123
|
-
this.addComponent
|
|
122
|
+
this.addComponent(rackCell);
|
|
124
123
|
}
|
|
125
124
|
}
|
|
126
125
|
// ── Container gates ───────────────────────────────────────────────────────
|
package/dist/asrs-rack.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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 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 { AsrsRack3D } from './asrs-rack-3d.js'\n\n/** AsrsRack 컴포넌트 state */\nexport interface AsrsRackState 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/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 declare state: AsrsRackState\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 (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.components as Component[] | undefined) ?? []\n for (const child of [...existing]) {\n if ((child as any).state?.type === 'rack-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('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._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.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)\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._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)\n }\n}\n"]}
|
package/dist/box.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
|
+
import type { State, Material3D, PickupFrame } from '@hatiolab/things-scene';
|
|
2
3
|
import { type Alignment, type LegendBinding, type PlacementArchetype } from '@operato/scene-base';
|
|
3
4
|
/**
|
|
4
5
|
* Box material — drives 3D structure and color.
|
|
@@ -12,6 +13,11 @@ import { type Alignment, type LegendBinding, type PlacementArchetype } from '@op
|
|
|
12
13
|
* different proportions, taping, and labels that warrant a distinct class.
|
|
13
14
|
*/
|
|
14
15
|
export type BoxMaterial = 'wood' | 'plastic';
|
|
16
|
+
/** Box 컴포넌트 state */
|
|
17
|
+
export interface BoxState extends State {
|
|
18
|
+
material?: BoxMaterial;
|
|
19
|
+
material3d?: Material3D;
|
|
20
|
+
}
|
|
15
21
|
declare const Box_base: any;
|
|
16
22
|
/**
|
|
17
23
|
* Box — a generic stackable container for goods. Wood crate or plastic tote
|
|
@@ -22,6 +28,7 @@ declare const Box_base: any;
|
|
|
22
28
|
* scene-tree). If a future use case needs nested boxes, extend Container.
|
|
23
29
|
*/
|
|
24
30
|
export default class Box extends Box_base {
|
|
31
|
+
state: BoxState;
|
|
25
32
|
static legends: Record<string, LegendBinding>;
|
|
26
33
|
static placement: PlacementArchetype;
|
|
27
34
|
static align: Alignment;
|
|
@@ -32,5 +39,14 @@ export default class Box extends Box_base {
|
|
|
32
39
|
render(ctx: CanvasRenderingContext2D): void;
|
|
33
40
|
get fillStyle(): string;
|
|
34
41
|
buildRealObject(): RealObject | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Phase H — pickup contract. Box 는 위에서 gripper / vacuum cup 으로 집기 —
|
|
44
|
+
* 단일 entry (top center). Box 의 dimensions 가 작아서 forklift fork 보다는
|
|
45
|
+
* gripper 가 일반적. forklift 로 들어올릴 box 는 통상 pallet 위에 stacking
|
|
46
|
+
* 후 pallet 째로 운반.
|
|
47
|
+
*
|
|
48
|
+
* tolerance 가 pallet 보다 빡빡 (gripper 정밀도 vs forklift pocket 폭).
|
|
49
|
+
*/
|
|
50
|
+
pickupFrames(): PickupFrame[];
|
|
35
51
|
}
|
|
36
52
|
export {};
|
package/dist/box.js
CHANGED
|
@@ -2,7 +2,7 @@ import { __decorate } from "tslib";
|
|
|
2
2
|
/*
|
|
3
3
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
4
4
|
*/
|
|
5
|
-
import { RectPath, Shape, sceneComponent } from '@hatiolab/things-scene';
|
|
5
|
+
import { RectPath, Shape, topApproachFrame, getWorldPose, sceneComponent } from '@hatiolab/things-scene';
|
|
6
6
|
import { Carriable, Legendable, Placeable } from '@operato/scene-base';
|
|
7
7
|
import { Box3D } from './box-3d.js';
|
|
8
8
|
const BODY_LEGEND = {
|
|
@@ -64,6 +64,33 @@ let Box = class Box extends Carriable(Legendable(Placeable(RectPath(Shape)))) {
|
|
|
64
64
|
buildRealObject() {
|
|
65
65
|
return new Box3D(this);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Phase H — pickup contract. Box 는 위에서 gripper / vacuum cup 으로 집기 —
|
|
69
|
+
* 단일 entry (top center). Box 의 dimensions 가 작아서 forklift fork 보다는
|
|
70
|
+
* gripper 가 일반적. forklift 로 들어올릴 box 는 통상 pallet 위에 stacking
|
|
71
|
+
* 후 pallet 째로 운반.
|
|
72
|
+
*
|
|
73
|
+
* tolerance 가 pallet 보다 빡빡 (gripper 정밀도 vs forklift pocket 폭).
|
|
74
|
+
*/
|
|
75
|
+
pickupFrames() {
|
|
76
|
+
const wp = getWorldPose(this);
|
|
77
|
+
const me = {
|
|
78
|
+
position: { x: wp.position.x, y: wp.position.y, z: wp.position.z },
|
|
79
|
+
rotation: { x: wp.rotation.x, y: wp.rotation.y, z: wp.rotation.z, w: wp.rotation.w }
|
|
80
|
+
};
|
|
81
|
+
const boxDepth = this.constructor.defaultDepth ?? 300;
|
|
82
|
+
return [
|
|
83
|
+
topApproachFrame({
|
|
84
|
+
carrierWorld: me,
|
|
85
|
+
topY: boxDepth, // Box top in carrier-local Y (depth = full height; top at depth)
|
|
86
|
+
approachDistance: 50, // gripper 가 hover 하는 거리
|
|
87
|
+
toolType: 'gripper',
|
|
88
|
+
tolerance: { positionMm: 5, angleDeg: 1 },
|
|
89
|
+
priority: 0,
|
|
90
|
+
id: 'top-gripper'
|
|
91
|
+
})
|
|
92
|
+
];
|
|
93
|
+
}
|
|
67
94
|
};
|
|
68
95
|
Box = __decorate([
|
|
69
96
|
sceneComponent('box')
|
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":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\nimport {\n ComponentNature,\n RealObject,\n RectPath,\n Shape,\n topApproachFrame,\n getWorldPose,\n sceneComponent\n} from '@hatiolab/things-scene'\nimport type { State, Material3D, PickupFrame, PoseSerialized } 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\n/** Box 컴포넌트 state */\nexport interface BoxState extends State {\n // ── 외관 ──\n material?: BoxMaterial\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\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 declare state: BoxState\n\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)\n }\n\n /**\n * Phase H — pickup contract. Box 는 위에서 gripper / vacuum cup 으로 집기 —\n * 단일 entry (top center). Box 의 dimensions 가 작아서 forklift fork 보다는\n * gripper 가 일반적. forklift 로 들어올릴 box 는 통상 pallet 위에 stacking\n * 후 pallet 째로 운반.\n *\n * tolerance 가 pallet 보다 빡빡 (gripper 정밀도 vs forklift pocket 폭).\n */\n pickupFrames(): PickupFrame[] {\n const wp = getWorldPose(this)\n const me: PoseSerialized = {\n position: { x: wp.position.x, y: wp.position.y, z: wp.position.z },\n rotation: { x: wp.rotation.x, y: wp.rotation.y, z: wp.rotation.z, w: wp.rotation.w }\n }\n const boxDepth = (this.constructor as any).defaultDepth ?? 300\n\n return [\n topApproachFrame({\n carrierWorld: me,\n topY: boxDepth, // Box top in carrier-local Y (depth = full height; top at depth)\n approachDistance: 50, // gripper 가 hover 하는 거리\n toolType: 'gripper',\n tolerance: { positionMm: 5, angleDeg: 1 },\n priority: 0,\n id: 'top-gripper'\n })\n ]\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic-container-3d.js","sourceRoot":"","sources":["../src/generic-container-3d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,MAAM,OAAO,kBAAmB,SAAQ,cAAc;IAC1C,SAAS,CAAC,IAAS;QAC3B,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACrB,IAAI,CAAC,eAAe,EAAE,CAAA;IACxB,CAAC;IAEO,eAAe;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"generic-container-3d.js","sourceRoot":"","sources":["../src/generic-container-3d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,MAAM,OAAO,kBAAmB,SAAQ,cAAc;IAC1C,SAAS,CAAC,IAAS;QAC3B,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACrB,IAAI,CAAC,eAAe,EAAE,CAAA;IACxB,CAAC;IAEO,eAAe;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAA;QAClC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IAC7D,CAAC;IAED,QAAQ,CAAC,KAA8B,EAAE,MAA+B;QACtE,IAAI,gBAAgB,IAAI,KAAK,IAAI,WAAW,IAAI,KAAK,EAAE,CAAC;YACtD,IAAI,CAAC,eAAe,EAAE,CAAA;YACtB,OAAM;QACR,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC/B,CAAC;CACF","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * GenericContainer3D — RealObjectGLTF 상속, 보관소 (Stocker / Buffer / Rack 류) 의 3D 표현.\n *\n * 상속 체인:\n * RealObjectGroup → RealObjectExternalModel → RealObjectGLTF → GenericContainer3D\n *\n * 재사용:\n * - GLB load (`_loadExternal` + 캐시)\n * - node index (`getNode`, `nodeNames`)\n * - 노드 상태 매핑 (`nodes` state → color/visible/opacity)\n * - 애니메이션 (`animations` / `playTargets`)\n * - top-view 스냅샷 (2D 렌더용)\n * - dispose / clear\n *\n * 추가:\n * - actuator (state 값 → 노드 transform). rack 의 lift, shutter 열림 등 동적 표현.\n *\n * GenericTransport3D 와 차이: mount / cargo reparent 미사용 (cargo holder 가 아니라 placeholder\n * 단계의 정적 보관소). cargo 보유는 사용자가 구체 type 으로 변환 후에 부여.\n */\n\nimport { RealObjectGLTF } from '@hatiolab/things-scene'\nimport { applyActuators } from '@operato/scene-base'\n\nexport class GenericContainer3D extends RealObjectGLTF {\n protected _onLoaded(gltf: any) {\n super._onLoaded(gltf)\n this._applyActuators()\n }\n\n private _applyActuators() {\n const state = this.component.state\n applyActuators(this, state.actuators, state.actuatorValues)\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('actuatorValues' in after || 'actuators' in after) {\n this._applyActuators()\n return\n }\n super.onchange(after, before)\n }\n}\n"]}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { Component, ComponentNature } from '@hatiolab/things-scene';
|
|
2
|
+
import type { State, Material3D } from '@hatiolab/things-scene';
|
|
2
3
|
import { type Alignment, type Heights, type LegendBinding, type PlacementArchetype } from '@operato/scene-base';
|
|
3
4
|
import { GenericContainer3D } from './generic-container-3d.js';
|
|
4
5
|
import type { ActuatorDef } from '@operato/scene-base';
|
|
5
6
|
export type ContainerStatus = 'empty' | 'partial' | 'full' | 'error';
|
|
7
|
+
export type ContainerFill = ContainerStatus;
|
|
8
|
+
/** GenericContainer 컴포넌트 state */
|
|
9
|
+
export interface GenericContainerState extends State {
|
|
10
|
+
fill?: ContainerFill;
|
|
11
|
+
actuators?: Record<string, ActuatorDef>;
|
|
12
|
+
actuatorValues?: Record<string, number>;
|
|
13
|
+
material3d?: Material3D;
|
|
14
|
+
}
|
|
6
15
|
declare const GenericContainer_base: any;
|
|
7
16
|
export default class GenericContainer extends GenericContainer_base {
|
|
17
|
+
state: GenericContainerState;
|
|
8
18
|
static legends: Record<string, LegendBinding>;
|
|
9
19
|
static placement: PlacementArchetype;
|
|
10
20
|
static align: Alignment;
|
|
@@ -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;
|
|
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;AAE/B,OAAO,EACL,UAAU,EACV,SAAS,EAKV,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAmB9D,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;IAGnG,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,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAA;IACnC,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAA;IACxC,CAAC;IAED,WAAW,CAAC,SAAoB;QAC9B,OAAO,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;IACtC,CAAC;IAED,eAAe;QACb,OAAO,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC;;AAlCkB,gBAAgB;IADpC,cAAc,CAAC,WAAW,CAAC;GACP,gBAAgB,CAmCpC;eAnCoB,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 type { State, Material3D } 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'\nexport type ContainerFill = ContainerStatus\n\n/** GenericContainer 컴포넌트 state */\nexport interface GenericContainerState extends State {\n // ── 운영 상태 ──\n fill?: ContainerFill\n\n // ── GLB 동적 노드 ──\n actuators?: Record<string, ActuatorDef>\n actuatorValues?: Record<string, number>\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\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 declare state: GenericContainerState\n\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.actuators ?? {}\n }\n\n get actuatorValues(): Record<string, number> {\n return this.state.actuatorValues ?? {}\n }\n\n containable(component: Component): boolean {\n return component.isDescendible(this)\n }\n\n buildRealObject() {\n return new GenericContainer3D(this)\n }\n}\n"]}
|
package/dist/pallet.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, ComponentNature, RealObject } from '@hatiolab/things-scene';
|
|
2
|
+
import type { State, Material3D, PickupFrame } from '@hatiolab/things-scene';
|
|
2
3
|
import { type Alignment, type LegendBinding, type PlacementArchetype } from '@operato/scene-base';
|
|
3
4
|
/**
|
|
4
5
|
* Pallet material — drives both 2D fill color and 3D structure.
|
|
@@ -12,6 +13,11 @@ import { type Alignment, type LegendBinding, type PlacementArchetype } from '@op
|
|
|
12
13
|
* legend + a 3D variant in pallet-3d.ts.
|
|
13
14
|
*/
|
|
14
15
|
export type PalletMaterial = 'wood' | 'plastic';
|
|
16
|
+
/** Pallet 컴포넌트 state */
|
|
17
|
+
export interface PalletState extends State {
|
|
18
|
+
material?: PalletMaterial;
|
|
19
|
+
material3d?: Material3D;
|
|
20
|
+
}
|
|
15
21
|
declare const Pallet_base: any;
|
|
16
22
|
/**
|
|
17
23
|
* Pallet — a flat transport structure that goods are stacked and stored on.
|
|
@@ -40,6 +46,7 @@ declare const Pallet_base: any;
|
|
|
40
46
|
* detection.
|
|
41
47
|
*/
|
|
42
48
|
export default class Pallet extends Pallet_base {
|
|
49
|
+
state: PalletState;
|
|
43
50
|
static legends: Record<string, LegendBinding>;
|
|
44
51
|
static placement: PlacementArchetype;
|
|
45
52
|
static align: Alignment;
|
|
@@ -66,5 +73,18 @@ export default class Pallet extends Pallet_base {
|
|
|
66
73
|
postrender(ctx: CanvasRenderingContext2D): void;
|
|
67
74
|
get fillStyle(): string;
|
|
68
75
|
buildRealObject(): RealObject | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Phase H — pickup contract. EUR/EPAL pallet 의 fork 진입은 양 단면 (긴 변
|
|
78
|
+
* 두 군데). 'east' / 'west' = pallet 의 짧은 축 방향에서 fork 가 들어감.
|
|
79
|
+
*
|
|
80
|
+
* fork 진입 높이 (entryHeight) 는 pallet 의 fork pocket 위치 — pallet 의
|
|
81
|
+
* 바닥에서 약 50mm (top deck 와 bottom deck 사이의 stringer 영역). 표준
|
|
82
|
+
* EUR pallet 의 fork pocket 은 144mm 두께의 약 50% 지점.
|
|
83
|
+
*
|
|
84
|
+
* approachDistance 는 forklift 가 fork 끝을 pallet 에 닿기 직전 hover 자세 —
|
|
85
|
+
* pallet 길이만큼 떨어져서 fork 길이 (보통 1100mm) 가 다 들어가도록 한다.
|
|
86
|
+
* 여기선 conservative 하게 pallet 너비 + 200mm.
|
|
87
|
+
*/
|
|
88
|
+
pickupFrames(): PickupFrame[];
|
|
69
89
|
}
|
|
70
90
|
export {};
|
package/dist/pallet.js
CHANGED
|
@@ -2,7 +2,7 @@ import { __decorate } from "tslib";
|
|
|
2
2
|
/*
|
|
3
3
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
4
4
|
*/
|
|
5
|
-
import { ContainerAbstract, sceneComponent } from '@hatiolab/things-scene';
|
|
5
|
+
import { ContainerAbstract, rectangularFootprintFrames, getWorldPose, sceneComponent } from '@hatiolab/things-scene';
|
|
6
6
|
import { Carriable, Legendable, Placeable } from '@operato/scene-base';
|
|
7
7
|
import { Pallet3D } from './pallet-3d.js';
|
|
8
8
|
const BODY_LEGEND = {
|
|
@@ -155,6 +155,44 @@ let Pallet = class Pallet extends Carriable(Legendable(Placeable(ContainerAbstra
|
|
|
155
155
|
buildRealObject() {
|
|
156
156
|
return new Pallet3D(this);
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Phase H — pickup contract. EUR/EPAL pallet 의 fork 진입은 양 단면 (긴 변
|
|
160
|
+
* 두 군데). 'east' / 'west' = pallet 의 짧은 축 방향에서 fork 가 들어감.
|
|
161
|
+
*
|
|
162
|
+
* fork 진입 높이 (entryHeight) 는 pallet 의 fork pocket 위치 — pallet 의
|
|
163
|
+
* 바닥에서 약 50mm (top deck 와 bottom deck 사이의 stringer 영역). 표준
|
|
164
|
+
* EUR pallet 의 fork pocket 은 144mm 두께의 약 50% 지점.
|
|
165
|
+
*
|
|
166
|
+
* approachDistance 는 forklift 가 fork 끝을 pallet 에 닿기 직전 hover 자세 —
|
|
167
|
+
* pallet 길이만큼 떨어져서 fork 길이 (보통 1100mm) 가 다 들어가도록 한다.
|
|
168
|
+
* 여기선 conservative 하게 pallet 너비 + 200mm.
|
|
169
|
+
*/
|
|
170
|
+
pickupFrames() {
|
|
171
|
+
const { width = 1200, height = 800 } = this.state;
|
|
172
|
+
const palletDepth = this.constructor.defaultDepth ?? 150;
|
|
173
|
+
// 4-way pallet: 모든 면에서 fork 진입 가능. 2-way 는 sides 를 ['east','west']
|
|
174
|
+
// 로 한정 — pallet 의 stringer 방향에 따라 다르나 default 는 4-way 가정.
|
|
175
|
+
// (state.palletType 같은 속성 추가 시 분기 가능 — 현재는 4-way 가정.)
|
|
176
|
+
const me = (() => {
|
|
177
|
+
const wp = getWorldPose(this);
|
|
178
|
+
return {
|
|
179
|
+
position: { x: wp.position.x, y: wp.position.y, z: wp.position.z },
|
|
180
|
+
rotation: { x: wp.rotation.x, y: wp.rotation.y, z: wp.rotation.z, w: wp.rotation.w }
|
|
181
|
+
};
|
|
182
|
+
})();
|
|
183
|
+
const longerAxis = Math.max(width, height);
|
|
184
|
+
return rectangularFootprintFrames({
|
|
185
|
+
carrierWorld: me,
|
|
186
|
+
width,
|
|
187
|
+
depth: height,
|
|
188
|
+
entryHeight: palletDepth * 0.4, // fork pocket 중심
|
|
189
|
+
approachDistance: longerAxis + 200,
|
|
190
|
+
sides: ['east', 'west', 'north', 'south'],
|
|
191
|
+
toolType: 'forklift-fork',
|
|
192
|
+
tolerance: { positionMm: 50, angleDeg: 5 },
|
|
193
|
+
priority: 0
|
|
194
|
+
});
|
|
195
|
+
}
|
|
158
196
|
};
|
|
159
197
|
Pallet = __decorate([
|
|
160
198
|
sceneComponent('pallet')
|