@operato/scene-storage 10.0.0-beta.38 → 10.0.0-beta.41

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 (44) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/box.js +18 -0
  3. package/dist/box.js.map +1 -1
  4. package/dist/crane-3d.d.ts +47 -2
  5. package/dist/crane-3d.js +246 -89
  6. package/dist/crane-3d.js.map +1 -1
  7. package/dist/crane.d.ts +96 -12
  8. package/dist/crane.js +395 -100
  9. package/dist/crane.js.map +1 -1
  10. package/dist/pallet.d.ts +15 -0
  11. package/dist/pallet.js +38 -2
  12. package/dist/pallet.js.map +1 -1
  13. package/dist/parcel-3d.js +22 -18
  14. package/dist/parcel-3d.js.map +1 -1
  15. package/dist/parcel.d.ts +4 -3
  16. package/dist/parcel.js +24 -5
  17. package/dist/parcel.js.map +1 -1
  18. package/dist/storage-cell.d.ts +5 -2
  19. package/dist/storage-cell.js +21 -3
  20. package/dist/storage-cell.js.map +1 -1
  21. package/dist/storage-rack-3d.js +42 -7
  22. package/dist/storage-rack-3d.js.map +1 -1
  23. package/dist/storage-rack.d.ts +26 -2
  24. package/dist/storage-rack.js +92 -10
  25. package/dist/storage-rack.js.map +1 -1
  26. package/package.json +3 -3
  27. package/src/box.ts +18 -0
  28. package/src/crane-3d.ts +258 -93
  29. package/src/crane.ts +445 -110
  30. package/src/pallet.ts +50 -1
  31. package/src/parcel-3d.ts +23 -18
  32. package/src/parcel.ts +24 -5
  33. package/src/storage-cell.ts +23 -3
  34. package/src/storage-rack-3d.ts +47 -8
  35. package/src/storage-rack.ts +110 -10
  36. package/test/test-cell-position.ts +105 -0
  37. package/test/test-crane-geometry.ts +167 -0
  38. package/test/test-phase-h-carrier-pickable.ts +4 -3
  39. package/translations/en.json +5 -1
  40. package/translations/ja.json +5 -1
  41. package/translations/ko.json +5 -1
  42. package/translations/ms.json +5 -1
  43. package/translations/zh.json +5 -1
  44. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,167 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * Crane 3D 의 기하학 공식 정합 검증 — *pure* unit test.
5
+ *
6
+ * 시뮬 시각 결함 진단 위한 *수학적 정합 sanity check*. carrier 위치/자세
7
+ * 결함이 *기하학 공식 mismatch* 인지 *3D scene-graph 의 다른 결함* 인지
8
+ * 좁힘.
9
+ *
10
+ * Forward 공식 (Crane3D.build):
11
+ * carriageH = S * 0.12 (S = min(width, height))
12
+ * bladeH = carriageH * 0.35
13
+ * liftGroup.y crane-local = baseTrolleyY + baseH/2 + carriageHeight + forkLift + carriageH/2
14
+ * _carrierBaseY (liftGroup-local) = -bladeH/2 (fork blade bottom)
15
+ * carrier 외부 bottom crane-local = liftGroup.y + _carrierBaseY
16
+ * carrier 외부 bottom world Y = craneCenterWorldY + crane-local
17
+ *
18
+ * Inverse (Crane3D.solveCarriageHeightForCarrierBaseWorldY):
19
+ * carriageHeight = worldY − craneCenterY − (baseTrolleyY + baseH/2 + carriageH/2 − bladeH/2) − forkLift
20
+ */
21
+
22
+ import 'should'
23
+
24
+ interface CraneParams {
25
+ S: number // min(width, height)
26
+ D: number // depth
27
+ }
28
+
29
+ interface DerivedParams {
30
+ carriageH: number
31
+ bladeH: number
32
+ baseH: number
33
+ railH: number
34
+ baseTrolleyY: number // crane-local (= -D/2 + railH + baseH/2)
35
+ }
36
+
37
+ function derive(p: CraneParams): DerivedParams {
38
+ const carriageH = p.S * 0.12
39
+ const bladeH = carriageH * 0.35
40
+ const baseH = p.S * 0.18
41
+ const railH = p.S * 0.04
42
+ const baseTrolleyY = -p.D / 2 + railH + baseH / 2
43
+ return { carriageH, bladeH, baseH, railH, baseTrolleyY }
44
+ }
45
+
46
+ // Forward: build 공식
47
+ function carrierBaseWorldY(
48
+ d: DerivedParams,
49
+ carriageHeight: number,
50
+ forkLift: number,
51
+ craneCenterY: number
52
+ ): number {
53
+ const liftGroupY = d.baseTrolleyY + d.baseH / 2 + carriageHeight + forkLift + d.carriageH / 2
54
+ const carrierBaseLocal = liftGroupY + (-d.bladeH / 2)
55
+ return craneCenterY + carrierBaseLocal
56
+ }
57
+
58
+ // Inverse: solveCarriageHeightForCarrierBaseWorldY
59
+ function solveCarriageHeight(
60
+ d: DerivedParams,
61
+ carrierBaseWorld: number,
62
+ forkLift: number,
63
+ craneCenterY: number
64
+ ): number {
65
+ return carrierBaseWorld - craneCenterY - (d.baseTrolleyY + d.baseH / 2 + d.carriageH / 2 - d.bladeH / 2) - forkLift
66
+ }
67
+
68
+ describe('Crane3D — geometry roundtrip', () => {
69
+ const params: CraneParams = { S: 400, D: 1000 }
70
+ const d = derive(params)
71
+
72
+ it('derived parameter values', () => {
73
+ d.carriageH.should.be.approximately(48, 0.001)
74
+ d.bladeH.should.be.approximately(16.8, 0.001)
75
+ d.baseH.should.be.approximately(72, 0.001)
76
+ d.railH.should.be.approximately(16, 0.001)
77
+ // baseTrolleyY = -500 + 16 + 36 = -448
78
+ d.baseTrolleyY.should.be.approximately(-448, 0.001)
79
+ })
80
+
81
+ it('forward → inverse roundtrip — forkLift=0', () => {
82
+ const craneCenterY = 0
83
+ const forkLift = 0
84
+ for (const ch of [0, 100, 200, 500, 800]) {
85
+ const baseWorld = carrierBaseWorldY(d, ch, forkLift, craneCenterY)
86
+ const recovered = solveCarriageHeight(d, baseWorld, forkLift, craneCenterY)
87
+ recovered.should.be.approximately(ch, 0.001)
88
+ }
89
+ })
90
+
91
+ it('forward → inverse roundtrip — forkLift>0 (들린 상태)', () => {
92
+ const craneCenterY = 0
93
+ const forkLift = 30
94
+ for (const ch of [0, 100, 500]) {
95
+ const baseWorld = carrierBaseWorldY(d, ch, forkLift, craneCenterY)
96
+ const recovered = solveCarriageHeight(d, baseWorld, forkLift, craneCenterY)
97
+ recovered.should.be.approximately(ch, 0.001)
98
+ }
99
+ })
100
+
101
+ it('forward → inverse — craneCenterY 보정 (crane.zPos != 0)', () => {
102
+ const craneCenterY = 250
103
+ const forkLift = 0
104
+ const ch = 100
105
+ const baseWorld = carrierBaseWorldY(d, ch, forkLift, craneCenterY)
106
+ const recovered = solveCarriageHeight(d, baseWorld, forkLift, craneCenterY)
107
+ recovered.should.be.approximately(ch, 0.001)
108
+ })
109
+ })
110
+
111
+ describe('Crane3D — pick 시퀀스 기하학', () => {
112
+ const params: CraneParams = { S: 400, D: 1000 }
113
+ const d = derive(params)
114
+ const craneCenterY = 0
115
+
116
+ it('pick 진입: carriageHeight 산출 → carrier 외부 bottom = cellBottom', () => {
117
+ const cellBottom = 100 // cell shelf 면 world Y
118
+ const forkLift = 0 // pick 진입 시 forkLift 0
119
+
120
+ const carriageHeight = solveCarriageHeight(d, cellBottom, forkLift, craneCenterY)
121
+ const baseWorld = carrierBaseWorldY(d, carriageHeight, forkLift, craneCenterY)
122
+
123
+ baseWorld.should.be.approximately(cellBottom, 0.001)
124
+ })
125
+
126
+ it('pick lift 후: forkLift = liftH → carrier 외부 bottom = cellBottom + liftH', () => {
127
+ const cellBottom = 100
128
+ const liftH = 30
129
+ // 진입 carriageHeight (forkLift=0)
130
+ const ch = solveCarriageHeight(d, cellBottom, 0, craneCenterY)
131
+ // lift 후 forkLift = liftH, carriageHeight 그대로
132
+ const liftedBaseWorld = carrierBaseWorldY(d, ch, liftH, craneCenterY)
133
+ liftedBaseWorld.should.be.approximately(cellBottom + liftH, 0.001)
134
+ })
135
+ })
136
+
137
+ describe('Crane3D — place 시퀀스 기하학', () => {
138
+ const params: CraneParams = { S: 400, D: 1000 }
139
+ const d = derive(params)
140
+ const craneCenterY = 0
141
+
142
+ it('place 진입 (holding): approachWorldY = cellBottom + liftH', () => {
143
+ const cellBottom = 200 // 다른 cell
144
+ const liftH = 30
145
+ const forkLiftRT = liftH // 들린 상태로 도착
146
+
147
+ // approachWorldY = cellBottom + liftH (holding 보정)
148
+ const approachWorldY = cellBottom + liftH
149
+
150
+ const carriageHeight = solveCarriageHeight(d, approachWorldY, forkLiftRT, craneCenterY)
151
+ const baseWorld = carrierBaseWorldY(d, carriageHeight, forkLiftRT, craneCenterY)
152
+
153
+ baseWorld.should.be.approximately(approachWorldY, 0.001)
154
+ })
155
+
156
+ it('place lower 후: forkLift = 0 → carrier 외부 bottom = cellBottom', () => {
157
+ const cellBottom = 200
158
+ const liftH = 30
159
+ const approachWorldY = cellBottom + liftH
160
+
161
+ const ch = solveCarriageHeight(d, approachWorldY, liftH, craneCenterY)
162
+ // lower 후 forkLift = 0
163
+ const loweredBaseWorld = carrierBaseWorldY(d, ch, 0, craneCenterY)
164
+
165
+ loweredBaseWorld.should.be.approximately(cellBottom, 0.001)
166
+ })
167
+ })
@@ -93,13 +93,14 @@ describe('Phase H — Parcel pickupFrames', () => {
93
93
  })
94
94
  })
95
95
 
96
- describe('Phase H invariant — carrier 별 toolType 분리', () => {
97
- it('Pallet 은 forklift-fork 만, Box 는 gripper 만 — 자동 호환성 검사 통과', () => {
96
+ describe('Phase H invariant — carrier 별 primary tool', () => {
97
+ it('Pallet 의 primary tool 은 forklift-fork (gripper 없음), Box 는 gripper 포함', () => {
98
98
  const palletSrc = readSrc('../src/pallet.ts')
99
99
  const boxSrc = readSrc('../src/box.ts')
100
+ // Pallet 은 forklift-fork primary, gripper 직접 안 잡음 (Pallet 위 Box 를 잡음)
100
101
  palletSrc.should.match(/forklift-fork/)
101
102
  palletSrc.should.not.match(/toolType:\s*['"]gripper['"]/)
103
+ // Box 는 gripper 포함 (primary). 호환성 위해 agv-deck / forklift-fork 도 추가 가능.
102
104
  boxSrc.should.match(/gripper/)
103
- boxSrc.should.not.match(/forklift-fork/)
104
105
  })
105
106
  })
@@ -36,13 +36,17 @@
36
36
  "label.hide-rack-frame": "hide rack frame",
37
37
  "label.stock-scale": "stock scale",
38
38
  "label.legend-target": "legend target",
39
+ "label.shelf-base-height": "shelf base height",
39
40
 
40
41
  "label.status": "status",
41
- "label.target": "target",
42
+ "label.simulate": "simulate",
43
+ "label.carriage-position": "carriage position",
44
+ "label.carriage-width": "carriage width",
42
45
  "label.carriage-height": "carriage height",
43
46
  "label.fork-length": "fork length",
44
47
  "label.fork-extension": "fork extension",
45
48
  "label.fork-lift": "fork lift",
49
+ "label.pocket-depth": "pocket depth",
46
50
 
47
51
  "option.idle": "idle",
48
52
  "option.moving": "moving",
@@ -36,13 +36,17 @@
36
36
  "label.hide-rack-frame": "ラックフレームを非表示",
37
37
  "label.stock-scale": "在庫スケール",
38
38
  "label.legend-target": "凡例対象",
39
+ "label.shelf-base-height": "棚の基準高さ",
39
40
 
40
41
  "label.status": "状態",
41
- "label.target": "ターゲット",
42
+ "label.simulate": "シミュレーション",
43
+ "label.carriage-position": "キャリッジ位置",
44
+ "label.carriage-width": "キャリッジ幅",
42
45
  "label.carriage-height": "キャリッジ高さ",
43
46
  "label.fork-length": "フォーク長",
44
47
  "label.fork-extension": "フォーク伸縮",
45
48
  "label.fork-lift": "フォーク昇降",
49
+ "label.pocket-depth": "ポケットの深さ",
46
50
 
47
51
  "option.idle": "待機",
48
52
  "option.moving": "移動中",
@@ -36,13 +36,17 @@
36
36
  "label.hide-rack-frame": "랙 프레임 숨김",
37
37
  "label.stock-scale": "재고 크기",
38
38
  "label.legend-target": "범례 대상",
39
+ "label.shelf-base-height": "선반 시작 높이",
39
40
 
40
41
  "label.status": "상태",
41
- "label.target": "타깃",
42
+ "label.simulate": "시뮬레이션",
43
+ "label.carriage-position": "캐리지 위치",
44
+ "label.carriage-width": "캐리지 폭",
42
45
  "label.carriage-height": "캐리지 높이",
43
46
  "label.fork-length": "포크 길이",
44
47
  "label.fork-extension": "포크 신축",
45
48
  "label.fork-lift": "포크 들기",
49
+ "label.pocket-depth": "포켓 깊이",
46
50
 
47
51
  "option.idle": "대기",
48
52
  "option.moving": "이동중",
@@ -36,13 +36,17 @@
36
36
  "label.hide-rack-frame": "sembunyi rangka rak",
37
37
  "label.stock-scale": "skala stok",
38
38
  "label.legend-target": "sasaran legenda",
39
+ "label.shelf-base-height": "tinggi asas rak",
39
40
 
40
41
  "label.status": "status",
41
- "label.target": "sasaran",
42
+ "label.simulate": "simulasi",
43
+ "label.carriage-position": "kedudukan pembawa",
44
+ "label.carriage-width": "lebar pembawa",
42
45
  "label.carriage-height": "tinggi pembawa",
43
46
  "label.fork-length": "panjang garpu",
44
47
  "label.fork-extension": "lanjutan garpu",
45
48
  "label.fork-lift": "angkat garpu",
49
+ "label.pocket-depth": "kedalaman poket",
46
50
 
47
51
  "option.idle": "menunggu",
48
52
  "option.moving": "bergerak",
@@ -36,13 +36,17 @@
36
36
  "label.hide-rack-frame": "隐藏货架框架",
37
37
  "label.stock-scale": "库存比例",
38
38
  "label.legend-target": "图例对象",
39
+ "label.shelf-base-height": "货架基准高度",
39
40
 
40
41
  "label.status": "状态",
41
- "label.target": "目标",
42
+ "label.simulate": "模拟",
43
+ "label.carriage-position": "载具位置",
44
+ "label.carriage-width": "载具宽度",
42
45
  "label.carriage-height": "载具高度",
43
46
  "label.fork-length": "叉长",
44
47
  "label.fork-extension": "叉伸",
45
48
  "label.fork-lift": "叉升",
49
+ "label.pocket-depth": "口袋深度",
46
50
 
47
51
  "option.idle": "待机",
48
52
  "option.moving": "移动中",