@fmsim/machine 2.0.0-beta.10 → 2.0.0-beta.100

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 (152) hide show
  1. package/dist/agv.js +6 -2
  2. package/dist/agv.js.map +1 -1
  3. package/dist/carrier-manager.js +183 -0
  4. package/dist/carrier-manager.js.map +1 -0
  5. package/dist/carrier.js +99 -9
  6. package/dist/carrier.js.map +1 -1
  7. package/dist/conveyor-join.js +8 -2
  8. package/dist/conveyor-join.js.map +1 -1
  9. package/dist/conveyor.js +8 -0
  10. package/dist/conveyor.js.map +1 -1
  11. package/dist/crane.js +144 -1
  12. package/dist/crane.js.map +1 -1
  13. package/dist/factories/agv-3d.js +8 -9
  14. package/dist/factories/agv-3d.js.map +1 -1
  15. package/dist/factories/conveyor-3d.js +168 -81
  16. package/dist/factories/conveyor-3d.js.map +1 -1
  17. package/dist/factories/crane-3d.js +48 -12
  18. package/dist/factories/crane-3d.js.map +1 -1
  19. package/dist/factories/crane-rail-3d.js +6 -6
  20. package/dist/factories/crane-rail-3d.js.map +1 -1
  21. package/dist/factories/machine-3d.js +527 -181
  22. package/dist/factories/machine-3d.js.map +1 -1
  23. package/dist/factories/oht-3d.js +20 -20
  24. package/dist/factories/oht-3d.js.map +1 -1
  25. package/dist/factories/shuttle-3d.js +2 -5
  26. package/dist/factories/shuttle-3d.js.map +1 -1
  27. package/dist/factories/z-priority-defaults.js +33 -0
  28. package/dist/factories/z-priority-defaults.js.map +1 -0
  29. package/dist/factories/z-priority.js +54 -0
  30. package/dist/factories/z-priority.js.map +1 -0
  31. package/dist/features/conveyor-mixin.js.map +1 -1
  32. package/dist/features/mcs-status-default.js +15 -1
  33. package/dist/features/mcs-status-default.js.map +1 -1
  34. package/dist/features/mcs-status-mixin.js +22 -0
  35. package/dist/features/mcs-status-mixin.js.map +1 -1
  36. package/dist/features/mcs-zone-mixin.js.map +1 -1
  37. package/dist/features/parent-object-mixin.js +15 -7
  38. package/dist/features/parent-object-mixin.js.map +1 -1
  39. package/dist/floating-container.js +5 -0
  40. package/dist/floating-container.js.map +1 -1
  41. package/dist/index.js +52 -34
  42. package/dist/index.js.map +1 -1
  43. package/dist/mcs-carrier-holder.js +56 -22
  44. package/dist/mcs-carrier-holder.js.map +1 -1
  45. package/dist/mcs-machine.js +39 -0
  46. package/dist/mcs-machine.js.map +1 -1
  47. package/dist/mcs-unit.js +20 -0
  48. package/dist/mcs-unit.js.map +1 -1
  49. package/dist/mcs-vehicle.js +8 -3
  50. package/dist/mcs-vehicle.js.map +1 -1
  51. package/dist/node.js +10 -3
  52. package/dist/node.js.map +1 -1
  53. package/dist/oht.js +4 -0
  54. package/dist/oht.js.map +1 -1
  55. package/dist/port-flow.js +4 -0
  56. package/dist/port-flow.js.map +1 -1
  57. package/dist/port.js +4 -0
  58. package/dist/port.js.map +1 -1
  59. package/dist/scanner.js +30 -0
  60. package/dist/scanner.js.map +1 -0
  61. package/dist/scene/root-container-override.js +160 -96
  62. package/dist/scene/root-container-override.js.map +1 -1
  63. package/dist/shelf.js +4 -0
  64. package/dist/shelf.js.map +1 -1
  65. package/dist/static-container.js +5 -0
  66. package/dist/static-container.js.map +1 -1
  67. package/dist/stocker-abnormal-bar.js +5 -1
  68. package/dist/stocker-abnormal-bar.js.map +1 -1
  69. package/dist/stocker-capacity-bar.js +253 -6
  70. package/dist/stocker-capacity-bar.js.map +1 -1
  71. package/dist/stocker-carrier-bar.js +5 -1
  72. package/dist/stocker-carrier-bar.js.map +1 -1
  73. package/dist/stocker.js +6 -0
  74. package/dist/stocker.js.map +1 -1
  75. package/dist/templates/backdrop.js +30 -0
  76. package/dist/templates/backdrop.js.map +1 -0
  77. package/dist/templates/decal.js +21 -0
  78. package/dist/templates/decal.js.map +1 -0
  79. package/dist/templates/index.js +7 -1
  80. package/dist/templates/index.js.map +1 -1
  81. package/dist/templates/scanner.js +20 -0
  82. package/dist/templates/scanner.js.map +1 -0
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/dist/utils/crane-position.js +83 -0
  85. package/dist/utils/crane-position.js.map +1 -0
  86. package/dist/utils/data-value.js +21 -0
  87. package/dist/utils/data-value.js.map +1 -0
  88. package/dist/utils/fit-gauge-scale.js +28 -0
  89. package/dist/utils/fit-gauge-scale.js.map +1 -0
  90. package/dist/utils/get-value-on-ranges.js +2 -4
  91. package/dist/utils/get-value-on-ranges.js.map +1 -1
  92. package/dist/utils/stocker-gauge.js +78 -0
  93. package/dist/utils/stocker-gauge.js.map +1 -0
  94. package/dist/zone-capacity-bar.js +3 -3
  95. package/dist/zone-capacity-bar.js.map +1 -1
  96. package/icons/backdrop.png +0 -0
  97. package/icons/decal.png +0 -0
  98. package/jest.config.js +10 -0
  99. package/package.json +4 -3
  100. package/src/agv.ts +7 -2
  101. package/src/carrier-manager.ts +223 -0
  102. package/src/carrier.ts +103 -10
  103. package/src/conveyor-join.ts +5 -2
  104. package/src/conveyor.ts +8 -0
  105. package/src/crane.ts +181 -1
  106. package/src/factories/agv-3d.ts +9 -10
  107. package/src/factories/conveyor-3d.ts +171 -84
  108. package/src/factories/crane-3d.ts +53 -12
  109. package/src/factories/crane-rail-3d.ts +8 -7
  110. package/src/factories/machine-3d.ts +616 -191
  111. package/src/factories/oht-3d.ts +20 -20
  112. package/src/factories/shuttle-3d.ts +2 -6
  113. package/src/factories/z-priority-defaults.ts +38 -0
  114. package/src/factories/z-priority.ts +60 -0
  115. package/src/features/conveyor-mixin.ts +1 -1
  116. package/src/features/mcs-status-default.ts +15 -1
  117. package/src/features/mcs-status-mixin.ts +26 -4
  118. package/src/features/mcs-zone-mixin.ts +1 -1
  119. package/src/features/parent-object-mixin.ts +17 -8
  120. package/src/floating-container.ts +5 -0
  121. package/src/index.ts +58 -34
  122. package/src/mcs-carrier-holder.ts +57 -34
  123. package/src/mcs-machine.ts +42 -0
  124. package/src/mcs-unit.ts +24 -1
  125. package/src/mcs-vehicle.ts +9 -6
  126. package/src/node.ts +9 -3
  127. package/src/oht.ts +6 -1
  128. package/src/port-flow.ts +5 -0
  129. package/src/port.ts +5 -0
  130. package/src/scanner.ts +29 -0
  131. package/src/scene/root-container-override.ts +154 -103
  132. package/src/shelf.ts +5 -0
  133. package/src/static-container.ts +5 -0
  134. package/src/stocker-abnormal-bar.ts +6 -1
  135. package/src/stocker-capacity-bar.ts +298 -7
  136. package/src/stocker-carrier-bar.ts +6 -1
  137. package/src/stocker.ts +7 -0
  138. package/src/templates/backdrop.ts +30 -0
  139. package/src/templates/decal.ts +21 -0
  140. package/src/templates/index.ts +7 -1
  141. package/src/templates/scanner.ts +20 -0
  142. package/src/utils/crane-position.ts +105 -0
  143. package/src/utils/data-value.ts +19 -0
  144. package/src/utils/fit-gauge-scale.ts +35 -0
  145. package/src/utils/get-value-on-ranges.ts +2 -4
  146. package/src/utils/stocker-gauge.ts +111 -0
  147. package/src/zone-capacity-bar.ts +3 -3
  148. package/translations/en.json +26 -1
  149. package/translations/ja.json +26 -1
  150. package/translations/ko.json +26 -1
  151. package/translations/ms.json +26 -1
  152. package/translations/zh.json +26 -1
@@ -14,26 +14,23 @@ import * as THREE from 'three';
14
14
  import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
15
15
  import { RealObjectGroup, registerRealObjectFactory } from '@hatiolab/things-scene';
16
16
  import { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js';
17
+ import { Z_FMSIM } from './z-priority';
17
18
  const FRAME_COLOR = 0x888899;
18
19
  const ROLLER_COLOR = 0xaaaabc;
19
20
  const DEFAULT_COLOR = 0x999999;
20
21
  export class Conveyor3D extends RealObjectGroup {
22
+ get zPriority() { return Z_FMSIM.CONVEYOR; }
21
23
  get effectiveDepth() {
22
24
  const { depth } = this.component.state;
23
25
  return depth || TRANSPORT_SURFACE_HEIGHT;
24
26
  }
25
- // group origin = 바닥 (y=0). MCSRealObject3D와 동일 컨벤션.
26
- // 기하는 build()에서 y=0(바닥)~y=depth(상단) 범위로 배치.
27
- get syncZPosOffset() {
28
- return 0;
29
- }
30
27
  resolveColor() {
31
28
  const comp = this.component;
32
29
  // 1) MCSStatusMixin.statusColor (legend 기반 상태 색상)
33
30
  // '#F0F0F0'은 상태 미설정 시 기본 fallback — 무시하고 defaultColor 사용
34
31
  try {
35
32
  const sc = comp.statusColor;
36
- if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0')
33
+ if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0' && sc !== 'transparent')
37
34
  return new THREE.Color(sc).getHex();
38
35
  }
39
36
  catch (_a) {
@@ -41,7 +38,7 @@ export class Conveyor3D extends RealObjectGroup {
41
38
  }
42
39
  // 2) state.fillStyle fallback
43
40
  const { fillStyle } = this.component.state;
44
- if (fillStyle && typeof fillStyle === 'string') {
41
+ if (fillStyle && typeof fillStyle === 'string' && fillStyle !== 'transparent') {
45
42
  try {
46
43
  return new THREE.Color(fillStyle).getHex();
47
44
  }
@@ -53,16 +50,29 @@ export class Conveyor3D extends RealObjectGroup {
53
50
  }
54
51
  build() {
55
52
  super.build();
56
- const { width, height, conveyorType } = this.component.state;
53
+ const { width: rawWidth, height: rawHeight, conveyorType, orientation } = this.component.state;
57
54
  const depth = this.effectiveDepth;
58
- if (!width || !height)
55
+ if (!rawWidth || !rawHeight)
59
56
  return;
57
+ // orientation: 'horizontal' = 롤러가 Z축(세로), 'vertical' = 롤러가 X축(가로)
58
+ // default = 긴 방향에 수직 (긴 축을 따라 이송)
59
+ const isVertical = orientation === 'vertical' ? true
60
+ : orientation === 'horizontal' ? false
61
+ : rawWidth < rawHeight; // default: 세로가 길면 롤러는 가로(X축)
62
+ // isVertical=true → 롤러 축이 X(가로), 이송 방향이 Z(세로)
63
+ // isVertical=false → 롤러 축이 Z(세로), 이송 방향이 X(가로)
64
+ const width = rawWidth;
65
+ const height = rawHeight;
60
66
  // 2D와 동일 로직으로 rollWidth 산출
61
67
  const rollWidth = Math.max(10, Math.min(width, height) / 2);
62
68
  const rollerDiameter = Math.max(rollWidth, 2);
63
69
  const frameH = Math.min(rollerDiameter, depth);
64
70
  const legH = Math.max(depth - frameH, 0);
65
- const railW = Math.max(height * 0.06, 2);
71
+ // isVertical일 때 이송 방향이 Z축: 레일은 Z축을 따라, 벨트폭은 width
72
+ // !isVertical일 때 이송 방향이 X축: 레일은 X축을 따라, 벨트폭은 height
73
+ const beltWidth = isVertical ? width : height;
74
+ const railLen = isVertical ? height : width;
75
+ const railW = Math.max(beltWidth * 0.06, 2);
66
76
  const legThickness = Math.max(railW * 0.8, 2);
67
77
  const frameMaterial = new THREE.MeshStandardMaterial({
68
78
  color: FRAME_COLOR,
@@ -73,103 +83,156 @@ export class Conveyor3D extends RealObjectGroup {
73
83
  // Y 좌표: y=0(바닥) ~ y=depth(상단). 레일 상단이 depth에 위치.
74
84
  const frameGeometries = [];
75
85
  const railY = depth - frameH / 2;
76
- // Side rails (along X)
77
- for (const zSign of [-1, 1]) {
78
- const rail = new THREE.BoxGeometry(width, frameH, railW);
79
- rail.translate(0, railY, zSign * (height / 2 - railW / 2));
80
- frameGeometries.push(rail);
86
+ // Side rails — 이송 방향을 따라, 벨트 양측에 배치
87
+ if (isVertical) {
88
+ // 레일이 Z축(세로)을 따라, ±X에 배치
89
+ for (const xSign of [-1, 1]) {
90
+ const rail = new THREE.BoxGeometry(railW, frameH, height);
91
+ rail.translate(xSign * (width / 2 - railW / 2), railY, 0);
92
+ frameGeometries.push(rail);
93
+ }
94
+ }
95
+ else {
96
+ // 레일이 X축(가로)을 따라, ±Z에 배치
97
+ for (const zSign of [-1, 1]) {
98
+ const rail = new THREE.BoxGeometry(width, frameH, railW);
99
+ rail.translate(0, railY, zSign * (height / 2 - railW / 2));
100
+ frameGeometries.push(rail);
101
+ }
81
102
  }
82
103
  // 4 legs at corners
83
104
  if (legH > 0) {
84
105
  const legTopY = depth - frameH;
85
106
  const legCenterY = legTopY - legH / 2;
107
+ // 다리 위치: 레일 안쪽에 정렬
108
+ const legXPos = isVertical ? (width / 2 - railW / 2) : (width / 2 - legThickness / 2);
109
+ const legZPos = isVertical ? (height / 2 - legThickness / 2) : (height / 2 - railW / 2);
86
110
  for (const xSign of [-1, 1]) {
87
111
  for (const zSign of [-1, 1]) {
88
- const lx = xSign * (width / 2 - legThickness / 2);
89
- const lz = zSign * (height / 2 - railW / 2);
90
112
  const leg = new THREE.BoxGeometry(legThickness, legH, legThickness);
91
- leg.translate(lx, legCenterY, lz);
113
+ leg.translate(xSign * legXPos, legCenterY, zSign * legZPos);
92
114
  frameGeometries.push(leg);
93
115
  }
94
116
  }
95
- // Cross-bracing: 앞뒤 다리 연결 (Z 방향)
117
+ // Cross-bracing
96
118
  const braceH = legThickness * 0.6;
97
119
  const braceW = legThickness * 0.6;
98
- const braceLen = height - railW;
99
120
  const braceY = legTopY - legH * 0.35;
100
- for (const xSign of [-1, 1]) {
101
- const brace = new THREE.BoxGeometry(braceW, braceH, braceLen);
102
- brace.translate(xSign * (width / 2 - legThickness / 2), braceY, 0);
103
- frameGeometries.push(brace);
121
+ // 이송 방향 브레이스 (레일과 평행)
122
+ if (isVertical) {
123
+ for (const xSign of [-1, 1]) {
124
+ const brace = new THREE.BoxGeometry(braceW, braceH, height - legThickness);
125
+ brace.translate(xSign * legXPos, braceY, 0);
126
+ frameGeometries.push(brace);
127
+ }
104
128
  }
105
- // Cross-bracing: 좌우 다리 연결 (X 방향)
106
- const braceLenX = width - legThickness;
107
- for (const zSign of [-1, 1]) {
108
- const brace = new THREE.BoxGeometry(braceLenX, braceH, braceW);
109
- brace.translate(0, braceY, zSign * (height / 2 - railW / 2));
110
- frameGeometries.push(brace);
129
+ else {
130
+ for (const zSign of [-1, 1]) {
131
+ const brace = new THREE.BoxGeometry(width - legThickness, braceH, braceW);
132
+ brace.translate(0, braceY, zSign * legZPos);
133
+ frameGeometries.push(brace);
134
+ }
135
+ }
136
+ // 벨트폭 방향 브레이스 (레일과 수직)
137
+ if (isVertical) {
138
+ for (const zSign of [-1, 1]) {
139
+ const brace = new THREE.BoxGeometry(width - railW, braceH, braceW);
140
+ brace.translate(0, braceY, zSign * legZPos);
141
+ frameGeometries.push(brace);
142
+ }
143
+ }
144
+ else {
145
+ for (const xSign of [-1, 1]) {
146
+ const brace = new THREE.BoxGeometry(braceW, braceH, height - railW);
147
+ brace.translate(xSign * legXPos, braceY, 0);
148
+ frameGeometries.push(brace);
149
+ }
111
150
  }
112
151
  }
113
152
  const frameMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(frameGeometries), frameMaterial);
114
153
  frameMesh.castShadow = true;
115
154
  frameMesh.receiveShadow = true;
116
- this.object3d.add(frameMesh);
155
+ this.components3D.add(frameMesh);
117
156
  // --- Rollers, Belt, or Rails ---
118
157
  if (conveyorType === 'rail') {
119
- this.buildRails(width, height, depth, frameH, railW);
158
+ this.buildRails(width, height, depth, frameH, railW, isVertical);
120
159
  }
121
160
  else if (conveyorType === 'belt') {
122
- this.buildBelt(width, height, depth, frameH, railW);
161
+ this.buildBelt(width, height, depth, frameH, railW, isVertical);
123
162
  }
124
163
  else {
125
- this.buildRollers(width, height, depth, frameH, railW, rollWidth);
164
+ this.buildRollers(width, height, depth, frameH, railW, rollWidth, isVertical);
126
165
  }
127
166
  }
128
- buildRollers(width, height, depth, frameH, railW, rollWidth) {
167
+ buildRollers(width, height, depth, frameH, railW, rollWidth, isVertical = false) {
129
168
  const rollerRadius = Math.max(rollWidth / 8, 1);
130
- const rollerLength = height - railW * 2 - 0.5;
131
169
  const diameter = rollerRadius * 2;
132
- // 롤러 간 최소 간격 (z-fighting 방지용 미세 갭만)
133
170
  const step = diameter * 1.05;
134
- // 롤러 상단 = 이송 표면 (y=depth)
135
171
  const rollerY = depth - rollerRadius;
136
172
  const color = this.resolveColor();
137
173
  const rollerGeometries = [];
138
- const count = Math.max(1, Math.floor(width / step));
139
- const totalSpan = (count - 1) * step;
140
- const startX = -totalSpan / 2;
141
- for (let i = 0; i < count; i++) {
142
- const x = startX + i * step;
143
- const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16);
144
- roller.rotateX(Math.PI / 2);
145
- roller.translate(x, rollerY, 0);
146
- rollerGeometries.push(roller);
174
+ if (isVertical) {
175
+ // 롤러 축 = X축(가로), 이송 방향 = Z축(세로)
176
+ const rollerLength = width - railW * 2 - 0.5;
177
+ const count = Math.max(1, Math.floor(height / step));
178
+ const totalSpan = (count - 1) * step;
179
+ const startZ = -totalSpan / 2;
180
+ for (let i = 0; i < count; i++) {
181
+ const z = startZ + i * step;
182
+ const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16);
183
+ roller.rotateZ(Math.PI / 2);
184
+ roller.translate(0, rollerY, z);
185
+ rollerGeometries.push(roller);
186
+ }
187
+ }
188
+ else {
189
+ // 롤러 축 = Z축(세로), 이송 방향 = X축(가로) — 기존 동작
190
+ const rollerLength = height - railW * 2 - 0.5;
191
+ const count = Math.max(1, Math.floor(width / step));
192
+ const totalSpan = (count - 1) * step;
193
+ const startX = -totalSpan / 2;
194
+ for (let i = 0; i < count; i++) {
195
+ const x = startX + i * step;
196
+ const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16);
197
+ roller.rotateX(Math.PI / 2);
198
+ roller.translate(x, rollerY, 0);
199
+ rollerGeometries.push(roller);
200
+ }
147
201
  }
148
202
  if (rollerGeometries.length > 0) {
149
203
  const rollerMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(rollerGeometries), new THREE.MeshStandardMaterial({ color, metalness: 0.9, roughness: 0.2 }));
150
204
  rollerMesh.castShadow = true;
151
- this.object3d.add(rollerMesh);
205
+ this.components3D.add(rollerMesh);
152
206
  }
153
207
  }
154
- buildBelt(width, height, depth, frameH, railW) {
208
+ buildBelt(width, height, depth, frameH, railW, isVertical = false) {
155
209
  const drumRadius = frameH * 0.38;
156
- const drumLength = height - railW * 2;
157
210
  const beltY = depth - drumRadius;
158
211
  const color = this.resolveColor();
212
+ // 이송 방향 길이와 벨트폭
213
+ const transportLen = isVertical ? height : width;
214
+ const beltWidth = isVertical ? width : height;
215
+ const drumLength = beltWidth - railW * 2;
159
216
  // Two end drums
160
217
  const drumGeometries = [];
161
218
  const drumInset = drumRadius * 1.5;
162
- const leftDrumX = -width / 2 + drumInset;
163
- const rightDrumX = width / 2 - drumInset;
164
- for (const dx of [leftDrumX, rightDrumX]) {
219
+ const drum1 = -transportLen / 2 + drumInset;
220
+ const drum2 = transportLen / 2 - drumInset;
221
+ for (const dp of [drum1, drum2]) {
165
222
  const drum = new THREE.CylinderGeometry(drumRadius, drumRadius, drumLength, 16);
166
- drum.rotateX(Math.PI / 2);
167
- drum.translate(dx, beltY, 0);
223
+ if (isVertical) {
224
+ drum.rotateZ(Math.PI / 2);
225
+ drum.translate(0, beltY, dp);
226
+ }
227
+ else {
228
+ drum.rotateX(Math.PI / 2);
229
+ drum.translate(dp, beltY, 0);
230
+ }
168
231
  drumGeometries.push(drum);
169
232
  }
170
233
  const drumMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(drumGeometries), new THREE.MeshStandardMaterial({ color: ROLLER_COLOR, metalness: 0.9, roughness: 0.2 }));
171
234
  drumMesh.castShadow = true;
172
- this.object3d.add(drumMesh);
235
+ this.components3D.add(drumMesh);
173
236
  // Belt surface
174
237
  const beltThickness = drumRadius * 0.12;
175
238
  const beltMaterial = new THREE.MeshStandardMaterial({
@@ -178,49 +241,63 @@ export class Conveyor3D extends RealObjectGroup {
178
241
  roughness: 0.9,
179
242
  });
180
243
  // Flat belt top surface spanning between drums
181
- const flatLen = rightDrumX - leftDrumX;
182
- const flatGeo = new THREE.BoxGeometry(flatLen, beltThickness, drumLength);
244
+ const flatLen = drum2 - drum1;
245
+ const flatGeo = isVertical
246
+ ? new THREE.BoxGeometry(drumLength, beltThickness, flatLen)
247
+ : new THREE.BoxGeometry(flatLen, beltThickness, drumLength);
183
248
  const flatMesh = new THREE.Mesh(flatGeo, beltMaterial);
184
249
  flatMesh.position.set(0, beltY + drumRadius, 0);
185
250
  flatMesh.castShadow = true;
186
- this.object3d.add(flatMesh);
251
+ this.components3D.add(flatMesh);
187
252
  // Belt wrap around drums (segmented half-cylinder)
188
253
  const wrapSegments = 12;
189
- for (const [dx, angleStart] of [
190
- [leftDrumX, Math.PI / 2],
191
- [rightDrumX, -Math.PI / 2],
254
+ for (const [dp, angleStart] of [
255
+ [drum1, Math.PI / 2],
256
+ [drum2, -Math.PI / 2],
192
257
  ]) {
193
258
  const wrapGeometries = [];
194
259
  for (let i = 0; i < wrapSegments; i++) {
195
260
  const a0 = angleStart + (Math.PI * i) / wrapSegments;
196
261
  const a1 = angleStart + (Math.PI * (i + 1)) / wrapSegments;
197
262
  const r = drumRadius + beltThickness / 2;
198
- const x0 = dx + Math.cos(a0) * r;
263
+ const p0 = dp + Math.cos(a0) * r;
199
264
  const y0 = beltY + Math.sin(a0) * r;
200
- const x1 = dx + Math.cos(a1) * r;
265
+ const p1 = dp + Math.cos(a1) * r;
201
266
  const y1 = beltY + Math.sin(a1) * r;
202
- const segLen = Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2) * 1.05;
203
- const segAngle = Math.atan2(y1 - y0, x1 - x0);
204
- const seg = new THREE.BoxGeometry(segLen, beltThickness, drumLength);
205
- seg.rotateZ(segAngle);
206
- seg.translate((x0 + x1) / 2, (y0 + y1) / 2, 0);
207
- wrapGeometries.push(seg);
267
+ const segLen = Math.sqrt((p1 - p0) ** 2 + (y1 - y0) ** 2) * 1.05;
268
+ const segAngle = Math.atan2(y1 - y0, p1 - p0);
269
+ if (isVertical) {
270
+ const seg = new THREE.BoxGeometry(drumLength, beltThickness, segLen);
271
+ seg.rotateX(-segAngle);
272
+ seg.translate(0, (y0 + y1) / 2, (p0 + p1) / 2);
273
+ wrapGeometries.push(seg);
274
+ }
275
+ else {
276
+ const seg = new THREE.BoxGeometry(segLen, beltThickness, drumLength);
277
+ seg.rotateZ(segAngle);
278
+ seg.translate((p0 + p1) / 2, (y0 + y1) / 2, 0);
279
+ wrapGeometries.push(seg);
280
+ }
208
281
  }
209
282
  const wrapMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(wrapGeometries), beltMaterial);
210
283
  wrapMesh.castShadow = true;
211
- this.object3d.add(wrapMesh);
284
+ this.components3D.add(wrapMesh);
212
285
  }
213
286
  // Flat belt bottom (return path)
214
- const bottomGeo = new THREE.BoxGeometry(flatLen, beltThickness, drumLength * 0.85);
287
+ const bottomGeo = isVertical
288
+ ? new THREE.BoxGeometry(drumLength * 0.85, beltThickness, flatLen)
289
+ : new THREE.BoxGeometry(flatLen, beltThickness, drumLength * 0.85);
215
290
  const bottomMesh = new THREE.Mesh(bottomGeo, beltMaterial);
216
291
  bottomMesh.position.set(0, beltY - drumRadius, 0);
217
- this.object3d.add(bottomMesh);
292
+ this.components3D.add(bottomMesh);
218
293
  }
219
- buildRails(width, height, depth, frameH, railW) {
294
+ buildRails(width, height, depth, frameH, railW, isVertical = false) {
220
295
  const color = this.resolveColor();
221
296
  const railHeight = frameH * 0.3;
222
297
  const railY = depth - railHeight / 2;
223
- const innerWidth = height - railW * 2;
298
+ const beltWidth = isVertical ? width : height;
299
+ const transportLen = isVertical ? height : width;
300
+ const innerWidth = beltWidth - railW * 2;
224
301
  // 두 레일 간격: 내부 폭의 60%
225
302
  const railSpacing = innerWidth * 0.6;
226
303
  const railMaterial = new THREE.MeshStandardMaterial({
@@ -229,20 +306,30 @@ export class Conveyor3D extends RealObjectGroup {
229
306
  roughness: 0.3,
230
307
  });
231
308
  const railGeometries = [];
232
- // Two parallel rails running along X axis
233
- for (const zSign of [-1, 1]) {
234
- const rail = new THREE.BoxGeometry(width - railW * 2, railHeight, railW * 0.8);
235
- rail.translate(0, railY, zSign * (railSpacing / 2));
236
- railGeometries.push(rail);
309
+ // Two parallel rails running along transport direction
310
+ if (isVertical) {
311
+ for (const xSign of [-1, 1]) {
312
+ const rail = new THREE.BoxGeometry(railW * 0.8, railHeight, transportLen - railW * 2);
313
+ rail.translate(xSign * (railSpacing / 2), railY, 0);
314
+ railGeometries.push(rail);
315
+ }
316
+ }
317
+ else {
318
+ for (const zSign of [-1, 1]) {
319
+ const rail = new THREE.BoxGeometry(transportLen - railW * 2, railHeight, railW * 0.8);
320
+ rail.translate(0, railY, zSign * (railSpacing / 2));
321
+ railGeometries.push(rail);
322
+ }
237
323
  }
238
324
  const railMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(railGeometries), railMaterial);
239
325
  railMesh.castShadow = true;
240
- this.object3d.add(railMesh);
326
+ this.components3D.add(railMesh);
241
327
  }
242
328
  updateDimension() { }
243
329
  updateAlpha() { }
244
330
  onchange(after, before) {
245
331
  if ('conveyorType' in after ||
332
+ 'orientation' in after ||
246
333
  'width' in after ||
247
334
  'height' in after ||
248
335
  'depth' in after ||
@@ -1 +1 @@
1
- {"version":3,"file":"conveyor-3d.js","sourceRoot":"","sources":["../../src/factories/conveyor-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Conveyor 3D Factory\n *\n * 'conveyor' type (default): side rails + cylindrical rollers + 4 legs + cross-bracing\n * 'belt' type: side rails + two end drums + belt wrapping + 4 legs + cross-bracing\n * 'rail' type: side rails + two parallel rails + 4 legs + cross-bracing\n *\n * Based on operato-scene conveyor-3d.ts, adapted for fmsim:\n * - conveyorType is string ('', 'conveyor', 'belt', 'rail') instead of numeric\n * - statusColor from MCSStatusMixin instead of value-based lookup\n * - rollWidth auto-calculated: Math.max(10, Math.min(width, height) / 2)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup, registerRealObjectFactory } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\n\nconst FRAME_COLOR = 0x888899\nconst ROLLER_COLOR = 0xaaaabc\nconst DEFAULT_COLOR = 0x999999\n\nexport class Conveyor3D extends RealObjectGroup {\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n // group origin = 바닥 (y=0). MCSRealObject3D와 동일 컨벤션.\n // 기하는 build()에서 y=0(바닥)~y=depth(상단) 범위로 배치.\n protected get syncZPosOffset(): number {\n return 0\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n // 1) MCSStatusMixin.statusColor (legend 기반 상태 색상)\n // '#F0F0F0'은 상태 미설정 시 기본 fallback — 무시하고 defaultColor 사용\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n // 2) state.fillStyle fallback\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_COLOR\n }\n\n build() {\n super.build()\n\n const { width, height, conveyorType } = this.component.state\n const depth = this.effectiveDepth\n if (!width || !height) return\n\n // 2D와 동일 로직으로 rollWidth 산출\n const rollWidth = Math.max(10, Math.min(width, height) / 2)\n const rollerDiameter = Math.max(rollWidth, 2)\n const frameH = Math.min(rollerDiameter, depth)\n const legH = Math.max(depth - frameH, 0)\n const railW = Math.max(height * 0.06, 2)\n const legThickness = Math.max(railW * 0.8, 2)\n\n const frameMaterial = new THREE.MeshStandardMaterial({\n color: FRAME_COLOR,\n metalness: 0.85,\n roughness: 0.35,\n })\n\n // --- Frame: side rails + legs + cross-bracing ---\n // Y 좌표: y=0(바닥) ~ y=depth(상단). 레일 상단이 depth에 위치.\n const frameGeometries: THREE.BufferGeometry[] = []\n const railY = depth - frameH / 2\n\n // Side rails (along X)\n for (const zSign of [-1, 1]) {\n const rail = new THREE.BoxGeometry(width, frameH, railW)\n rail.translate(0, railY, zSign * (height / 2 - railW / 2))\n frameGeometries.push(rail)\n }\n\n // 4 legs at corners\n if (legH > 0) {\n const legTopY = depth - frameH\n const legCenterY = legTopY - legH / 2\n\n for (const xSign of [-1, 1]) {\n for (const zSign of [-1, 1]) {\n const lx = xSign * (width / 2 - legThickness / 2)\n const lz = zSign * (height / 2 - railW / 2)\n const leg = new THREE.BoxGeometry(legThickness, legH, legThickness)\n leg.translate(lx, legCenterY, lz)\n frameGeometries.push(leg)\n }\n }\n\n // Cross-bracing: 앞뒤 다리 연결 (Z 방향)\n const braceH = legThickness * 0.6\n const braceW = legThickness * 0.6\n const braceLen = height - railW\n const braceY = legTopY - legH * 0.35\n\n for (const xSign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(braceW, braceH, braceLen)\n brace.translate(xSign * (width / 2 - legThickness / 2), braceY, 0)\n frameGeometries.push(brace)\n }\n\n // Cross-bracing: 좌우 다리 연결 (X 방향)\n const braceLenX = width - legThickness\n for (const zSign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(braceLenX, braceH, braceW)\n brace.translate(0, braceY, zSign * (height / 2 - railW / 2))\n frameGeometries.push(brace)\n }\n }\n\n const frameMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(frameGeometries), frameMaterial)\n frameMesh.castShadow = true\n frameMesh.receiveShadow = true\n this.object3d.add(frameMesh)\n\n // --- Rollers, Belt, or Rails ---\n if (conveyorType === 'rail') {\n this.buildRails(width, height, depth, frameH, railW)\n } else if (conveyorType === 'belt') {\n this.buildBelt(width, height, depth, frameH, railW)\n } else {\n this.buildRollers(width, height, depth, frameH, railW, rollWidth)\n }\n }\n\n private buildRollers(\n width: number,\n height: number,\n depth: number,\n frameH: number,\n railW: number,\n rollWidth: number\n ) {\n const rollerRadius = Math.max(rollWidth / 8, 1)\n const rollerLength = height - railW * 2 - 0.5\n const diameter = rollerRadius * 2\n // 롤러 간 최소 간격 (z-fighting 방지용 미세 갭만)\n const step = diameter * 1.05\n // 롤러 상단 = 이송 표면 (y=depth)\n const rollerY = depth - rollerRadius\n const color = this.resolveColor()\n\n const rollerGeometries: THREE.BufferGeometry[] = []\n const count = Math.max(1, Math.floor(width / step))\n const totalSpan = (count - 1) * step\n const startX = -totalSpan / 2\n\n for (let i = 0; i < count; i++) {\n const x = startX + i * step\n const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16)\n roller.rotateX(Math.PI / 2)\n roller.translate(x, rollerY, 0)\n rollerGeometries.push(roller)\n }\n\n if (rollerGeometries.length > 0) {\n const rollerMesh = new THREE.Mesh(\n BufferGeometryUtils.mergeGeometries(rollerGeometries),\n new THREE.MeshStandardMaterial({ color, metalness: 0.9, roughness: 0.2 })\n )\n rollerMesh.castShadow = true\n this.object3d.add(rollerMesh)\n }\n }\n\n private buildBelt(width: number, height: number, depth: number, frameH: number, railW: number) {\n const drumRadius = frameH * 0.38\n const drumLength = height - railW * 2\n const beltY = depth - drumRadius\n const color = this.resolveColor()\n\n // Two end drums\n const drumGeometries: THREE.BufferGeometry[] = []\n const drumInset = drumRadius * 1.5\n const leftDrumX = -width / 2 + drumInset\n const rightDrumX = width / 2 - drumInset\n\n for (const dx of [leftDrumX, rightDrumX]) {\n const drum = new THREE.CylinderGeometry(drumRadius, drumRadius, drumLength, 16)\n drum.rotateX(Math.PI / 2)\n drum.translate(dx, beltY, 0)\n drumGeometries.push(drum)\n }\n\n const drumMesh = new THREE.Mesh(\n BufferGeometryUtils.mergeGeometries(drumGeometries),\n new THREE.MeshStandardMaterial({ color: ROLLER_COLOR, metalness: 0.9, roughness: 0.2 })\n )\n drumMesh.castShadow = true\n this.object3d.add(drumMesh)\n\n // Belt surface\n const beltThickness = drumRadius * 0.12\n const beltMaterial = new THREE.MeshStandardMaterial({\n color: color,\n metalness: 0.0,\n roughness: 0.9,\n })\n\n // Flat belt top surface spanning between drums\n const flatLen = rightDrumX - leftDrumX\n const flatGeo = new THREE.BoxGeometry(flatLen, beltThickness, drumLength)\n const flatMesh = new THREE.Mesh(flatGeo, beltMaterial)\n flatMesh.position.set(0, beltY + drumRadius, 0)\n flatMesh.castShadow = true\n this.object3d.add(flatMesh)\n\n // Belt wrap around drums (segmented half-cylinder)\n const wrapSegments = 12\n for (const [dx, angleStart] of [\n [leftDrumX, Math.PI / 2],\n [rightDrumX, -Math.PI / 2],\n ] as [number, number][]) {\n const wrapGeometries: THREE.BufferGeometry[] = []\n for (let i = 0; i < wrapSegments; i++) {\n const a0 = angleStart + (Math.PI * i) / wrapSegments\n const a1 = angleStart + (Math.PI * (i + 1)) / wrapSegments\n const r = drumRadius + beltThickness / 2\n\n const x0 = dx + Math.cos(a0) * r\n const y0 = beltY + Math.sin(a0) * r\n const x1 = dx + Math.cos(a1) * r\n const y1 = beltY + Math.sin(a1) * r\n\n const segLen = Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2) * 1.05\n const segAngle = Math.atan2(y1 - y0, x1 - x0)\n\n const seg = new THREE.BoxGeometry(segLen, beltThickness, drumLength)\n seg.rotateZ(segAngle)\n seg.translate((x0 + x1) / 2, (y0 + y1) / 2, 0)\n wrapGeometries.push(seg)\n }\n const wrapMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(wrapGeometries), beltMaterial)\n wrapMesh.castShadow = true\n this.object3d.add(wrapMesh)\n }\n\n // Flat belt bottom (return path)\n const bottomGeo = new THREE.BoxGeometry(flatLen, beltThickness, drumLength * 0.85)\n const bottomMesh = new THREE.Mesh(bottomGeo, beltMaterial)\n bottomMesh.position.set(0, beltY - drumRadius, 0)\n this.object3d.add(bottomMesh)\n }\n\n private buildRails(width: number, height: number, depth: number, frameH: number, railW: number) {\n const color = this.resolveColor()\n const railHeight = frameH * 0.3\n const railY = depth - railHeight / 2\n const innerWidth = height - railW * 2\n // 두 레일 간격: 내부 폭의 60%\n const railSpacing = innerWidth * 0.6\n\n const railMaterial = new THREE.MeshStandardMaterial({\n color,\n metalness: 0.7,\n roughness: 0.3,\n })\n\n const railGeometries: THREE.BufferGeometry[] = []\n\n // Two parallel rails running along X axis\n for (const zSign of [-1, 1]) {\n const rail = new THREE.BoxGeometry(width - railW * 2, railHeight, railW * 0.8)\n rail.translate(0, railY, zSign * (railSpacing / 2))\n railGeometries.push(rail)\n }\n\n const railMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(railGeometries), railMaterial)\n railMesh.castShadow = true\n this.object3d.add(railMesh)\n }\n\n updateDimension() {}\n updateAlpha() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'conveyorType' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after ||\n 'fillStyle' in after ||\n 'strokeStyle' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('Conveyor', (component: Component) => new Conveyor3D(component))\n"]}
1
+ {"version":3,"file":"conveyor-3d.js","sourceRoot":"","sources":["../../src/factories/conveyor-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Conveyor 3D Factory\n *\n * 'conveyor' type (default): side rails + cylindrical rollers + 4 legs + cross-bracing\n * 'belt' type: side rails + two end drums + belt wrapping + 4 legs + cross-bracing\n * 'rail' type: side rails + two parallel rails + 4 legs + cross-bracing\n *\n * Based on operato-scene conveyor-3d.ts, adapted for fmsim:\n * - conveyorType is string ('', 'conveyor', 'belt', 'rail') instead of numeric\n * - statusColor from MCSStatusMixin instead of value-based lookup\n * - rollWidth auto-calculated: Math.max(10, Math.min(width, height) / 2)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup, registerRealObjectFactory } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\nimport { Z_FMSIM } from './z-priority'\n\nconst FRAME_COLOR = 0x888899\nconst ROLLER_COLOR = 0xaaaabc\nconst DEFAULT_COLOR = 0x999999\n\nexport class Conveyor3D extends RealObjectGroup {\n get zPriority(): number { return Z_FMSIM.CONVEYOR }\n\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n // 1) MCSStatusMixin.statusColor (legend 기반 상태 색상)\n // '#F0F0F0'은 상태 미설정 시 기본 fallback — 무시하고 defaultColor 사용\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0' && sc !== 'transparent') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n // 2) state.fillStyle fallback\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string' && fillStyle !== 'transparent') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_COLOR\n }\n\n build() {\n super.build()\n\n const { width: rawWidth, height: rawHeight, conveyorType, orientation } = this.component.state\n const depth = this.effectiveDepth\n if (!rawWidth || !rawHeight) return\n\n // orientation: 'horizontal' = 롤러가 Z축(세로), 'vertical' = 롤러가 X축(가로)\n // default = 긴 방향에 수직 (긴 축을 따라 이송)\n const isVertical = orientation === 'vertical' ? true\n : orientation === 'horizontal' ? false\n : rawWidth < rawHeight // default: 세로가 길면 롤러는 가로(X축)\n // isVertical=true → 롤러 축이 X(가로), 이송 방향이 Z(세로)\n // isVertical=false → 롤러 축이 Z(세로), 이송 방향이 X(가로)\n const width = rawWidth\n const height = rawHeight\n\n // 2D와 동일 로직으로 rollWidth 산출\n const rollWidth = Math.max(10, Math.min(width, height) / 2)\n const rollerDiameter = Math.max(rollWidth, 2)\n const frameH = Math.min(rollerDiameter, depth)\n const legH = Math.max(depth - frameH, 0)\n\n // isVertical일 때 이송 방향이 Z축: 레일은 Z축을 따라, 벨트폭은 width\n // !isVertical일 때 이송 방향이 X축: 레일은 X축을 따라, 벨트폭은 height\n const beltWidth = isVertical ? width : height\n const railLen = isVertical ? height : width\n const railW = Math.max(beltWidth * 0.06, 2)\n const legThickness = Math.max(railW * 0.8, 2)\n\n const frameMaterial = new THREE.MeshStandardMaterial({\n color: FRAME_COLOR,\n metalness: 0.85,\n roughness: 0.35,\n })\n\n // --- Frame: side rails + legs + cross-bracing ---\n // Y 좌표: y=0(바닥) ~ y=depth(상단). 레일 상단이 depth에 위치.\n const frameGeometries: THREE.BufferGeometry[] = []\n const railY = depth - frameH / 2\n\n // Side rails — 이송 방향을 따라, 벨트 양측에 배치\n if (isVertical) {\n // 레일이 Z축(세로)을 따라, ±X에 배치\n for (const xSign of [-1, 1]) {\n const rail = new THREE.BoxGeometry(railW, frameH, height)\n rail.translate(xSign * (width / 2 - railW / 2), railY, 0)\n frameGeometries.push(rail)\n }\n } else {\n // 레일이 X축(가로)을 따라, ±Z에 배치\n for (const zSign of [-1, 1]) {\n const rail = new THREE.BoxGeometry(width, frameH, railW)\n rail.translate(0, railY, zSign * (height / 2 - railW / 2))\n frameGeometries.push(rail)\n }\n }\n\n // 4 legs at corners\n if (legH > 0) {\n const legTopY = depth - frameH\n const legCenterY = legTopY - legH / 2\n\n // 다리 위치: 레일 안쪽에 정렬\n const legXPos = isVertical ? (width / 2 - railW / 2) : (width / 2 - legThickness / 2)\n const legZPos = isVertical ? (height / 2 - legThickness / 2) : (height / 2 - railW / 2)\n\n for (const xSign of [-1, 1]) {\n for (const zSign of [-1, 1]) {\n const leg = new THREE.BoxGeometry(legThickness, legH, legThickness)\n leg.translate(xSign * legXPos, legCenterY, zSign * legZPos)\n frameGeometries.push(leg)\n }\n }\n\n // Cross-bracing\n const braceH = legThickness * 0.6\n const braceW = legThickness * 0.6\n const braceY = legTopY - legH * 0.35\n\n // 이송 방향 브레이스 (레일과 평행)\n if (isVertical) {\n for (const xSign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(braceW, braceH, height - legThickness)\n brace.translate(xSign * legXPos, braceY, 0)\n frameGeometries.push(brace)\n }\n } else {\n for (const zSign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(width - legThickness, braceH, braceW)\n brace.translate(0, braceY, zSign * legZPos)\n frameGeometries.push(brace)\n }\n }\n\n // 벨트폭 방향 브레이스 (레일과 수직)\n if (isVertical) {\n for (const zSign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(width - railW, braceH, braceW)\n brace.translate(0, braceY, zSign * legZPos)\n frameGeometries.push(brace)\n }\n } else {\n for (const xSign of [-1, 1]) {\n const brace = new THREE.BoxGeometry(braceW, braceH, height - railW)\n brace.translate(xSign * legXPos, braceY, 0)\n frameGeometries.push(brace)\n }\n }\n }\n\n const frameMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(frameGeometries), frameMaterial)\n frameMesh.castShadow = true\n frameMesh.receiveShadow = true\n this.components3D.add(frameMesh)\n\n // --- Rollers, Belt, or Rails ---\n if (conveyorType === 'rail') {\n this.buildRails(width, height, depth, frameH, railW, isVertical)\n } else if (conveyorType === 'belt') {\n this.buildBelt(width, height, depth, frameH, railW, isVertical)\n } else {\n this.buildRollers(width, height, depth, frameH, railW, rollWidth, isVertical)\n }\n }\n\n private buildRollers(\n width: number,\n height: number,\n depth: number,\n frameH: number,\n railW: number,\n rollWidth: number,\n isVertical: boolean = false\n ) {\n const rollerRadius = Math.max(rollWidth / 8, 1)\n const diameter = rollerRadius * 2\n const step = diameter * 1.05\n const rollerY = depth - rollerRadius\n const color = this.resolveColor()\n\n const rollerGeometries: THREE.BufferGeometry[] = []\n\n if (isVertical) {\n // 롤러 축 = X축(가로), 이송 방향 = Z축(세로)\n const rollerLength = width - railW * 2 - 0.5\n const count = Math.max(1, Math.floor(height / step))\n const totalSpan = (count - 1) * step\n const startZ = -totalSpan / 2\n\n for (let i = 0; i < count; i++) {\n const z = startZ + i * step\n const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16)\n roller.rotateZ(Math.PI / 2)\n roller.translate(0, rollerY, z)\n rollerGeometries.push(roller)\n }\n } else {\n // 롤러 축 = Z축(세로), 이송 방향 = X축(가로) — 기존 동작\n const rollerLength = height - railW * 2 - 0.5\n const count = Math.max(1, Math.floor(width / step))\n const totalSpan = (count - 1) * step\n const startX = -totalSpan / 2\n\n for (let i = 0; i < count; i++) {\n const x = startX + i * step\n const roller = new THREE.CylinderGeometry(rollerRadius, rollerRadius, rollerLength, 16)\n roller.rotateX(Math.PI / 2)\n roller.translate(x, rollerY, 0)\n rollerGeometries.push(roller)\n }\n }\n\n if (rollerGeometries.length > 0) {\n const rollerMesh = new THREE.Mesh(\n BufferGeometryUtils.mergeGeometries(rollerGeometries),\n new THREE.MeshStandardMaterial({ color, metalness: 0.9, roughness: 0.2 })\n )\n rollerMesh.castShadow = true\n this.components3D.add(rollerMesh)\n }\n }\n\n private buildBelt(width: number, height: number, depth: number, frameH: number, railW: number, isVertical: boolean = false) {\n const drumRadius = frameH * 0.38\n const beltY = depth - drumRadius\n const color = this.resolveColor()\n\n // 이송 방향 길이와 벨트폭\n const transportLen = isVertical ? height : width\n const beltWidth = isVertical ? width : height\n const drumLength = beltWidth - railW * 2\n\n // Two end drums\n const drumGeometries: THREE.BufferGeometry[] = []\n const drumInset = drumRadius * 1.5\n const drum1 = -transportLen / 2 + drumInset\n const drum2 = transportLen / 2 - drumInset\n\n for (const dp of [drum1, drum2]) {\n const drum = new THREE.CylinderGeometry(drumRadius, drumRadius, drumLength, 16)\n if (isVertical) {\n drum.rotateZ(Math.PI / 2)\n drum.translate(0, beltY, dp)\n } else {\n drum.rotateX(Math.PI / 2)\n drum.translate(dp, beltY, 0)\n }\n drumGeometries.push(drum)\n }\n\n const drumMesh = new THREE.Mesh(\n BufferGeometryUtils.mergeGeometries(drumGeometries),\n new THREE.MeshStandardMaterial({ color: ROLLER_COLOR, metalness: 0.9, roughness: 0.2 })\n )\n drumMesh.castShadow = true\n this.components3D.add(drumMesh)\n\n // Belt surface\n const beltThickness = drumRadius * 0.12\n const beltMaterial = new THREE.MeshStandardMaterial({\n color: color,\n metalness: 0.0,\n roughness: 0.9,\n })\n\n // Flat belt top surface spanning between drums\n const flatLen = drum2 - drum1\n const flatGeo = isVertical\n ? new THREE.BoxGeometry(drumLength, beltThickness, flatLen)\n : new THREE.BoxGeometry(flatLen, beltThickness, drumLength)\n const flatMesh = new THREE.Mesh(flatGeo, beltMaterial)\n flatMesh.position.set(0, beltY + drumRadius, 0)\n flatMesh.castShadow = true\n this.components3D.add(flatMesh)\n\n // Belt wrap around drums (segmented half-cylinder)\n const wrapSegments = 12\n for (const [dp, angleStart] of [\n [drum1, Math.PI / 2],\n [drum2, -Math.PI / 2],\n ] as [number, number][]) {\n const wrapGeometries: THREE.BufferGeometry[] = []\n for (let i = 0; i < wrapSegments; i++) {\n const a0 = angleStart + (Math.PI * i) / wrapSegments\n const a1 = angleStart + (Math.PI * (i + 1)) / wrapSegments\n const r = drumRadius + beltThickness / 2\n\n const p0 = dp + Math.cos(a0) * r\n const y0 = beltY + Math.sin(a0) * r\n const p1 = dp + Math.cos(a1) * r\n const y1 = beltY + Math.sin(a1) * r\n\n const segLen = Math.sqrt((p1 - p0) ** 2 + (y1 - y0) ** 2) * 1.05\n const segAngle = Math.atan2(y1 - y0, p1 - p0)\n\n if (isVertical) {\n const seg = new THREE.BoxGeometry(drumLength, beltThickness, segLen)\n seg.rotateX(-segAngle)\n seg.translate(0, (y0 + y1) / 2, (p0 + p1) / 2)\n wrapGeometries.push(seg)\n } else {\n const seg = new THREE.BoxGeometry(segLen, beltThickness, drumLength)\n seg.rotateZ(segAngle)\n seg.translate((p0 + p1) / 2, (y0 + y1) / 2, 0)\n wrapGeometries.push(seg)\n }\n }\n const wrapMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(wrapGeometries), beltMaterial)\n wrapMesh.castShadow = true\n this.components3D.add(wrapMesh)\n }\n\n // Flat belt bottom (return path)\n const bottomGeo = isVertical\n ? new THREE.BoxGeometry(drumLength * 0.85, beltThickness, flatLen)\n : new THREE.BoxGeometry(flatLen, beltThickness, drumLength * 0.85)\n const bottomMesh = new THREE.Mesh(bottomGeo, beltMaterial)\n bottomMesh.position.set(0, beltY - drumRadius, 0)\n this.components3D.add(bottomMesh)\n }\n\n private buildRails(width: number, height: number, depth: number, frameH: number, railW: number, isVertical: boolean = false) {\n const color = this.resolveColor()\n const railHeight = frameH * 0.3\n const railY = depth - railHeight / 2\n const beltWidth = isVertical ? width : height\n const transportLen = isVertical ? height : width\n const innerWidth = beltWidth - railW * 2\n // 두 레일 간격: 내부 폭의 60%\n const railSpacing = innerWidth * 0.6\n\n const railMaterial = new THREE.MeshStandardMaterial({\n color,\n metalness: 0.7,\n roughness: 0.3,\n })\n\n const railGeometries: THREE.BufferGeometry[] = []\n\n // Two parallel rails running along transport direction\n if (isVertical) {\n for (const xSign of [-1, 1]) {\n const rail = new THREE.BoxGeometry(railW * 0.8, railHeight, transportLen - railW * 2)\n rail.translate(xSign * (railSpacing / 2), railY, 0)\n railGeometries.push(rail)\n }\n } else {\n for (const zSign of [-1, 1]) {\n const rail = new THREE.BoxGeometry(transportLen - railW * 2, railHeight, railW * 0.8)\n rail.translate(0, railY, zSign * (railSpacing / 2))\n railGeometries.push(rail)\n }\n }\n\n const railMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(railGeometries), railMaterial)\n railMesh.castShadow = true\n this.components3D.add(railMesh)\n }\n\n updateDimension() {}\n updateAlpha() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'conveyorType' in after ||\n 'orientation' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after ||\n 'fillStyle' in after ||\n 'strokeStyle' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('Conveyor', (component: Component) => new Conveyor3D(component))\n"]}