@operato/scene-storage 10.0.0-beta.32 → 10.0.0-beta.34

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 (89) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/crane-3d.d.ts +14 -0
  3. package/dist/crane-3d.js +238 -0
  4. package/dist/crane-3d.js.map +1 -0
  5. package/dist/crane.d.ts +157 -0
  6. package/dist/crane.js +440 -0
  7. package/dist/crane.js.map +1 -0
  8. package/dist/index.d.ts +13 -6
  9. package/dist/index.js +9 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/mobile-storage-rack.d.ts +17 -0
  12. package/dist/mobile-storage-rack.js +55 -0
  13. package/dist/mobile-storage-rack.js.map +1 -0
  14. package/dist/rack-column.d.ts +35 -0
  15. package/dist/rack-column.js +258 -0
  16. package/dist/rack-column.js.map +1 -0
  17. package/dist/rack-grid-3d.d.ts +13 -0
  18. package/dist/rack-grid-3d.js +94 -0
  19. package/dist/rack-grid-3d.js.map +1 -0
  20. package/dist/rack-grid-cell.d.ts +341 -0
  21. package/dist/rack-grid-cell.js +321 -0
  22. package/dist/rack-grid-cell.js.map +1 -0
  23. package/dist/rack-grid-helpers.d.ts +28 -0
  24. package/dist/rack-grid-helpers.js +71 -0
  25. package/dist/rack-grid-helpers.js.map +1 -0
  26. package/dist/rack-grid-location.d.ts +37 -0
  27. package/dist/rack-grid-location.js +227 -0
  28. package/dist/rack-grid-location.js.map +1 -0
  29. package/dist/rack-grid.d.ts +80 -0
  30. package/dist/rack-grid.js +829 -0
  31. package/dist/rack-grid.js.map +1 -0
  32. package/dist/stock.d.ts +78 -0
  33. package/dist/stock.js +333 -0
  34. package/dist/stock.js.map +1 -0
  35. package/dist/{rack-cell-3d.d.ts → storage-cell-3d.d.ts} +1 -1
  36. package/dist/{rack-cell-3d.js → storage-cell-3d.js} +3 -3
  37. package/dist/storage-cell-3d.js.map +1 -0
  38. package/dist/{rack-cell.d.ts → storage-cell.d.ts} +12 -6
  39. package/dist/{rack-cell.js → storage-cell.js} +9 -9
  40. package/dist/storage-cell.js.map +1 -0
  41. package/dist/{asrs-rack-3d.d.ts → storage-rack-3d.d.ts} +1 -1
  42. package/dist/{asrs-rack-3d.js → storage-rack-3d.js} +4 -4
  43. package/dist/storage-rack-3d.js.map +1 -0
  44. package/dist/{asrs-rack.d.ts → storage-rack.d.ts} +22 -16
  45. package/dist/{asrs-rack.js → storage-rack.js} +32 -26
  46. package/dist/storage-rack.js.map +1 -0
  47. package/dist/templates/index.d.ts +60 -0
  48. package/dist/templates/index.js +59 -17
  49. package/dist/templates/index.js.map +1 -1
  50. package/package.json +3 -3
  51. package/src/crane-3d.ts +273 -0
  52. package/src/crane.ts +538 -0
  53. package/src/index.ts +13 -6
  54. package/src/mobile-storage-rack.ts +56 -0
  55. package/src/rack-column.ts +340 -0
  56. package/src/rack-grid-3d.ts +128 -0
  57. package/src/rack-grid-cell.ts +404 -0
  58. package/src/rack-grid-helpers.ts +77 -0
  59. package/src/rack-grid-location.ts +286 -0
  60. package/src/rack-grid.ts +994 -0
  61. package/src/stock.ts +426 -0
  62. package/src/{rack-cell-3d.ts → storage-cell-3d.ts} +2 -2
  63. package/src/{rack-cell.ts → storage-cell.ts} +19 -13
  64. package/src/{asrs-rack-3d.ts → storage-rack-3d.ts} +3 -3
  65. package/src/{asrs-rack.ts → storage-rack.ts} +31 -25
  66. package/src/templates/index.ts +59 -17
  67. package/test/test-rack-grid-crane.ts +212 -0
  68. package/test/test-rack-grid.ts +77 -0
  69. package/test/{test-asrs-crane.ts → test-storage-rack-crane.ts} +8 -8
  70. package/translations/en.json +55 -7
  71. package/translations/ja.json +52 -4
  72. package/translations/ko.json +52 -4
  73. package/translations/ms.json +55 -7
  74. package/translations/zh.json +52 -4
  75. package/tsconfig.tsbuildinfo +1 -1
  76. package/dist/asrs-crane-3d.d.ts +0 -17
  77. package/dist/asrs-crane-3d.js +0 -181
  78. package/dist/asrs-crane-3d.js.map +0 -1
  79. package/dist/asrs-crane.d.ts +0 -98
  80. package/dist/asrs-crane.js +0 -216
  81. package/dist/asrs-crane.js.map +0 -1
  82. package/dist/asrs-rack-3d.js.map +0 -1
  83. package/dist/asrs-rack.js.map +0 -1
  84. package/dist/rack-cell-3d.js.map +0 -1
  85. package/dist/rack-cell.js.map +0 -1
  86. package/src/asrs-crane-3d.ts +0 -211
  87. package/src/asrs-crane.ts +0 -275
  88. /package/icons/{asrs-crane.png → crane.png} +0 -0
  89. /package/icons/{asrs-rack.png → storage-rack.png} +0 -0
@@ -0,0 +1,340 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import * as THREE from 'three'
6
+ import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
7
+
8
+ import { RealObject } from '@hatiolab/things-scene'
9
+ import { Stock } from './stock.js'
10
+ import RackGrid from './rack-grid.js'
11
+
12
+
13
+ const _color = new THREE.Color()
14
+ const _matrix = new THREE.Matrix4()
15
+ const _position = new THREE.Vector3()
16
+ const _quaternion = new THREE.Quaternion()
17
+ const _scale = new THREE.Vector3()
18
+
19
+ export class Rack extends RealObject {
20
+ private _frame?: THREE.BufferGeometry
21
+ private _board?: THREE.BufferGeometry
22
+ private _instancedMesh?: THREE.InstancedMesh
23
+ private _stocks: Stock[] = []
24
+
25
+ static rackFrameGeometry = new THREE.BoxGeometry(1, 1, 1)
26
+ static boardGeometry = new THREE.PlaneGeometry(1, 1, 1, 1)
27
+
28
+ private static _boardMaterial?: THREE.MeshStandardMaterial
29
+ private static _stockMaterial?: THREE.MeshStandardMaterial
30
+
31
+ get visualizer(): any | undefined {
32
+ var component = this.component
33
+
34
+ while (component) {
35
+ if (component.state.type == 'visualizer') {
36
+ return component as any
37
+ }
38
+ component = component.parent
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Stock 등록 대상: 1순위 조상 Visualizer(하위호환), 2순위 서비스 레지스트리의 stock-hub
44
+ */
45
+ get stockRegistry(): { putObject(id: string, obj: RealObject): void } | undefined {
46
+ const vis = this.visualizer
47
+ if (vis) return vis
48
+ return (this.component.root as any)?.getService?.('stock')
49
+ }
50
+
51
+ get rackTable(): RackGrid | undefined {
52
+ var component = this.component
53
+
54
+ while (component) {
55
+ if (component.state.type == 'rack-grid') {
56
+ return component as unknown as RackGrid
57
+ }
58
+ component = component.parent
59
+ }
60
+ }
61
+
62
+ get cz() {
63
+ var { shelves = 1, depth = 1 } = this.rackTable!.state
64
+
65
+ return 0.5 * depth * shelves
66
+ }
67
+
68
+ static get boardMaterial() {
69
+ if (!Rack._boardMaterial) {
70
+ Rack._boardMaterial = new THREE.MeshStandardMaterial({
71
+ color: '#dedede',
72
+ side: THREE.DoubleSide,
73
+ polygonOffset: true,
74
+ polygonOffsetFactor: -0.1
75
+ })
76
+ }
77
+
78
+ return Rack._boardMaterial
79
+ }
80
+
81
+ static get stockMaterial() {
82
+ if (!Rack._stockMaterial) {
83
+ Rack._stockMaterial = new THREE.MeshStandardMaterial({
84
+ color: 0xffffff,
85
+ side: THREE.FrontSide,
86
+ metalness: 0,
87
+ roughness: 1,
88
+ envMapIntensity: 0
89
+ })
90
+ }
91
+ return Rack._stockMaterial
92
+ }
93
+
94
+ get frame() {
95
+ return this._frame
96
+ }
97
+
98
+ get board() {
99
+ return this._board
100
+ }
101
+
102
+ get instancedMesh() {
103
+ return this._instancedMesh
104
+ }
105
+
106
+ get stocks() {
107
+ return this._stocks
108
+ }
109
+
110
+ build() {
111
+ super.build()
112
+
113
+ var { depth, hideRackFrame, shelves = 1, shelfLocations: commonShelfLocation, stockScale = 0.7 } = this.rackTable!.state
114
+
115
+ var { width, height, shelfLocations = commonShelfLocation, binLocations = '' } = this.component.state
116
+
117
+ let scale = stockScale
118
+
119
+ var shelfLocIds
120
+
121
+ if (!shelfLocations) {
122
+ shelfLocIds = []
123
+ for (var i = 0; i < shelves; i++) shelfLocIds.push(i + 1)
124
+ } else shelfLocIds = shelfLocations.split(/\s*,\s*/)
125
+
126
+ var shelfBins = binLocations.trim().split('\n').reverse()
127
+
128
+ if (!hideRackFrame) {
129
+ this._frame = this.createRackFrame(width, height, depth * shelves)
130
+ this._board = this.createRackBoards(shelves, width, height, depth, shelfLocIds)
131
+ }
132
+
133
+ // Stock 인스턴스 정보 수집
134
+ const stockEntries: { stock: Stock; x: number; y: number; z: number; w: number; h: number; d: number; name: string }[] = []
135
+
136
+ for (var i = 0; i < shelves; i++) {
137
+ let bottom = -depth * shelves * 0.5
138
+ if (shelfLocIds[i] == '') {
139
+ continue
140
+ }
141
+
142
+ var bins = (shelfBins[i] || '').trim().split(/\s*,\s*/)
143
+
144
+ var binWidth = width / (bins.length || 1)
145
+ for (var b = 0; b < bins.length; b++) {
146
+ let sw = binWidth * scale
147
+ let sh = height * scale
148
+ let sd = depth * scale
149
+
150
+ let stock = new Stock(this.component, { width: sw, height: sh, depth: sd })
151
+
152
+ let x = (width / 2) * ((2 * b - (bins.length - 1)) / bins.length)
153
+ let y = bottom + depth * i + sd * 0.5
154
+ let z = 0
155
+
156
+ var binCode = (bins[b] || '').replace('.', '')
157
+ let name = `${this.makeLocationString(shelfLocIds[i])}${binCode}`
158
+
159
+ stockEntries.push({ stock, x, y, z, w: sw, h: sh, d: sd, name })
160
+ }
161
+ }
162
+
163
+ // InstancedMesh 생성 (전체 Stock을 1개 draw call로)
164
+ if (stockEntries.length > 0) {
165
+ const instMesh = new THREE.InstancedMesh(
166
+ Stock.stockGeometry,
167
+ Rack.stockMaterial,
168
+ stockEntries.length
169
+ )
170
+
171
+ // instanceColor 초기화
172
+ const defaultColor = new THREE.Color(Stock.defaultMaterial.color)
173
+ for (let idx = 0; idx < stockEntries.length; idx++) {
174
+ const { stock, x, y, z, w, h, d, name } = stockEntries[idx]
175
+
176
+ _matrix.compose(
177
+ _position.set(x, y, z),
178
+ _quaternion.identity(),
179
+ _scale.set(w, d, h)
180
+ )
181
+ instMesh.setMatrixAt(idx, _matrix)
182
+ instMesh.setColorAt(idx, defaultColor)
183
+
184
+ // Stock에 instanced 참조 설정
185
+ stock._instancedMesh = instMesh
186
+ stock._instanceIndex = idx
187
+ stock._basePosition = { x, y, z }
188
+ stock._baseScale = { x: w, y: d, z: h }
189
+
190
+ // name 기반 lookup 유지
191
+ this._stocks.push(stock)
192
+ this.stockRegistry?.putObject(name, stock)
193
+ }
194
+
195
+ instMesh.instanceMatrix.needsUpdate = true
196
+ instMesh.instanceColor!.needsUpdate = true
197
+ instMesh.receiveShadow = true
198
+
199
+ // focused stock 회전 애니메이션
200
+ const self = this
201
+ instMesh.onBeforeRender = () => {
202
+ let needsUpdate = false
203
+ let hasFocused = false
204
+ for (const stock of self._stocks) {
205
+ if (stock._focused && stock._focusedAt != null) {
206
+ const elapsed = performance.now() - stock._focusedAt
207
+ const angle = 2 * Math.PI * (elapsed / 2000)
208
+ _quaternion.setFromAxisAngle(_position.set(0, 1, 0), angle)
209
+ _matrix.compose(
210
+ _position.set(stock._basePosition!.x, stock._basePosition!.y, stock._basePosition!.z),
211
+ _quaternion,
212
+ _scale.set(stock._baseScale!.x, stock._baseScale!.y, stock._baseScale!.z)
213
+ )
214
+ instMesh.setMatrixAt(stock._instanceIndex, _matrix)
215
+ needsUpdate = true
216
+ hasFocused = true
217
+ } else if (stock._focusedAt != null) {
218
+ delete stock._focusedAt
219
+ _matrix.compose(
220
+ _position.set(stock._basePosition!.x, stock._basePosition!.y, stock._basePosition!.z),
221
+ _quaternion.identity(),
222
+ _scale.set(stock._baseScale!.x, stock._baseScale!.y, stock._baseScale!.z)
223
+ )
224
+ instMesh.setMatrixAt(stock._instanceIndex, _matrix)
225
+ needsUpdate = true
226
+ }
227
+ }
228
+ if (needsUpdate) {
229
+ instMesh.instanceMatrix.needsUpdate = true
230
+ }
231
+ if (hasFocused) {
232
+ self.component.invalidate()
233
+ }
234
+ }
235
+
236
+ this.object3d.add(instMesh)
237
+ this._instancedMesh = instMesh
238
+ }
239
+ }
240
+
241
+ createRackFrame(w: number, h: number, d: number): THREE.BufferGeometry {
242
+ const frameWeight = Math.round(Math.min(w, h) / 10)
243
+ const geometries = []
244
+
245
+ for (let i = 0; i < 4; i++) {
246
+ const geometry = Rack.rackFrameGeometry.clone()
247
+ geometry.scale(frameWeight, d, frameWeight)
248
+
249
+ switch (i) {
250
+ case 0:
251
+ geometry.translate(w / 2, 0, h / 2)
252
+ break
253
+ case 1:
254
+ geometry.translate(w / 2, 0, -h / 2)
255
+ break
256
+ case 2:
257
+ geometry.translate(-w / 2, 0, h / 2)
258
+ break
259
+ case 3:
260
+ geometry.translate(-w / 2, 0, -h / 2)
261
+ break
262
+ }
263
+
264
+ geometries.push(geometry)
265
+ }
266
+
267
+ return BufferGeometryUtils.mergeGeometries(geometries)
268
+ }
269
+
270
+ createRackBoards(
271
+ shelves: number,
272
+ width: number,
273
+ height: number,
274
+ depth: number,
275
+ shelfLocIds: string[]
276
+ ): THREE.BufferGeometry {
277
+ let bottom = -depth * shelves * 0.5
278
+ const geometries = []
279
+
280
+ for (let i = 1; i < shelves; i++) {
281
+ if (shelfLocIds[i] === '') {
282
+ continue
283
+ }
284
+
285
+ const geometry = Rack.boardGeometry.clone()
286
+
287
+ geometry.scale(width, height, 1)
288
+ geometry.rotateX(Math.PI / 2)
289
+ geometry.translate(0, bottom + depth * i, 0)
290
+
291
+ geometries.push(geometry)
292
+ }
293
+
294
+ return BufferGeometryUtils.mergeGeometries(geometries)
295
+ }
296
+
297
+ makeLocationString(shelfString: string) {
298
+ var { section = '', unit = '' } = this.component.state
299
+ var { locPattern = '{z}{s}-{u}-{sh}', zone = '' } = this.rackTable!.state
300
+
301
+ var locationString = locPattern
302
+
303
+ locationString = locationString.replace(/{z}/i, zone)
304
+ locationString = locationString.replace(/{s}/i, section)
305
+ locationString = locationString.replace(/{u}/i, unit)
306
+ locationString = locationString.replace(/{sh}/i, shelfString)
307
+
308
+ return locationString
309
+ }
310
+
311
+ makeShelfString(pattern: string, shelf: number, length: number) {
312
+ /**
313
+ * pattern #: 숫자
314
+ * pattern 0: 고정 자리수
315
+ * pattern -: 역순
316
+ */
317
+
318
+ if (!pattern || !shelf || !length) return
319
+
320
+ var isReverse = /^\-/.test(pattern)
321
+ pattern = pattern.replace(/#+/, '#')
322
+
323
+ var fixedLength = (pattern.match(/0/g) || []).length || 0
324
+ var shelfString = String(isReverse ? length - shelf + 1 : shelf)
325
+
326
+ if (shelfString.length > fixedLength && fixedLength > 0) {
327
+ shelfString = shelfString.substring(shelfString.length - fixedLength)
328
+ } else {
329
+ var prefix = ''
330
+ for (var i = 0; i < fixedLength - shelfString.length; i++) {
331
+ prefix += '0'
332
+ }
333
+ shelfString = prefix + shelfString
334
+ }
335
+
336
+ return shelfString
337
+ }
338
+
339
+ updateAlpha() {}
340
+ }
@@ -0,0 +1,128 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import * as THREE from 'three'
6
+ import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
7
+ import { Component, RealObjectGroup } from '@hatiolab/things-scene'
8
+
9
+ import { Rack } from './rack-column.js'
10
+ import type RackGrid from './rack-grid.js'
11
+
12
+ const DEFAULT_FRAME_COLOR = 0x8a8a8a
13
+
14
+ export class RackGrid3D extends RealObjectGroup {
15
+ private _frameMaterial?: THREE.MeshStandardMaterial
16
+
17
+ build() {
18
+ super.build()
19
+
20
+ this.createRacks()
21
+ }
22
+
23
+ // bottom origin: object3d가 zPos(바닥)에 위치 (내부 mesh는 center-local 좌표로 쌓임)
24
+ protected get syncZPosOffset(): number {
25
+ return 0
26
+ }
27
+
28
+ override get geometricOffsetY(): number {
29
+ return 0
30
+ }
31
+
32
+ private createFrameMaterial(): THREE.MeshStandardMaterial {
33
+ this._frameMaterial?.dispose()
34
+
35
+ const { strokeStyle } = this.component.state
36
+ const color = strokeStyle && typeof strokeStyle === 'string' ? strokeStyle : DEFAULT_FRAME_COLOR
37
+
38
+ this._frameMaterial = new THREE.MeshStandardMaterial({
39
+ color,
40
+ roughness: 0.35,
41
+ metalness: 0.85
42
+ })
43
+
44
+ return this._frameMaterial
45
+ }
46
+
47
+ createRacks() {
48
+ const { rotation = 0, shelfLocations, shelves = 1 } = this.component.state
49
+
50
+ this.object3d.rotation.y = -rotation
51
+
52
+ const racks = (this.component as unknown as RackGrid).components
53
+ .map((cell: Component) => {
54
+ const { shelfLocations: shelfLoc = shelfLocations, isEmpty } = cell.state
55
+
56
+ if (!isEmpty) {
57
+ cell.setState('shelfLocations', shelfLoc)
58
+
59
+ const rack = new Rack(cell)
60
+ cell._realObject = rack // 중복 생성 방지: addObject 재귀에서 skip
61
+
62
+ rack.update()
63
+ this.object3d.add(rack.object3d)
64
+
65
+ return rack
66
+ }
67
+ return
68
+ })
69
+ .filter((rack: any): rack is Rack => !!rack)
70
+
71
+ this.mergeAndAddRackCommonObjects(racks)
72
+ }
73
+
74
+ mergeAndAddRackCommonObjects(racks: Rack[]) {
75
+ const framesGeometries: THREE.BufferGeometry[] = []
76
+ const boardsGeometries: THREE.BufferGeometry[] = []
77
+
78
+ if (racks.length > 0) {
79
+ racks.forEach(rack => {
80
+ const geometry = rack.frame
81
+
82
+ if (!geometry) {
83
+ return
84
+ }
85
+
86
+ geometry.translate(rack.position.x, rack.position.y, rack.position.z)
87
+ geometry.scale(rack.scale.x, rack.scale.y, rack.scale.z)
88
+ framesGeometries.push(geometry)
89
+ })
90
+
91
+ if (framesGeometries.length > 0) {
92
+ const frameMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(framesGeometries), this.createFrameMaterial())
93
+ this.object3d.add(frameMesh)
94
+ }
95
+
96
+ racks.forEach(rack => {
97
+ const geometry = rack.board
98
+
99
+ if (!geometry) {
100
+ return
101
+ }
102
+
103
+ geometry.translate(rack.position.x, rack.position.y, rack.position.z)
104
+ geometry.scale(rack.scale.x, rack.scale.y, rack.scale.z)
105
+ boardsGeometries.push(geometry)
106
+ })
107
+
108
+ if (boardsGeometries.length > 0) {
109
+ const material = Rack.boardMaterial
110
+ material.opacity = 0.5
111
+ material.transparent = true
112
+
113
+ const boardMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(boardsGeometries), material)
114
+
115
+ this.object3d.add(boardMesh)
116
+ }
117
+ }
118
+ }
119
+
120
+ dispose() {
121
+ this._frameMaterial?.dispose()
122
+ this._frameMaterial = undefined
123
+
124
+ super.dispose()
125
+ }
126
+
127
+ updateAlpha() {}
128
+ }