@operato/scene-storage 10.0.0-beta.40 → 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.
- package/CHANGELOG.md +17 -0
- package/dist/box.js +18 -0
- package/dist/box.js.map +1 -1
- package/dist/crane-3d.d.ts +47 -2
- package/dist/crane-3d.js +246 -89
- package/dist/crane-3d.js.map +1 -1
- package/dist/crane.d.ts +96 -12
- package/dist/crane.js +395 -100
- package/dist/crane.js.map +1 -1
- package/dist/pallet.d.ts +15 -0
- package/dist/pallet.js +38 -2
- package/dist/pallet.js.map +1 -1
- package/dist/parcel-3d.js +22 -18
- package/dist/parcel-3d.js.map +1 -1
- package/dist/parcel.d.ts +4 -3
- package/dist/parcel.js +24 -5
- package/dist/parcel.js.map +1 -1
- package/dist/storage-cell.d.ts +5 -2
- package/dist/storage-cell.js +21 -3
- package/dist/storage-cell.js.map +1 -1
- package/dist/storage-rack-3d.js +42 -7
- package/dist/storage-rack-3d.js.map +1 -1
- package/dist/storage-rack.d.ts +26 -2
- package/dist/storage-rack.js +92 -10
- package/dist/storage-rack.js.map +1 -1
- package/package.json +3 -3
- package/src/box.ts +18 -0
- package/src/crane-3d.ts +258 -93
- package/src/crane.ts +445 -110
- package/src/pallet.ts +50 -1
- package/src/parcel-3d.ts +23 -18
- package/src/parcel.ts +24 -5
- package/src/storage-cell.ts +23 -3
- package/src/storage-rack-3d.ts +47 -8
- package/src/storage-rack.ts +110 -10
- package/test/test-cell-position.ts +105 -0
- package/test/test-crane-geometry.ts +167 -0
- package/test/test-phase-h-carrier-pickable.ts +4 -3
- package/translations/en.json +5 -1
- package/translations/ja.json +5 -1
- package/translations/ko.json +5 -1
- package/translations/ms.json +5 -1
- package/translations/zh.json +5 -1
- 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 별
|
|
97
|
-
it('Pallet 은 forklift-fork
|
|
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
|
})
|
package/translations/en.json
CHANGED
|
@@ -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.
|
|
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",
|
package/translations/ja.json
CHANGED
|
@@ -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.
|
|
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": "移動中",
|
package/translations/ko.json
CHANGED
|
@@ -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.
|
|
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": "이동중",
|
package/translations/ms.json
CHANGED
|
@@ -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.
|
|
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",
|
package/translations/zh.json
CHANGED
|
@@ -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.
|
|
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": "移动中",
|