@operato/scene-storage 10.0.0-beta.40 → 10.0.0-beta.42

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 (102) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/MIGRATION-plan-a-slot-api.md +266 -0
  3. package/PLAN-A-rack-as-slot-holder.md +164 -0
  4. package/dist/box.js +18 -0
  5. package/dist/box.js.map +1 -1
  6. package/dist/crane-3d.d.ts +47 -2
  7. package/dist/crane-3d.js +246 -89
  8. package/dist/crane-3d.js.map +1 -1
  9. package/dist/crane.d.ts +96 -12
  10. package/dist/crane.js +395 -100
  11. package/dist/crane.js.map +1 -1
  12. package/dist/index.d.ts +3 -4
  13. package/dist/index.js +1 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/pallet.d.ts +15 -0
  16. package/dist/pallet.js +38 -2
  17. package/dist/pallet.js.map +1 -1
  18. package/dist/parcel-3d.js +22 -18
  19. package/dist/parcel-3d.js.map +1 -1
  20. package/dist/parcel.d.ts +4 -3
  21. package/dist/parcel.js +24 -5
  22. package/dist/parcel.js.map +1 -1
  23. package/dist/rack-grid-3d.d.ts +18 -7
  24. package/dist/rack-grid-3d.js +372 -69
  25. package/dist/rack-grid-3d.js.map +1 -1
  26. package/dist/rack-grid-cell.d.ts +21 -72
  27. package/dist/rack-grid-cell.js +147 -243
  28. package/dist/rack-grid-cell.js.map +1 -1
  29. package/dist/rack-grid.d.ts +277 -56
  30. package/dist/rack-grid.js +1230 -695
  31. package/dist/rack-grid.js.map +1 -1
  32. package/dist/rack-materials.d.ts +9 -0
  33. package/dist/rack-materials.js +55 -0
  34. package/dist/rack-materials.js.map +1 -0
  35. package/dist/storage-rack-3d.d.ts +15 -0
  36. package/dist/storage-rack-3d.js +165 -29
  37. package/dist/storage-rack-3d.js.map +1 -1
  38. package/dist/storage-rack.d.ts +253 -32
  39. package/dist/storage-rack.js +726 -66
  40. package/dist/storage-rack.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/box.ts +18 -0
  43. package/src/crane-3d.ts +258 -93
  44. package/src/crane.ts +445 -110
  45. package/src/index.ts +3 -4
  46. package/src/pallet.ts +50 -1
  47. package/src/parcel-3d.ts +23 -18
  48. package/src/parcel.ts +24 -5
  49. package/src/rack-grid-3d.ts +383 -80
  50. package/src/rack-grid-cell.ts +161 -305
  51. package/src/rack-grid.ts +1263 -762
  52. package/src/rack-materials.ts +61 -0
  53. package/src/storage-rack-3d.ts +182 -29
  54. package/src/storage-rack.ts +819 -67
  55. package/test/test-carrier-lifecycle.ts +361 -0
  56. package/test/test-coord-alignment.ts +201 -0
  57. package/test/test-crane-geometry.ts +167 -0
  58. package/test/test-external-to-rack.ts +461 -0
  59. package/test/test-mover-concurrent-bug.ts +304 -0
  60. package/test/test-mover-rollback.ts +290 -0
  61. package/test/test-phase-h-carrier-pickable.ts +4 -3
  62. package/test/test-r19-place-absorb.ts +174 -0
  63. package/test/test-rack-3d-attach-real.ts +301 -0
  64. package/test/test-rack-concurrent.ts +254 -0
  65. package/test/test-rack-edge-cases.ts +323 -0
  66. package/test/test-rack-grid-cell.ts +318 -0
  67. package/test/test-rack-grid-location.ts +657 -0
  68. package/test/test-real-3d-positioning.ts +158 -0
  69. package/test/test-slot-center-convention.ts +116 -0
  70. package/test/test-slot-target.ts +189 -0
  71. package/test/test-storage-rack-batched.ts +606 -0
  72. package/test/test-storage-rack-click.ts +329 -0
  73. package/test/test-storage-rack-slot-api.ts +357 -0
  74. package/test/test-toscene-convention.ts +162 -0
  75. package/test/test-user-scenario-sequential.ts +334 -0
  76. package/translations/en.json +7 -1
  77. package/translations/ja.json +7 -1
  78. package/translations/ko.json +7 -1
  79. package/translations/ms.json +7 -1
  80. package/translations/zh.json +7 -1
  81. package/tsconfig.tsbuildinfo +1 -1
  82. package/dist/rack-column.d.ts +0 -35
  83. package/dist/rack-column.js +0 -258
  84. package/dist/rack-column.js.map +0 -1
  85. package/dist/rack-grid-helpers.d.ts +0 -28
  86. package/dist/rack-grid-helpers.js +0 -71
  87. package/dist/rack-grid-helpers.js.map +0 -1
  88. package/dist/rack-grid-location.d.ts +0 -37
  89. package/dist/rack-grid-location.js +0 -227
  90. package/dist/rack-grid-location.js.map +0 -1
  91. package/dist/storage-cell-3d.d.ts +0 -25
  92. package/dist/storage-cell-3d.js +0 -88
  93. package/dist/storage-cell-3d.js.map +0 -1
  94. package/dist/storage-cell.d.ts +0 -70
  95. package/dist/storage-cell.js +0 -197
  96. package/dist/storage-cell.js.map +0 -1
  97. package/src/rack-column.ts +0 -340
  98. package/src/rack-grid-helpers.ts +0 -77
  99. package/src/rack-grid-location.ts +0 -286
  100. package/src/storage-cell-3d.ts +0 -101
  101. package/src/storage-cell.ts +0 -247
  102. package/test/test-rack-grid.ts +0 -77
package/src/index.ts CHANGED
@@ -13,12 +13,11 @@ export { default as StorageRack } from './storage-rack.js'
13
13
  export type { StorageRackState } from './storage-rack.js'
14
14
  export { StorageRack3D } from './storage-rack-3d.js'
15
15
  export { default as RackGrid } from './rack-grid.js'
16
- export type { RackGridState } from './rack-grid.js'
16
+ export type { RackGridState, CellOverride } from './rack-grid.js'
17
17
  export { RackGrid3D } from './rack-grid-3d.js'
18
+ export { default as RackGridCell } from './rack-grid-cell.js'
19
+ export type { RackGridCellState } from './rack-grid-cell.js'
18
20
  export { default as MobileStorageRack } from './mobile-storage-rack.js'
19
- export { default as StorageCell } from './storage-cell.js'
20
- export type { StorageCellType } from './storage-cell.js'
21
- export { StorageCell3D } from './storage-cell-3d.js'
22
21
  export { default as Crane } from './crane.js'
23
22
  export type { CraneState, CraneStatus } from './crane.js'
24
23
  export { Crane3D } from './crane-3d.js'
package/src/pallet.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  RealObject,
9
9
  Pose6DOF,
10
10
  rectangularFootprintFrames,
11
+ topApproachFrame,
11
12
  getWorldPose,
12
13
  sceneComponent
13
14
  } from '@hatiolab/things-scene'
@@ -41,6 +42,15 @@ export interface PalletState extends State {
41
42
  // ── 외관 ──
42
43
  material?: PalletMaterial
43
44
 
45
+ /**
46
+ * Fork pocket 의 깊이 (mm) — pallet 외부 bottom (skid bottom) 부터 deck bottom
47
+ * 까지의 수직 거리. fork blade 가 이 *pocket 안* 으로 수평 진입. 표준 EUR
48
+ * pallet (144mm depth) 의 pocket ≈ 50~60mm.
49
+ *
50
+ * 미명시 시 default = depth 의 40% (= ~60mm for 150mm pallet).
51
+ */
52
+ pocketDepth?: number
53
+
44
54
  // ── 3D 재질 ──
45
55
  material3d?: Material3D
46
56
  }
@@ -66,6 +76,12 @@ const NATURE: ComponentNature = {
66
76
  { display: 'Plastic', value: 'plastic' }
67
77
  ]
68
78
  }
79
+ },
80
+ {
81
+ type: 'number',
82
+ label: 'pocket-depth',
83
+ name: 'pocketDepth',
84
+ placeholder: 'mm — fork 진입 pocket 깊이 (skid bottom → deck bottom). default depth × 40%.'
69
85
  }
70
86
  ],
71
87
  help: 'scene/component/pallet'
@@ -121,6 +137,24 @@ export default class Pallet extends Carriable(Legendable(Placeable(ContainerAbst
121
137
  return NATURE
122
138
  }
123
139
 
140
+ /**
141
+ * Fork pocket 의 깊이 — fork blade 가 진입하는 *pallet 외부 bottom 부터 deck
142
+ * bottom* 까지의 수직 거리. crane.attachPointFor 가 이 값을 차감해서
143
+ * carrier 외부 bottom (skid) 이 fork blade bottom *아래로 pocketDepth 만큼*
144
+ * 깊이 정렬 (= fork 가 pallet 의 *deck 와 skid 사이* 안 으로 들어간 자세).
145
+ */
146
+ get pocketDepth(): number {
147
+ const explicit = (this.state as any).pocketDepth
148
+ if (typeof explicit === 'number' && Number.isFinite(explicit) && explicit >= 0) {
149
+ return explicit
150
+ }
151
+ const d = this.state.depth
152
+ const depth = typeof d === 'number' && Number.isFinite(d) && d > 0
153
+ ? d
154
+ : ((this.constructor as any).defaultDepth ?? 150)
155
+ return depth * 0.4
156
+ }
157
+
124
158
  get anchors() {
125
159
  return []
126
160
  }
@@ -240,7 +274,7 @@ export default class Pallet extends Carriable(Legendable(Placeable(ContainerAbst
240
274
 
241
275
  const longerAxis = Math.max(width, height)
242
276
 
243
- return rectangularFootprintFrames({
277
+ const forkFrames = rectangularFootprintFrames({
244
278
  carrierWorld: me,
245
279
  width,
246
280
  depth: height,
@@ -251,5 +285,20 @@ export default class Pallet extends Carriable(Legendable(Placeable(ContainerAbst
251
285
  tolerance: { positionMm: 50, angleDeg: 5 },
252
286
  priority: 0
253
287
  })
288
+
289
+ // 큰 AGV (deck size 충분) 가 pallet 을 위에 적재하는 패턴. priority 낮음
290
+ // — forklift fork 가 더 자연스러운 default. AGV deck size 가 pallet 보다
291
+ // 작으면 application 측이 carrier 의 dimensions check 필요.
292
+ const deckFrame = topApproachFrame({
293
+ carrierWorld: me,
294
+ topY: palletDepth,
295
+ approachDistance: longerAxis * 0.5 + 100,
296
+ toolType: 'agv-deck',
297
+ tolerance: { positionMm: 30, angleDeg: 4 },
298
+ priority: 1,
299
+ id: 'top-deck'
300
+ })
301
+
302
+ return [...forkFrames, deckFrame]
254
303
  }
255
304
  }
package/src/parcel-3d.ts CHANGED
@@ -21,6 +21,26 @@ const CARDBOARD_COLOR = 0xc8a878
21
21
  const TAPE_COLOR = 0xddc899
22
22
  const LABEL_COLOR = 0xeeeeee
23
23
 
24
+ // ── Module-level shared materials ───────────────────────────────────────────
25
+ // Parcel 인스턴스가 수백~수천 가능. instance 별 new MeshStandardMaterial 시
26
+ // material 개수 폭증 → GPU memory + draw call 비효율. static color 라 단일
27
+ // instance 공유. 색 변경 시 모든 Parcel 에 자동 반영 (의도).
28
+ const PARCEL_BODY_MATERIAL = new THREE.MeshStandardMaterial({
29
+ color: CARDBOARD_COLOR,
30
+ metalness: 0,
31
+ roughness: 0.9
32
+ })
33
+ const PARCEL_TAPE_MATERIAL = new THREE.MeshStandardMaterial({
34
+ color: TAPE_COLOR,
35
+ metalness: 0.05,
36
+ roughness: 0.5
37
+ })
38
+ const PARCEL_LABEL_MATERIAL = new THREE.MeshStandardMaterial({
39
+ color: LABEL_COLOR,
40
+ metalness: 0,
41
+ roughness: 0.4
42
+ })
43
+
24
44
  export class Parcel3D extends RealObjectGroup {
25
45
  build() {
26
46
  super.build()
@@ -30,12 +50,7 @@ export class Parcel3D extends RealObjectGroup {
30
50
 
31
51
  // ── Main body ────────────────────────────────────────────────────
32
52
  const bodyGeo = new THREE.BoxGeometry(width, depth, height)
33
- const bodyMaterial = new THREE.MeshStandardMaterial({
34
- color: CARDBOARD_COLOR,
35
- metalness: 0,
36
- roughness: 0.9
37
- })
38
- const bodyMesh = new THREE.Mesh(bodyGeo, bodyMaterial)
53
+ const bodyMesh = new THREE.Mesh(bodyGeo, PARCEL_BODY_MATERIAL)
39
54
  bodyMesh.position.set(0, 0, 0)
40
55
  bodyMesh.castShadow = true
41
56
  bodyMesh.receiveShadow = true
@@ -48,12 +63,7 @@ export class Parcel3D extends RealObjectGroup {
48
63
  const tapeGeo = tapeAlongLong
49
64
  ? new THREE.BoxGeometry(width * 1.005, tapeT, tapeW)
50
65
  : new THREE.BoxGeometry(tapeW, tapeT, height * 1.005)
51
- const tapeMaterial = new THREE.MeshStandardMaterial({
52
- color: TAPE_COLOR,
53
- metalness: 0.05,
54
- roughness: 0.5
55
- })
56
- const tapeMesh = new THREE.Mesh(tapeGeo, tapeMaterial)
66
+ const tapeMesh = new THREE.Mesh(tapeGeo, PARCEL_TAPE_MATERIAL)
57
67
  tapeMesh.position.set(0, baseY + depth + tapeT / 2 - 0.01, 0)
58
68
  this.object3d.add(tapeMesh)
59
69
 
@@ -61,12 +71,7 @@ export class Parcel3D extends RealObjectGroup {
61
71
  const labelW = Math.min(width, height) * 0.35
62
72
  const labelH = labelW * 0.6
63
73
  const labelGeo = new THREE.BoxGeometry(labelW, depth * 0.005, labelH)
64
- const labelMaterial = new THREE.MeshStandardMaterial({
65
- color: LABEL_COLOR,
66
- metalness: 0,
67
- roughness: 0.4
68
- })
69
- const labelMesh = new THREE.Mesh(labelGeo, labelMaterial)
74
+ const labelMesh = new THREE.Mesh(labelGeo, PARCEL_LABEL_MATERIAL)
70
75
  // Position on top, off-center by ~25% of long axis
71
76
  if (tapeAlongLong) {
72
77
  labelMesh.position.set(width * 0.2, baseY + depth + depth * 0.0025, -height * 0.15)
package/src/parcel.ts CHANGED
@@ -93,9 +93,10 @@ export default class Parcel extends Carriable(Placeable(RectPath(Shape))) {
93
93
  }
94
94
 
95
95
  /**
96
- * Phase H — pickup contract. Parcel 위에서 vacuum gripper / suction cup 으로
97
- * 집기 Box 동일한 패턴이지만 cardboard 표면이라 더 큰 흡착 필요.
98
- * tolerance 약간 완화 (cardboard 변형 가능성).
96
+ * Phase H — pickup contract. Parcel pickup 방식:
97
+ * - gripper (vacuum / suction): 위에서 흡착 RobotArm
98
+ * - agv-deck: AGV/Forklift deck 위에 위에서 얹기 — 같은 top approach 지만
99
+ * deck 자체가 운반체라 tolerance 더 완화
99
100
  */
100
101
  pickupFrames(): PickupFrame[] {
101
102
  const wp = getWorldPose(this)
@@ -109,11 +110,29 @@ export default class Parcel extends Carriable(Placeable(RectPath(Shape))) {
109
110
  topApproachFrame({
110
111
  carrierWorld: me,
111
112
  topY: parcelDepth,
112
- approachDistance: 80, // gripper hover 거리 (Box 보다 더 — vacuum 펼침)
113
+ approachDistance: 80,
113
114
  toolType: 'gripper',
114
- tolerance: { positionMm: 10, angleDeg: 2 }, // cardboard 변형 감안
115
+ tolerance: { positionMm: 10, angleDeg: 2 },
115
116
  priority: 0,
116
117
  id: 'top-suction'
118
+ }),
119
+ topApproachFrame({
120
+ carrierWorld: me,
121
+ topY: parcelDepth,
122
+ approachDistance: 60,
123
+ toolType: 'agv-deck',
124
+ tolerance: { positionMm: 20, angleDeg: 5 },
125
+ priority: 1,
126
+ id: 'top-deck'
127
+ }),
128
+ topApproachFrame({
129
+ carrierWorld: me,
130
+ topY: parcelDepth,
131
+ approachDistance: 100, // crane fork 가 cell 진입 hover
132
+ toolType: 'forklift-fork',
133
+ tolerance: { positionMm: 30, angleDeg: 5 }, // fork 적재 tolerance
134
+ priority: 2, // gripper/deck 다음
135
+ id: 'top-fork'
117
136
  })
118
137
  ]
119
138
  }