@operato/scene-storage 10.0.0-beta.44 → 10.0.0-beta.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/crane-3d.d.ts +10 -0
  3. package/dist/crane-3d.js +34 -5
  4. package/dist/crane-3d.js.map +1 -1
  5. package/dist/crane.d.ts +136 -6
  6. package/dist/crane.js +578 -48
  7. package/dist/crane.js.map +1 -1
  8. package/dist/parcel-3d.d.ts +1 -0
  9. package/dist/parcel-3d.js +18 -1
  10. package/dist/parcel-3d.js.map +1 -1
  11. package/dist/rack-grid-3d.js +26 -8
  12. package/dist/rack-grid-3d.js.map +1 -1
  13. package/dist/rack-grid.d.ts +103 -10
  14. package/dist/rack-grid.js +484 -86
  15. package/dist/rack-grid.js.map +1 -1
  16. package/dist/storage-rack-3d.js +1 -1
  17. package/dist/storage-rack-3d.js.map +1 -1
  18. package/dist/storage-rack.d.ts +40 -6
  19. package/dist/storage-rack.js +111 -14
  20. package/dist/storage-rack.js.map +1 -1
  21. package/package.json +4 -4
  22. package/src/crane-3d.ts +34 -4
  23. package/src/crane.ts +625 -57
  24. package/src/parcel-3d.ts +19 -1
  25. package/src/rack-grid-3d.ts +31 -8
  26. package/src/rack-grid.ts +504 -82
  27. package/src/storage-rack-3d.ts +1 -1
  28. package/src/storage-rack.ts +111 -14
  29. package/test/test-coord-alignment.ts +2 -2
  30. package/test/test-crane-bay-match.ts +130 -0
  31. package/test/test-crane-binding-resolve.ts +168 -0
  32. package/test/test-crane-duration.ts +90 -0
  33. package/test/test-crane-rotation-reach.ts +218 -0
  34. package/test/test-rack-grid-3d-alignment.ts +235 -0
  35. package/test/test-rack-grid-3d-attach-real.ts +375 -0
  36. package/test/test-rack-grid-cell.ts +2 -2
  37. package/test/test-rack-grid-location.ts +2 -2
  38. package/test/test-rack-grid-occupied-slots.ts +165 -0
  39. package/test/test-rack-grid-picking-position.ts +154 -0
  40. package/test/test-rack-grid-slot-api.ts +483 -0
  41. package/test/test-slot-ids-enumeration.ts +137 -0
  42. package/test/things-scene-loader-impl.mjs +37 -0
  43. package/test/things-scene-loader.mjs +24 -0
  44. package/translations/en.json +2 -0
  45. package/translations/ja.json +2 -0
  46. package/translations/ko.json +2 -0
  47. package/translations/ms.json +2 -0
  48. package/translations/zh.json +2 -0
  49. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,137 @@
1
+ /*
2
+ * SlottedHolder.slotIds() — capability 기반 enumeration 검증.
3
+ *
4
+ * Phase 0 의 핵심: rack 종류 무관 (StorageRack / RackGrid) 으로 *모든 slot id 목록*
5
+ * 을 얻는 능력. 인접 slot 발견, simulate, audit 등의 entry point.
6
+ */
7
+
8
+ import 'should'
9
+ import StorageRack from '../src/storage-rack.js'
10
+ import RackGrid from '../src/rack-grid.js'
11
+
12
+ // inline duck-type — scene-base 의 src import 가 test 환경의 Component named
13
+ // import 와 충돌 (things-scene CJS↔ESM) 회피.
14
+ function isSlottedHolder(x: any): boolean {
15
+ return !!x &&
16
+ typeof x.hasCarrierAt === 'function' &&
17
+ typeof x.obtainCarrier === 'function' &&
18
+ typeof x.canReceiveAt === 'function' &&
19
+ typeof x.receiveAt === 'function' &&
20
+ typeof x.getSlotAttachObject3d === 'function' &&
21
+ typeof x.slotIds === 'function'
22
+ }
23
+
24
+ // ── Mock things-scene env — Component constructor 에 필요한 최소 _app ────────
25
+
26
+ function makeApp(): any {
27
+ return {
28
+ register: () => {},
29
+ components: [],
30
+ layout: 'absolute'
31
+ }
32
+ }
33
+
34
+ // ──────────────────────────────────────────────────────────────────────────
35
+
36
+ describe('SlottedHolder.slotIds — capability enumeration', () => {
37
+ describe('StorageRack', () => {
38
+ it('default bays=5, levels=4 → 20 slots', () => {
39
+ const rack = new StorageRack({ bays: 5, levels: 4 } as any, makeApp())
40
+ const ids = rack.slotIds()
41
+ ids.length.should.equal(20)
42
+ })
43
+
44
+ it('bays=3, levels=2 → 6 slots, "0-0-0".."2-0-1"', () => {
45
+ const rack = new StorageRack({ bays: 3, levels: 2 } as any, makeApp())
46
+ const ids = rack.slotIds()
47
+ ids.length.should.equal(6)
48
+ // bay 순회 → level 순회
49
+ ids.should.containEql('0-0-0')
50
+ ids.should.containEql('0-0-1')
51
+ ids.should.containEql('2-0-0')
52
+ ids.should.containEql('2-0-1')
53
+ })
54
+
55
+ it('id format = slotIdOf(bay, 1, level) → "0-based"', () => {
56
+ const rack = new StorageRack({ bays: 2, levels: 2 } as any, makeApp())
57
+ const ids = rack.slotIds()
58
+ ids.should.containEql(rack.slotIdOf(1, 1, 1)) // "0-0-0"
59
+ ids.should.containEql(rack.slotIdOf(2, 1, 2)) // "1-0-1"
60
+ })
61
+
62
+ it('default state (no bays/levels) → 5×4 = 20', () => {
63
+ const rack = new StorageRack({} as any, makeApp())
64
+ rack.slotIds().length.should.equal(20)
65
+ })
66
+ })
67
+
68
+ describe('RackGrid', () => {
69
+ it('default columns=5, rows=3, shelves=4 → 60 slots', () => {
70
+ const rg = new RackGrid({ columns: 5, rows: 3, shelves: 4 } as any, makeApp())
71
+ const ids = rg.slotIds()
72
+ ids.length.should.equal(60)
73
+ })
74
+
75
+ it('columns=2, rows=1, shelves=3 → 6 slots', () => {
76
+ const rg = new RackGrid({ columns: 2, rows: 1, shelves: 3 } as any, makeApp())
77
+ const ids = rg.slotIds()
78
+ ids.length.should.equal(6)
79
+ ids.should.containEql('0-0-0')
80
+ ids.should.containEql('0-0-1')
81
+ ids.should.containEql('0-0-2')
82
+ ids.should.containEql('1-0-0')
83
+ ids.should.containEql('1-0-1')
84
+ ids.should.containEql('1-0-2')
85
+ })
86
+
87
+ it('id format = "col-row-shelf" (0-based)', () => {
88
+ const rg = new RackGrid({ columns: 2, rows: 2, shelves: 2 } as any, makeApp())
89
+ const ids = rg.slotIds()
90
+ // 마지막 col, row, shelf
91
+ ids.should.containEql('1-1-1')
92
+ })
93
+
94
+ it('cellOverrides[posKey].isEmpty=true 인 bay 는 enumeration 에서 제외', () => {
95
+ const rg = new RackGrid({
96
+ columns: 2,
97
+ rows: 1,
98
+ shelves: 2,
99
+ cellOverrides: {
100
+ '1-0': { isEmpty: true } // col=1, row=0 bay 가 isEmpty
101
+ }
102
+ } as any, makeApp())
103
+ const ids = rg.slotIds()
104
+ // col=0 만 — 2 shelves × 1 col × 1 row = 2 slots
105
+ ids.length.should.equal(2)
106
+ ids.should.containEql('0-0-0')
107
+ ids.should.containEql('0-0-1')
108
+ // col=1 의 slot 은 *없어야*
109
+ ids.should.not.containEql('1-0-0')
110
+ ids.should.not.containEql('1-0-1')
111
+ })
112
+ })
113
+
114
+ describe('isSlottedHolder duck-type 가 slotIds 도 검사', () => {
115
+ it('StorageRack 인스턴스는 SlottedHolder 로 인식', () => {
116
+ const rack = new StorageRack({ bays: 2, levels: 2 } as any, makeApp())
117
+ isSlottedHolder(rack).should.be.true()
118
+ })
119
+
120
+ it('RackGrid 인스턴스는 SlottedHolder 로 인식', () => {
121
+ const rg = new RackGrid({ columns: 2, rows: 1, shelves: 2 } as any, makeApp())
122
+ isSlottedHolder(rg).should.be.true()
123
+ })
124
+
125
+ it('slotIds 미구현 plain object 는 SlottedHolder 아님', () => {
126
+ const fake = {
127
+ hasCarrierAt() { return false },
128
+ obtainCarrier() { return null },
129
+ canReceiveAt() { return true },
130
+ receiveAt() {},
131
+ getSlotAttachObject3d() { return undefined }
132
+ // slotIds 빠짐
133
+ }
134
+ isSlottedHolder(fake).should.be.false()
135
+ })
136
+ })
137
+ })
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Things-scene loader implementation — `@hatiolab/things-scene` 의 Node resolve
3
+ * 를 browser ESM bundle (things-scene.mjs) 로 redirect.
4
+ *
5
+ * 이 module 은 register() 의 대상. 별 thread context 에서 Node 의 module resolution
6
+ * hook 으로 동작.
7
+ */
8
+
9
+ import { pathToFileURL, fileURLToPath } from 'node:url'
10
+ import { resolve as pathResolve, dirname } from 'node:path'
11
+
12
+ // 처음 import resolve 시점에 things-scene 의 browser ESM 경로 확정.
13
+ let cachedBrowserUrl = null
14
+
15
+ function getBrowserBundle() {
16
+ if (cachedBrowserUrl) return cachedBrowserUrl
17
+ // 현재 file 의 위치 — packages/storage/test/.
18
+ // things-scene bundle 위치 — operato-scene/node_modules/@hatiolab/things-scene/things-scene.mjs
19
+ const here = fileURLToPath(import.meta.url)
20
+ const root = pathResolve(dirname(here), '..', '..', '..') // operato-scene root
21
+ const browserBundle = pathResolve(root, 'node_modules', '@hatiolab', 'things-scene', 'things-scene.mjs')
22
+ cachedBrowserUrl = pathToFileURL(browserBundle).href
23
+ return cachedBrowserUrl
24
+ }
25
+
26
+ export async function resolve(specifier, context, nextResolve) {
27
+ // @hatiolab/things-scene 의 bare specifier 만 redirect.
28
+ // subpath 또는 deep import 는 default 동작 유지.
29
+ if (specifier === '@hatiolab/things-scene') {
30
+ return {
31
+ url: getBrowserBundle(),
32
+ format: 'module',
33
+ shortCircuit: true
34
+ }
35
+ }
36
+ return nextResolve(specifier, context)
37
+ }
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Node ESM loader — @hatiolab/things-scene 의 "node" condition 우회.
3
+ *
4
+ * Issue:
5
+ * things-scene package.json 의 exports 가 Node 우선 things-scene-transfer.mjs
6
+ * (minimal Node bundle) — Component 가 export 되지 않음. scene-config.ts /
7
+ * placeable.ts 등 SceneConfig 기반 코드 load 시 ESM error 발생.
8
+ *
9
+ * Fix:
10
+ * 이 loader 가 `@hatiolab/things-scene` 의 import 를 가로채 things-scene.mjs
11
+ * (browser ESM, Component 포함) 로 redirect. test/setup.js 의 polyfill 들이
12
+ * browser 글로벌을 대신.
13
+ *
14
+ * Activation:
15
+ * mocha --node-option="import=./test/things-scene-loader.mjs" ...
16
+ * 또는 node --import ./test/things-scene-loader.mjs
17
+ */
18
+
19
+ import { register } from 'node:module'
20
+ import { pathToFileURL, fileURLToPath } from 'node:url'
21
+ import { dirname, resolve as pathResolve } from 'node:path'
22
+
23
+ const loaderUrl = new URL('./things-scene-loader-impl.mjs', import.meta.url)
24
+ register(loaderUrl, import.meta.url)
@@ -42,6 +42,8 @@
42
42
 
43
43
  "label.status": "status",
44
44
  "label.simulate": "simulate",
45
+ "label.speed": "speed",
46
+ "label.bound-holders": "bound holders",
45
47
  "label.carriage-position": "carriage position",
46
48
  "label.carriage-width": "carriage width",
47
49
  "label.carriage-height": "carriage height",
@@ -42,6 +42,8 @@
42
42
 
43
43
  "label.status": "状態",
44
44
  "label.simulate": "シミュレーション",
45
+ "label.speed": "速度",
46
+ "label.bound-holders": "バインドホルダー",
45
47
  "label.carriage-position": "キャリッジ位置",
46
48
  "label.carriage-width": "キャリッジ幅",
47
49
  "label.carriage-height": "キャリッジ高さ",
@@ -42,6 +42,8 @@
42
42
 
43
43
  "label.status": "상태",
44
44
  "label.simulate": "시뮬레이션",
45
+ "label.speed": "속도",
46
+ "label.bound-holders": "바인딩된 홀더",
45
47
  "label.carriage-position": "캐리지 위치",
46
48
  "label.carriage-width": "캐리지 폭",
47
49
  "label.carriage-height": "캐리지 높이",
@@ -42,6 +42,8 @@
42
42
 
43
43
  "label.status": "status",
44
44
  "label.simulate": "simulasi",
45
+ "label.speed": "kelajuan",
46
+ "label.bound-holders": "pemegang terikat",
45
47
  "label.carriage-position": "kedudukan pembawa",
46
48
  "label.carriage-width": "lebar pembawa",
47
49
  "label.carriage-height": "tinggi pembawa",
@@ -42,6 +42,8 @@
42
42
 
43
43
  "label.status": "状态",
44
44
  "label.simulate": "模拟",
45
+ "label.speed": "速度",
46
+ "label.bound-holders": "绑定的容器",
45
47
  "label.carriage-position": "载具位置",
46
48
  "label.carriage-width": "载具宽度",
47
49
  "label.carriage-height": "载具高度",