@fmsim/machine 2.0.0-beta.6 → 2.0.0-beta.61
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/dist/agv.js +2 -2
- package/dist/agv.js.map +1 -1
- package/dist/carrier-manager.js +93 -0
- package/dist/carrier-manager.js.map +1 -0
- package/dist/carrier.js +49 -4
- package/dist/carrier.js.map +1 -1
- package/dist/conveyor-join.js +8 -2
- package/dist/conveyor-join.js.map +1 -1
- package/dist/conveyor.js +8 -0
- package/dist/conveyor.js.map +1 -1
- package/dist/crane.js.map +1 -1
- package/dist/factories/agv-3d.js +8 -9
- package/dist/factories/agv-3d.js.map +1 -1
- package/dist/factories/conveyor-3d.js +166 -81
- package/dist/factories/conveyor-3d.js.map +1 -1
- package/dist/factories/crane-3d.js +5 -7
- package/dist/factories/crane-3d.js.map +1 -1
- package/dist/factories/crane-rail-3d.js +4 -6
- package/dist/factories/crane-rail-3d.js.map +1 -1
- package/dist/factories/machine-3d.js +363 -132
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/factories/oht-3d.js +20 -20
- package/dist/factories/oht-3d.js.map +1 -1
- package/dist/factories/shuttle-3d.js +2 -5
- package/dist/factories/shuttle-3d.js.map +1 -1
- package/dist/features/mcs-status-mixin.js +22 -0
- package/dist/features/mcs-status-mixin.js.map +1 -1
- package/dist/index.js +50 -34
- package/dist/index.js.map +1 -1
- package/dist/mcs-carrier-holder.js +39 -22
- package/dist/mcs-carrier-holder.js.map +1 -1
- package/dist/mcs-vehicle.js +8 -3
- package/dist/mcs-vehicle.js.map +1 -1
- package/dist/node.js +1 -1
- package/dist/node.js.map +1 -1
- package/dist/oht.js.map +1 -1
- package/dist/scanner.js +30 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scene/root-container-override.js +9 -0
- package/dist/scene/root-container-override.js.map +1 -1
- package/dist/stocker-abnormal-bar.js +1 -1
- package/dist/stocker-abnormal-bar.js.map +1 -1
- package/dist/stocker-capacity-bar.js +3 -3
- package/dist/stocker-capacity-bar.js.map +1 -1
- package/dist/stocker-carrier-bar.js +1 -1
- package/dist/stocker-carrier-bar.js.map +1 -1
- package/dist/templates/index.js +2 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/scanner.js +20 -0
- package/dist/templates/scanner.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/get-value-on-ranges.js +2 -4
- package/dist/utils/get-value-on-ranges.js.map +1 -1
- package/dist/zone-capacity-bar.js +3 -3
- package/dist/zone-capacity-bar.js.map +1 -1
- package/jest.config.js +10 -0
- package/package.json +4 -3
- package/src/agv.ts +2 -2
- package/src/carrier-manager.ts +117 -0
- package/src/carrier.ts +48 -2
- package/src/conveyor-join.ts +5 -2
- package/src/conveyor.ts +8 -0
- package/src/crane.ts +1 -1
- package/src/factories/agv-3d.ts +9 -10
- package/src/factories/conveyor-3d.ts +168 -84
- package/src/factories/crane-3d.ts +6 -8
- package/src/factories/crane-rail-3d.ts +5 -7
- package/src/factories/machine-3d.ts +428 -144
- package/src/factories/oht-3d.ts +20 -20
- package/src/factories/shuttle-3d.ts +2 -6
- package/src/features/mcs-status-mixin.ts +21 -0
- package/src/index.ts +56 -34
- package/src/mcs-carrier-holder.ts +38 -34
- package/src/mcs-vehicle.ts +9 -6
- package/src/node.ts +1 -1
- package/src/oht.ts +1 -1
- package/src/scanner.ts +29 -0
- package/src/scene/root-container-override.ts +12 -0
- package/src/stocker-abnormal-bar.ts +1 -1
- package/src/stocker-capacity-bar.ts +3 -3
- package/src/stocker-carrier-bar.ts +1 -1
- package/src/templates/index.ts +2 -0
- package/src/templates/scanner.ts +20 -0
- package/src/utils/get-value-on-ranges.ts +2 -4
- package/src/zone-capacity-bar.ts +3 -3
- package/translations/en.json +2 -1
- package/translations/ja.json +2 -1
- package/translations/ko.json +2 -1
- package/translations/ms.json +2 -1
- package/translations/zh.json +2 -1
|
@@ -27,12 +27,6 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
27
27
|
return depth || TRANSPORT_SURFACE_HEIGHT
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// group origin = 바닥 (y=0). MCSRealObject3D와 동일 컨벤션.
|
|
31
|
-
// 기하는 build()에서 y=0(바닥)~y=depth(상단) 범위로 배치.
|
|
32
|
-
protected get syncZPosOffset(): number {
|
|
33
|
-
return 0
|
|
34
|
-
}
|
|
35
|
-
|
|
36
30
|
private resolveColor(): number {
|
|
37
31
|
const comp = this.component as any
|
|
38
32
|
|
|
@@ -40,14 +34,14 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
40
34
|
// '#F0F0F0'은 상태 미설정 시 기본 fallback — 무시하고 defaultColor 사용
|
|
41
35
|
try {
|
|
42
36
|
const sc = comp.statusColor
|
|
43
|
-
if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()
|
|
37
|
+
if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0' && sc !== 'transparent') return new THREE.Color(sc).getHex()
|
|
44
38
|
} catch {
|
|
45
39
|
/* statusColor 미구현 시 무시 */
|
|
46
40
|
}
|
|
47
41
|
|
|
48
42
|
// 2) state.fillStyle fallback
|
|
49
43
|
const { fillStyle } = this.component.state
|
|
50
|
-
if (fillStyle && typeof fillStyle === 'string') {
|
|
44
|
+
if (fillStyle && typeof fillStyle === 'string' && fillStyle !== 'transparent') {
|
|
51
45
|
try {
|
|
52
46
|
return new THREE.Color(fillStyle).getHex()
|
|
53
47
|
} catch {
|
|
@@ -61,16 +55,31 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
61
55
|
build() {
|
|
62
56
|
super.build()
|
|
63
57
|
|
|
64
|
-
const { width, height, conveyorType } = this.component.state
|
|
58
|
+
const { width: rawWidth, height: rawHeight, conveyorType, orientation } = this.component.state
|
|
65
59
|
const depth = this.effectiveDepth
|
|
66
|
-
if (!
|
|
60
|
+
if (!rawWidth || !rawHeight) return
|
|
61
|
+
|
|
62
|
+
// orientation: 'horizontal' = 롤러가 Z축(세로), 'vertical' = 롤러가 X축(가로)
|
|
63
|
+
// default = 긴 방향에 수직 (긴 축을 따라 이송)
|
|
64
|
+
const isVertical = orientation === 'vertical' ? true
|
|
65
|
+
: orientation === 'horizontal' ? false
|
|
66
|
+
: rawWidth < rawHeight // default: 세로가 길면 롤러는 가로(X축)
|
|
67
|
+
// isVertical=true → 롤러 축이 X(가로), 이송 방향이 Z(세로)
|
|
68
|
+
// isVertical=false → 롤러 축이 Z(세로), 이송 방향이 X(가로)
|
|
69
|
+
const width = rawWidth
|
|
70
|
+
const height = rawHeight
|
|
67
71
|
|
|
68
72
|
// 2D와 동일 로직으로 rollWidth 산출
|
|
69
73
|
const rollWidth = Math.max(10, Math.min(width, height) / 2)
|
|
70
74
|
const rollerDiameter = Math.max(rollWidth, 2)
|
|
71
75
|
const frameH = Math.min(rollerDiameter, depth)
|
|
72
76
|
const legH = Math.max(depth - frameH, 0)
|
|
73
|
-
|
|
77
|
+
|
|
78
|
+
// isVertical일 때 이송 방향이 Z축: 레일은 Z축을 따라, 벨트폭은 width
|
|
79
|
+
// !isVertical일 때 이송 방향이 X축: 레일은 X축을 따라, 벨트폭은 height
|
|
80
|
+
const beltWidth = isVertical ? width : height
|
|
81
|
+
const railLen = isVertical ? height : width
|
|
82
|
+
const railW = Math.max(beltWidth * 0.06, 2)
|
|
74
83
|
const legThickness = Math.max(railW * 0.8, 2)
|
|
75
84
|
|
|
76
85
|
const frameMaterial = new THREE.MeshStandardMaterial({
|
|
@@ -84,11 +93,21 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
84
93
|
const frameGeometries: THREE.BufferGeometry[] = []
|
|
85
94
|
const railY = depth - frameH / 2
|
|
86
95
|
|
|
87
|
-
// Side rails
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
// Side rails — 이송 방향을 따라, 벨트 양측에 배치
|
|
97
|
+
if (isVertical) {
|
|
98
|
+
// 레일이 Z축(세로)을 따라, ±X에 배치
|
|
99
|
+
for (const xSign of [-1, 1]) {
|
|
100
|
+
const rail = new THREE.BoxGeometry(railW, frameH, height)
|
|
101
|
+
rail.translate(xSign * (width / 2 - railW / 2), railY, 0)
|
|
102
|
+
frameGeometries.push(rail)
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// 레일이 X축(가로)을 따라, ±Z에 배치
|
|
106
|
+
for (const zSign of [-1, 1]) {
|
|
107
|
+
const rail = new THREE.BoxGeometry(width, frameH, railW)
|
|
108
|
+
rail.translate(0, railY, zSign * (height / 2 - railW / 2))
|
|
109
|
+
frameGeometries.push(rail)
|
|
110
|
+
}
|
|
92
111
|
}
|
|
93
112
|
|
|
94
113
|
// 4 legs at corners
|
|
@@ -96,49 +115,66 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
96
115
|
const legTopY = depth - frameH
|
|
97
116
|
const legCenterY = legTopY - legH / 2
|
|
98
117
|
|
|
118
|
+
// 다리 위치: 레일 안쪽에 정렬
|
|
119
|
+
const legXPos = isVertical ? (width / 2 - railW / 2) : (width / 2 - legThickness / 2)
|
|
120
|
+
const legZPos = isVertical ? (height / 2 - legThickness / 2) : (height / 2 - railW / 2)
|
|
121
|
+
|
|
99
122
|
for (const xSign of [-1, 1]) {
|
|
100
123
|
for (const zSign of [-1, 1]) {
|
|
101
|
-
const lx = xSign * (width / 2 - legThickness / 2)
|
|
102
|
-
const lz = zSign * (height / 2 - railW / 2)
|
|
103
124
|
const leg = new THREE.BoxGeometry(legThickness, legH, legThickness)
|
|
104
|
-
leg.translate(
|
|
125
|
+
leg.translate(xSign * legXPos, legCenterY, zSign * legZPos)
|
|
105
126
|
frameGeometries.push(leg)
|
|
106
127
|
}
|
|
107
128
|
}
|
|
108
129
|
|
|
109
|
-
// Cross-bracing
|
|
130
|
+
// Cross-bracing
|
|
110
131
|
const braceH = legThickness * 0.6
|
|
111
132
|
const braceW = legThickness * 0.6
|
|
112
|
-
const braceLen = height - railW
|
|
113
133
|
const braceY = legTopY - legH * 0.35
|
|
114
134
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
135
|
+
// 이송 방향 브레이스 (레일과 평행)
|
|
136
|
+
if (isVertical) {
|
|
137
|
+
for (const xSign of [-1, 1]) {
|
|
138
|
+
const brace = new THREE.BoxGeometry(braceW, braceH, height - legThickness)
|
|
139
|
+
brace.translate(xSign * legXPos, braceY, 0)
|
|
140
|
+
frameGeometries.push(brace)
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
for (const zSign of [-1, 1]) {
|
|
144
|
+
const brace = new THREE.BoxGeometry(width - legThickness, braceH, braceW)
|
|
145
|
+
brace.translate(0, braceY, zSign * legZPos)
|
|
146
|
+
frameGeometries.push(brace)
|
|
147
|
+
}
|
|
119
148
|
}
|
|
120
149
|
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
150
|
+
// 벨트폭 방향 브레이스 (레일과 수직)
|
|
151
|
+
if (isVertical) {
|
|
152
|
+
for (const zSign of [-1, 1]) {
|
|
153
|
+
const brace = new THREE.BoxGeometry(width - railW, braceH, braceW)
|
|
154
|
+
brace.translate(0, braceY, zSign * legZPos)
|
|
155
|
+
frameGeometries.push(brace)
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
for (const xSign of [-1, 1]) {
|
|
159
|
+
const brace = new THREE.BoxGeometry(braceW, braceH, height - railW)
|
|
160
|
+
brace.translate(xSign * legXPos, braceY, 0)
|
|
161
|
+
frameGeometries.push(brace)
|
|
162
|
+
}
|
|
127
163
|
}
|
|
128
164
|
}
|
|
129
165
|
|
|
130
166
|
const frameMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(frameGeometries), frameMaterial)
|
|
131
167
|
frameMesh.castShadow = true
|
|
132
168
|
frameMesh.receiveShadow = true
|
|
133
|
-
this.
|
|
169
|
+
this.components3D.add(frameMesh)
|
|
134
170
|
|
|
135
171
|
// --- Rollers, Belt, or Rails ---
|
|
136
172
|
if (conveyorType === 'rail') {
|
|
137
|
-
this.buildRails(width, height, depth, frameH, railW)
|
|
173
|
+
this.buildRails(width, height, depth, frameH, railW, isVertical)
|
|
138
174
|
} else if (conveyorType === 'belt') {
|
|
139
|
-
this.buildBelt(width, height, depth, frameH, railW)
|
|
175
|
+
this.buildBelt(width, height, depth, frameH, railW, isVertical)
|
|
140
176
|
} else {
|
|
141
|
-
this.buildRollers(width, height, depth, frameH, railW, rollWidth)
|
|
177
|
+
this.buildRollers(width, height, depth, frameH, railW, rollWidth, isVertical)
|
|
142
178
|
}
|
|
143
179
|
}
|
|
144
180
|
|
|
@@ -148,28 +184,45 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
148
184
|
depth: number,
|
|
149
185
|
frameH: number,
|
|
150
186
|
railW: number,
|
|
151
|
-
rollWidth: number
|
|
187
|
+
rollWidth: number,
|
|
188
|
+
isVertical: boolean = false
|
|
152
189
|
) {
|
|
153
190
|
const rollerRadius = Math.max(rollWidth / 8, 1)
|
|
154
|
-
const rollerLength = height - railW * 2 - 0.5
|
|
155
191
|
const diameter = rollerRadius * 2
|
|
156
|
-
// 롤러 간 최소 간격 (z-fighting 방지용 미세 갭만)
|
|
157
192
|
const step = diameter * 1.05
|
|
158
|
-
// 롤러 상단 = 이송 표면 (y=depth)
|
|
159
193
|
const rollerY = depth - rollerRadius
|
|
160
194
|
const color = this.resolveColor()
|
|
161
195
|
|
|
162
196
|
const rollerGeometries: THREE.BufferGeometry[] = []
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
197
|
+
|
|
198
|
+
if (isVertical) {
|
|
199
|
+
// 롤러 축 = X축(가로), 이송 방향 = Z축(세로)
|
|
200
|
+
const rollerLength = width - railW * 2 - 0.5
|
|
201
|
+
const count = Math.max(1, Math.floor(height / step))
|
|
202
|
+
const totalSpan = (count - 1) * step
|
|
203
|
+
const startZ = -totalSpan / 2
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < count; i++) {
|
|
206
|
+
const z = startZ + i * step
|
|
207
|
+
const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16)
|
|
208
|
+
roller.rotateZ(Math.PI / 2)
|
|
209
|
+
roller.translate(0, rollerY, z)
|
|
210
|
+
rollerGeometries.push(roller)
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
// 롤러 축 = Z축(세로), 이송 방향 = X축(가로) — 기존 동작
|
|
214
|
+
const rollerLength = height - railW * 2 - 0.5
|
|
215
|
+
const count = Math.max(1, Math.floor(width / step))
|
|
216
|
+
const totalSpan = (count - 1) * step
|
|
217
|
+
const startX = -totalSpan / 2
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < count; i++) {
|
|
220
|
+
const x = startX + i * step
|
|
221
|
+
const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16)
|
|
222
|
+
roller.rotateX(Math.PI / 2)
|
|
223
|
+
roller.translate(x, rollerY, 0)
|
|
224
|
+
rollerGeometries.push(roller)
|
|
225
|
+
}
|
|
173
226
|
}
|
|
174
227
|
|
|
175
228
|
if (rollerGeometries.length > 0) {
|
|
@@ -178,26 +231,35 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
178
231
|
new THREE.MeshStandardMaterial({ color, metalness: 0.9, roughness: 0.2 })
|
|
179
232
|
)
|
|
180
233
|
rollerMesh.castShadow = true
|
|
181
|
-
this.
|
|
234
|
+
this.components3D.add(rollerMesh)
|
|
182
235
|
}
|
|
183
236
|
}
|
|
184
237
|
|
|
185
|
-
private buildBelt(width: number, height: number, depth: number, frameH: number, railW: number) {
|
|
238
|
+
private buildBelt(width: number, height: number, depth: number, frameH: number, railW: number, isVertical: boolean = false) {
|
|
186
239
|
const drumRadius = frameH * 0.38
|
|
187
|
-
const drumLength = height - railW * 2
|
|
188
240
|
const beltY = depth - drumRadius
|
|
189
241
|
const color = this.resolveColor()
|
|
190
242
|
|
|
243
|
+
// 이송 방향 길이와 벨트폭
|
|
244
|
+
const transportLen = isVertical ? height : width
|
|
245
|
+
const beltWidth = isVertical ? width : height
|
|
246
|
+
const drumLength = beltWidth - railW * 2
|
|
247
|
+
|
|
191
248
|
// Two end drums
|
|
192
249
|
const drumGeometries: THREE.BufferGeometry[] = []
|
|
193
250
|
const drumInset = drumRadius * 1.5
|
|
194
|
-
const
|
|
195
|
-
const
|
|
251
|
+
const drum1 = -transportLen / 2 + drumInset
|
|
252
|
+
const drum2 = transportLen / 2 - drumInset
|
|
196
253
|
|
|
197
|
-
for (const
|
|
254
|
+
for (const dp of [drum1, drum2]) {
|
|
198
255
|
const drum = new THREE.CylinderGeometry(drumRadius, drumRadius, drumLength, 16)
|
|
199
|
-
|
|
200
|
-
|
|
256
|
+
if (isVertical) {
|
|
257
|
+
drum.rotateZ(Math.PI / 2)
|
|
258
|
+
drum.translate(0, beltY, dp)
|
|
259
|
+
} else {
|
|
260
|
+
drum.rotateX(Math.PI / 2)
|
|
261
|
+
drum.translate(dp, beltY, 0)
|
|
262
|
+
}
|
|
201
263
|
drumGeometries.push(drum)
|
|
202
264
|
}
|
|
203
265
|
|
|
@@ -206,7 +268,7 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
206
268
|
new THREE.MeshStandardMaterial({ color: ROLLER_COLOR, metalness: 0.9, roughness: 0.2 })
|
|
207
269
|
)
|
|
208
270
|
drumMesh.castShadow = true
|
|
209
|
-
this.
|
|
271
|
+
this.components3D.add(drumMesh)
|
|
210
272
|
|
|
211
273
|
// Belt surface
|
|
212
274
|
const beltThickness = drumRadius * 0.12
|
|
@@ -217,18 +279,20 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
217
279
|
})
|
|
218
280
|
|
|
219
281
|
// Flat belt top surface spanning between drums
|
|
220
|
-
const flatLen =
|
|
221
|
-
const flatGeo =
|
|
282
|
+
const flatLen = drum2 - drum1
|
|
283
|
+
const flatGeo = isVertical
|
|
284
|
+
? new THREE.BoxGeometry(drumLength, beltThickness, flatLen)
|
|
285
|
+
: new THREE.BoxGeometry(flatLen, beltThickness, drumLength)
|
|
222
286
|
const flatMesh = new THREE.Mesh(flatGeo, beltMaterial)
|
|
223
287
|
flatMesh.position.set(0, beltY + drumRadius, 0)
|
|
224
288
|
flatMesh.castShadow = true
|
|
225
|
-
this.
|
|
289
|
+
this.components3D.add(flatMesh)
|
|
226
290
|
|
|
227
291
|
// Belt wrap around drums (segmented half-cylinder)
|
|
228
292
|
const wrapSegments = 12
|
|
229
|
-
for (const [
|
|
230
|
-
[
|
|
231
|
-
[
|
|
293
|
+
for (const [dp, angleStart] of [
|
|
294
|
+
[drum1, Math.PI / 2],
|
|
295
|
+
[drum2, -Math.PI / 2],
|
|
232
296
|
] as [number, number][]) {
|
|
233
297
|
const wrapGeometries: THREE.BufferGeometry[] = []
|
|
234
298
|
for (let i = 0; i < wrapSegments; i++) {
|
|
@@ -236,36 +300,47 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
236
300
|
const a1 = angleStart + (Math.PI * (i + 1)) / wrapSegments
|
|
237
301
|
const r = drumRadius + beltThickness / 2
|
|
238
302
|
|
|
239
|
-
const
|
|
303
|
+
const p0 = dp + Math.cos(a0) * r
|
|
240
304
|
const y0 = beltY + Math.sin(a0) * r
|
|
241
|
-
const
|
|
305
|
+
const p1 = dp + Math.cos(a1) * r
|
|
242
306
|
const y1 = beltY + Math.sin(a1) * r
|
|
243
307
|
|
|
244
|
-
const segLen = Math.sqrt((
|
|
245
|
-
const segAngle = Math.atan2(y1 - y0,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
308
|
+
const segLen = Math.sqrt((p1 - p0) ** 2 + (y1 - y0) ** 2) * 1.05
|
|
309
|
+
const segAngle = Math.atan2(y1 - y0, p1 - p0)
|
|
310
|
+
|
|
311
|
+
if (isVertical) {
|
|
312
|
+
const seg = new THREE.BoxGeometry(drumLength, beltThickness, segLen)
|
|
313
|
+
seg.rotateX(-segAngle)
|
|
314
|
+
seg.translate(0, (y0 + y1) / 2, (p0 + p1) / 2)
|
|
315
|
+
wrapGeometries.push(seg)
|
|
316
|
+
} else {
|
|
317
|
+
const seg = new THREE.BoxGeometry(segLen, beltThickness, drumLength)
|
|
318
|
+
seg.rotateZ(segAngle)
|
|
319
|
+
seg.translate((p0 + p1) / 2, (y0 + y1) / 2, 0)
|
|
320
|
+
wrapGeometries.push(seg)
|
|
321
|
+
}
|
|
251
322
|
}
|
|
252
323
|
const wrapMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(wrapGeometries), beltMaterial)
|
|
253
324
|
wrapMesh.castShadow = true
|
|
254
|
-
this.
|
|
325
|
+
this.components3D.add(wrapMesh)
|
|
255
326
|
}
|
|
256
327
|
|
|
257
328
|
// Flat belt bottom (return path)
|
|
258
|
-
const bottomGeo =
|
|
329
|
+
const bottomGeo = isVertical
|
|
330
|
+
? new THREE.BoxGeometry(drumLength * 0.85, beltThickness, flatLen)
|
|
331
|
+
: new THREE.BoxGeometry(flatLen, beltThickness, drumLength * 0.85)
|
|
259
332
|
const bottomMesh = new THREE.Mesh(bottomGeo, beltMaterial)
|
|
260
333
|
bottomMesh.position.set(0, beltY - drumRadius, 0)
|
|
261
|
-
this.
|
|
334
|
+
this.components3D.add(bottomMesh)
|
|
262
335
|
}
|
|
263
336
|
|
|
264
|
-
private buildRails(width: number, height: number, depth: number, frameH: number, railW: number) {
|
|
337
|
+
private buildRails(width: number, height: number, depth: number, frameH: number, railW: number, isVertical: boolean = false) {
|
|
265
338
|
const color = this.resolveColor()
|
|
266
339
|
const railHeight = frameH * 0.3
|
|
267
340
|
const railY = depth - railHeight / 2
|
|
268
|
-
const
|
|
341
|
+
const beltWidth = isVertical ? width : height
|
|
342
|
+
const transportLen = isVertical ? height : width
|
|
343
|
+
const innerWidth = beltWidth - railW * 2
|
|
269
344
|
// 두 레일 간격: 내부 폭의 60%
|
|
270
345
|
const railSpacing = innerWidth * 0.6
|
|
271
346
|
|
|
@@ -277,16 +352,24 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
277
352
|
|
|
278
353
|
const railGeometries: THREE.BufferGeometry[] = []
|
|
279
354
|
|
|
280
|
-
// Two parallel rails running along
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
355
|
+
// Two parallel rails running along transport direction
|
|
356
|
+
if (isVertical) {
|
|
357
|
+
for (const xSign of [-1, 1]) {
|
|
358
|
+
const rail = new THREE.BoxGeometry(railW * 0.8, railHeight, transportLen - railW * 2)
|
|
359
|
+
rail.translate(xSign * (railSpacing / 2), railY, 0)
|
|
360
|
+
railGeometries.push(rail)
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
for (const zSign of [-1, 1]) {
|
|
364
|
+
const rail = new THREE.BoxGeometry(transportLen - railW * 2, railHeight, railW * 0.8)
|
|
365
|
+
rail.translate(0, railY, zSign * (railSpacing / 2))
|
|
366
|
+
railGeometries.push(rail)
|
|
367
|
+
}
|
|
285
368
|
}
|
|
286
369
|
|
|
287
370
|
const railMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(railGeometries), railMaterial)
|
|
288
371
|
railMesh.castShadow = true
|
|
289
|
-
this.
|
|
372
|
+
this.components3D.add(railMesh)
|
|
290
373
|
}
|
|
291
374
|
|
|
292
375
|
updateDimension() {}
|
|
@@ -295,6 +378,7 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
295
378
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>) {
|
|
296
379
|
if (
|
|
297
380
|
'conveyorType' in after ||
|
|
381
|
+
'orientation' in after ||
|
|
298
382
|
'width' in after ||
|
|
299
383
|
'height' in after ||
|
|
300
384
|
'depth' in after ||
|
|
@@ -27,10 +27,6 @@ export class Crane3D extends RealObjectGroup {
|
|
|
27
27
|
return depth || 5 * TRANSPORT_SURFACE_HEIGHT - 2
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
protected get syncZPosOffset(): number {
|
|
31
|
-
return 0
|
|
32
|
-
}
|
|
33
|
-
|
|
34
30
|
private resolveColor(): number {
|
|
35
31
|
const comp = this.component as any
|
|
36
32
|
|
|
@@ -62,6 +58,8 @@ export class Crane3D extends RealObjectGroup {
|
|
|
62
58
|
const d = this.effectiveDepth
|
|
63
59
|
if (!w || !h) return
|
|
64
60
|
|
|
61
|
+
const inner = this.components3D
|
|
62
|
+
|
|
65
63
|
// 높이 배분: 하단 클램프 4%, 마스트 92%, 상단 클램프 4%
|
|
66
64
|
const clampH = d * 0.04
|
|
67
65
|
const mastH = d * 0.92
|
|
@@ -81,7 +79,7 @@ export class Crane3D extends RealObjectGroup {
|
|
|
81
79
|
const bottomClamp = new THREE.Mesh(bottomClampGeo, clampMat)
|
|
82
80
|
bottomClamp.position.y = clampH / 2
|
|
83
81
|
bottomClamp.castShadow = true
|
|
84
|
-
|
|
82
|
+
inner.add(bottomClamp)
|
|
85
83
|
|
|
86
84
|
// ── 2. 수직 마스트 (좌우 기둥 + 상·하 가로 연결 바) ──
|
|
87
85
|
const mastBaseY = clampH
|
|
@@ -115,7 +113,7 @@ export class Crane3D extends RealObjectGroup {
|
|
|
115
113
|
|
|
116
114
|
const mastMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(mastGeometries), mastMat)
|
|
117
115
|
mastMesh.castShadow = true
|
|
118
|
-
|
|
116
|
+
inner.add(mastMesh)
|
|
119
117
|
|
|
120
118
|
// ── 3. 캐리지 포크 (마스트 40% 높이, 양쪽으로 뻗는 포크 암) ──
|
|
121
119
|
const forkY = mastBaseY + mastH * 0.4
|
|
@@ -151,14 +149,14 @@ export class Crane3D extends RealObjectGroup {
|
|
|
151
149
|
|
|
152
150
|
const forkMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(forkGeometries), forkMat)
|
|
153
151
|
forkMesh.castShadow = true
|
|
154
|
-
|
|
152
|
+
inner.add(forkMesh)
|
|
155
153
|
|
|
156
154
|
// ── 4. 상단 클램프 (천장 레일에 물리는 가이드부) ──
|
|
157
155
|
const topClampGeo = new THREE.BoxGeometry(clampW, clampH, clampD)
|
|
158
156
|
const topClamp = new THREE.Mesh(topClampGeo, clampMat)
|
|
159
157
|
topClamp.position.y = mastBaseY + mastH + clampH / 2
|
|
160
158
|
topClamp.castShadow = true
|
|
161
|
-
|
|
159
|
+
inner.add(topClamp)
|
|
162
160
|
}
|
|
163
161
|
|
|
164
162
|
updateDimension() {}
|
|
@@ -28,10 +28,6 @@ export class CraneRail3D extends RealObjectGroup {
|
|
|
28
28
|
return depth || DEFAULT_RAIL_DEPTH
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
protected get syncZPosOffset(): number {
|
|
32
|
-
return 0
|
|
33
|
-
}
|
|
34
|
-
|
|
35
31
|
build() {
|
|
36
32
|
super.build()
|
|
37
33
|
|
|
@@ -41,6 +37,8 @@ export class CraneRail3D extends RealObjectGroup {
|
|
|
41
37
|
const d = this.effectiveDepth
|
|
42
38
|
if (!w || !h) return
|
|
43
39
|
|
|
40
|
+
const inner = this.components3D
|
|
41
|
+
|
|
44
42
|
const railH = Math.max(d * 0.02, 2) // 레일 높이 (얇은 트랙)
|
|
45
43
|
const railW = Math.max(h * 0.06, 2) // 레일 폭
|
|
46
44
|
const supportThick = Math.max(Math.min(w, h) * 0.05, 2)
|
|
@@ -67,7 +65,7 @@ export class CraneRail3D extends RealObjectGroup {
|
|
|
67
65
|
const bottomMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(bottomGeometries), railMat)
|
|
68
66
|
bottomMesh.castShadow = true
|
|
69
67
|
bottomMesh.receiveShadow = true
|
|
70
|
-
|
|
68
|
+
inner.add(bottomMesh)
|
|
71
69
|
|
|
72
70
|
// ── 천장 가이드 레일 (2개 평행 트랙) ──
|
|
73
71
|
const topGeometries: THREE.BufferGeometry[] = []
|
|
@@ -78,7 +76,7 @@ export class CraneRail3D extends RealObjectGroup {
|
|
|
78
76
|
}
|
|
79
77
|
const topMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(topGeometries), railMat)
|
|
80
78
|
topMesh.castShadow = true
|
|
81
|
-
|
|
79
|
+
inner.add(topMesh)
|
|
82
80
|
|
|
83
81
|
// ── 양 끝단 수직 지지대 (바닥~천장 연결) ──
|
|
84
82
|
const supportH = d - railH * 2
|
|
@@ -96,7 +94,7 @@ export class CraneRail3D extends RealObjectGroup {
|
|
|
96
94
|
}
|
|
97
95
|
const supportMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(supportGeometries), supportMat)
|
|
98
96
|
supportMesh.castShadow = true
|
|
99
|
-
|
|
97
|
+
inner.add(supportMesh)
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
updateDimension() {}
|