@fmsim/machine 2.0.0-beta.24 → 2.0.0-beta.26

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.
@@ -97,8 +97,14 @@ let ConveyorJoin = class ConveyorJoin extends ConveyorMixin(MCSTransport) {
97
97
  const ratioy = (y - cy) / ((ry / 100) * ratio * 2 - 0.5);
98
98
  if (normx * normx + normy * normy < 0.25 && ratiox * ratiox + ratioy * ratioy > 0.25) {
99
99
  const angle = normalizeAngle(Math.atan2(-normy, normx) - Math.PI / 2);
100
- if (angle >= startAngle && angle <= endAngle) {
101
- return true;
100
+ if (startAngle <= endAngle) {
101
+ if (angle >= startAngle && angle <= endAngle)
102
+ return true;
103
+ }
104
+ else {
105
+ // wraparound: startAngle > endAngle (예: π → 0)
106
+ if (angle >= startAngle || angle <= endAngle)
107
+ return true;
102
108
  }
103
109
  }
104
110
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"conveyor-join.js","sourceRoot":"","sources":["../src/conveyor-join.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ComponentNature, Control, POSITION, sceneComponent } from '@hatiolab/things-scene'\nimport MCSTransport from './mcs-transport'\nimport { ConveyorMixin } from './features/conveyor-mixin'\nimport { normalizeAngle } from './utils/normalize-angle'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n ...MCSTransport.properties,\n {\n type: 'angle',\n label: 'start-angle',\n name: 'startAngle'\n },\n {\n type: 'angle',\n label: 'end-angle',\n name: 'endAngle'\n },\n {\n type: 'number',\n label: 'ratio',\n name: 'ratio'\n },\n {\n type: 'number',\n label: 'roll-width',\n name: 'rollWidth'\n }\n ]\n}\n\nvar controlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { cx, cy, rx, ry, startAngle, endAngle } = component.model\n\n var { x, y } = component.transcoordP2S(point.x, point.y)\n\n const angle = (startAngle + endAngle) / 2\n\n /* 원점으로부터 최대 거리 */\n const dx = Math.abs(rx * Math.sin(angle))\n const dy = Math.abs(ry * Math.cos(angle))\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n /* 원점으로부터 현재 컨트롤 위치까지의 거리 */\n const px = Math.abs(cx - x)\n const py = Math.abs(cy - y)\n const pdistance = Math.sqrt(px * px + py * py)\n\n var ratio = (pdistance / distance) * 100\n\n ratio = ratio >= 100 || ratio <= -100 ? 100 : Math.abs(ratio)\n\n component.set({ ratio })\n }\n}\n\nvar antiClockWiseControlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { cx, cy } = component.model\n\n var transcoorded = component.transcoordP2S(point.x, point.y)\n\n var theta = Math.atan2(-(transcoorded.y - cy), transcoorded.x - cx)\n\n if (theta > 0 && theta <= Math.PI / 2) theta = Math.PI / 2\n if (theta < 0 && theta >= -Math.PI / 2) theta = -Math.PI / 2\n\n const startAngle = (-theta + Math.PI / 2 - Math.PI * 2) % (Math.PI * 2)\n\n component.set({ startAngle })\n }\n}\n\nvar clockwiseControlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { cx, cy } = component.model\n\n var transcoorded = component.transcoordP2S(point.x, point.y)\n\n var theta = Math.atan2(-(transcoorded.y - cy), transcoorded.x - cx)\n\n if (theta > 0) if (theta >= Math.PI / 2) theta = Math.PI / 2\n if (theta < 0) if (theta <= -Math.PI / 2) theta = -Math.PI / 2\n\n var endAngle = -theta + Math.PI / 2\n\n component.set({ endAngle })\n }\n}\n\n@sceneComponent('ConveyorJoin')\nexport default class ConveyorJoin extends ConveyorMixin(MCSTransport) {\n get nature() {\n return NATURE\n }\n\n getLegendFallback() {\n return (this.root as any).conveyorLegendTheme || super.getLegendFallback()\n }\n\n containable(component: Component) {\n return ['Shuttle', 'Port'].includes(component.state.type)\n }\n\n contains(x: number, y: number) {\n var { cx, cy, rx, ry, ratio, startAngle, endAngle } = this.state\n rx = Math.abs(rx)\n ry = Math.abs(ry)\n\n const normx = (x - cx) / (rx * 2 - 0.5)\n const normy = (y - cy) / (ry * 2 - 0.5)\n const ratiox = (x - cx) / ((rx / 100) * ratio * 2 - 0.5)\n const ratioy = (y - cy) / ((ry / 100) * ratio * 2 - 0.5)\n\n if (normx * normx + normy * normy < 0.25 && ratiox * ratiox + ratioy * ratioy > 0.25) {\n const angle = normalizeAngle(Math.atan2(-normy, normx) - Math.PI / 2)\n\n if (angle >= startAngle && angle <= endAngle) {\n return true\n }\n }\n\n return false\n }\n\n render(context: CanvasRenderingContext2D) {\n var { ratio = 50, cx, cy, rx, ry, startAngle = 0, endAngle = Math.PI / 2 } = this.state\n\n context.beginPath()\n\n startAngle -= Math.PI / 2\n endAngle -= Math.PI / 2\n\n /* outer ellipse and inner ellipse */\n context.ellipse(cx, cy, Math.abs(rx), Math.abs(ry), 0, startAngle, endAngle)\n context.ellipse(cx, cy, Math.abs((rx / 100) * ratio), Math.abs((ry / 100) * ratio), 0, endAngle, startAngle, true)\n\n context.lineTo(rx * Math.cos(startAngle) + cx, ry * Math.sin(startAngle) + cy)\n\n this.drawFill(context)\n this.drawStroke(context)\n }\n\n get path() {\n var { cx, cy, rx, ry } = this.state\n\n return [\n {\n x: cx - rx,\n y: cy - ry\n },\n {\n x: cx + rx,\n y: cy - ry\n },\n {\n x: cx + rx,\n y: cy + ry\n },\n {\n x: cx - rx,\n y: cy + ry\n }\n ]\n }\n\n set path(path) {\n var left_top = path[0]\n var right_bottom = path[2]\n\n this.set({\n cx: left_top.x + (right_bottom.x - left_top.x) / 2,\n cy: left_top.y + (right_bottom.y - left_top.y) / 2,\n rx: (right_bottom.x - left_top.x) / 2,\n ry: (right_bottom.y - left_top.y) / 2\n })\n }\n\n get controls(): Control[] {\n var { cx, cy, rx, ry, ratio, startAngle, endAngle } = this.state\n\n var controls = [] as Control[]\n\n controls.push({\n x: cx + ((rx + (rx * ratio) / 100) / 2) * Math.sin(startAngle),\n y: cy - ((ry + (ry * ratio) / 100) / 2) * Math.cos(startAngle),\n handler: antiClockWiseControlHandler\n })\n\n controls.push({\n x: cx + ((rx + (rx * ratio) / 100) / 2) * Math.sin(endAngle),\n y: cy - ((ry + (ry * ratio) / 100) / 2) * Math.cos(endAngle),\n handler: clockwiseControlHandler\n })\n\n const angle = (startAngle + endAngle) / 2\n controls.push({\n x: cx + ((rx * ratio) / 100) * Math.sin(angle),\n y: cy - ((ry * ratio) / 100) * Math.cos(angle),\n handler: controlHandler\n })\n\n return controls\n }\n}\n\nComponent.memoize(ConveyorJoin.prototype, 'controls', false)\n"]}
1
+ {"version":3,"file":"conveyor-join.js","sourceRoot":"","sources":["../src/conveyor-join.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ComponentNature, Control, POSITION, sceneComponent } from '@hatiolab/things-scene'\nimport MCSTransport from './mcs-transport'\nimport { ConveyorMixin } from './features/conveyor-mixin'\nimport { normalizeAngle } from './utils/normalize-angle'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n ...MCSTransport.properties,\n {\n type: 'angle',\n label: 'start-angle',\n name: 'startAngle'\n },\n {\n type: 'angle',\n label: 'end-angle',\n name: 'endAngle'\n },\n {\n type: 'number',\n label: 'ratio',\n name: 'ratio'\n },\n {\n type: 'number',\n label: 'roll-width',\n name: 'rollWidth'\n }\n ]\n}\n\nvar controlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { cx, cy, rx, ry, startAngle, endAngle } = component.model\n\n var { x, y } = component.transcoordP2S(point.x, point.y)\n\n const angle = (startAngle + endAngle) / 2\n\n /* 원점으로부터 최대 거리 */\n const dx = Math.abs(rx * Math.sin(angle))\n const dy = Math.abs(ry * Math.cos(angle))\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n /* 원점으로부터 현재 컨트롤 위치까지의 거리 */\n const px = Math.abs(cx - x)\n const py = Math.abs(cy - y)\n const pdistance = Math.sqrt(px * px + py * py)\n\n var ratio = (pdistance / distance) * 100\n\n ratio = ratio >= 100 || ratio <= -100 ? 100 : Math.abs(ratio)\n\n component.set({ ratio })\n }\n}\n\nvar antiClockWiseControlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { cx, cy } = component.model\n\n var transcoorded = component.transcoordP2S(point.x, point.y)\n\n var theta = Math.atan2(-(transcoorded.y - cy), transcoorded.x - cx)\n\n if (theta > 0 && theta <= Math.PI / 2) theta = Math.PI / 2\n if (theta < 0 && theta >= -Math.PI / 2) theta = -Math.PI / 2\n\n const startAngle = (-theta + Math.PI / 2 - Math.PI * 2) % (Math.PI * 2)\n\n component.set({ startAngle })\n }\n}\n\nvar clockwiseControlHandler = {\n ondragmove: function (point: POSITION, index: number, component: Component) {\n var { cx, cy } = component.model\n\n var transcoorded = component.transcoordP2S(point.x, point.y)\n\n var theta = Math.atan2(-(transcoorded.y - cy), transcoorded.x - cx)\n\n if (theta > 0) if (theta >= Math.PI / 2) theta = Math.PI / 2\n if (theta < 0) if (theta <= -Math.PI / 2) theta = -Math.PI / 2\n\n var endAngle = -theta + Math.PI / 2\n\n component.set({ endAngle })\n }\n}\n\n@sceneComponent('ConveyorJoin')\nexport default class ConveyorJoin extends ConveyorMixin(MCSTransport) {\n get nature() {\n return NATURE\n }\n\n getLegendFallback() {\n return (this.root as any).conveyorLegendTheme || super.getLegendFallback()\n }\n\n containable(component: Component) {\n return ['Shuttle', 'Port'].includes(component.state.type)\n }\n\n contains(x: number, y: number) {\n var { cx, cy, rx, ry, ratio, startAngle, endAngle } = this.state\n rx = Math.abs(rx)\n ry = Math.abs(ry)\n\n const normx = (x - cx) / (rx * 2 - 0.5)\n const normy = (y - cy) / (ry * 2 - 0.5)\n const ratiox = (x - cx) / ((rx / 100) * ratio * 2 - 0.5)\n const ratioy = (y - cy) / ((ry / 100) * ratio * 2 - 0.5)\n\n if (normx * normx + normy * normy < 0.25 && ratiox * ratiox + ratioy * ratioy > 0.25) {\n const angle = normalizeAngle(Math.atan2(-normy, normx) - Math.PI / 2)\n\n if (startAngle <= endAngle) {\n if (angle >= startAngle && angle <= endAngle) return true\n } else {\n // wraparound: startAngle > endAngle (예: π → 0)\n if (angle >= startAngle || angle <= endAngle) return true\n }\n }\n\n return false\n }\n\n render(context: CanvasRenderingContext2D) {\n var { ratio = 50, cx, cy, rx, ry, startAngle = 0, endAngle = Math.PI / 2 } = this.state\n\n context.beginPath()\n\n startAngle -= Math.PI / 2\n endAngle -= Math.PI / 2\n\n /* outer ellipse and inner ellipse */\n context.ellipse(cx, cy, Math.abs(rx), Math.abs(ry), 0, startAngle, endAngle)\n context.ellipse(cx, cy, Math.abs((rx / 100) * ratio), Math.abs((ry / 100) * ratio), 0, endAngle, startAngle, true)\n\n context.lineTo(rx * Math.cos(startAngle) + cx, ry * Math.sin(startAngle) + cy)\n\n this.drawFill(context)\n this.drawStroke(context)\n }\n\n get path() {\n var { cx, cy, rx, ry } = this.state\n\n return [\n {\n x: cx - rx,\n y: cy - ry\n },\n {\n x: cx + rx,\n y: cy - ry\n },\n {\n x: cx + rx,\n y: cy + ry\n },\n {\n x: cx - rx,\n y: cy + ry\n }\n ]\n }\n\n set path(path) {\n var left_top = path[0]\n var right_bottom = path[2]\n\n this.set({\n cx: left_top.x + (right_bottom.x - left_top.x) / 2,\n cy: left_top.y + (right_bottom.y - left_top.y) / 2,\n rx: (right_bottom.x - left_top.x) / 2,\n ry: (right_bottom.y - left_top.y) / 2\n })\n }\n\n get controls(): Control[] {\n var { cx, cy, rx, ry, ratio, startAngle, endAngle } = this.state\n\n var controls = [] as Control[]\n\n controls.push({\n x: cx + ((rx + (rx * ratio) / 100) / 2) * Math.sin(startAngle),\n y: cy - ((ry + (ry * ratio) / 100) / 2) * Math.cos(startAngle),\n handler: antiClockWiseControlHandler\n })\n\n controls.push({\n x: cx + ((rx + (rx * ratio) / 100) / 2) * Math.sin(endAngle),\n y: cy - ((ry + (ry * ratio) / 100) / 2) * Math.cos(endAngle),\n handler: clockwiseControlHandler\n })\n\n const angle = (startAngle + endAngle) / 2\n controls.push({\n x: cx + ((rx * ratio) / 100) * Math.sin(angle),\n y: cy - ((ry * ratio) / 100) * Math.cos(angle),\n handler: controlHandler\n })\n\n return controls\n }\n}\n\nComponent.memoize(ConveyorJoin.prototype, 'controls', false)\n"]}
@@ -71,7 +71,11 @@ export class Conveyor3D extends RealObjectGroup {
71
71
  const rollerDiameter = Math.max(rollWidth, 2);
72
72
  const frameH = Math.min(rollerDiameter, depth);
73
73
  const legH = Math.max(depth - frameH, 0);
74
- const railW = Math.max(height * 0.06, 2);
74
+ // isVertical일 때 이송 방향이 Z축: 레일은 Z축을 따라, 벨트폭은 width
75
+ // !isVertical일 때 이송 방향이 X축: 레일은 X축을 따라, 벨트폭은 height
76
+ const beltWidth = isVertical ? width : height;
77
+ const railLen = isVertical ? height : width;
78
+ const railW = Math.max(beltWidth * 0.06, 2);
75
79
  const legThickness = Math.max(railW * 0.8, 2);
76
80
  const frameMaterial = new THREE.MeshStandardMaterial({
77
81
  color: FRAME_COLOR,
@@ -82,41 +86,70 @@ export class Conveyor3D extends RealObjectGroup {
82
86
  // Y 좌표: y=0(바닥) ~ y=depth(상단). 레일 상단이 depth에 위치.
83
87
  const frameGeometries = [];
84
88
  const railY = depth - frameH / 2;
85
- // Side rails (along X)
86
- for (const zSign of [-1, 1]) {
87
- const rail = new THREE.BoxGeometry(width, frameH, railW);
88
- rail.translate(0, railY, zSign * (height / 2 - railW / 2));
89
- frameGeometries.push(rail);
89
+ // Side rails — 이송 방향을 따라, 벨트 양측에 배치
90
+ if (isVertical) {
91
+ // 레일이 Z축(세로)을 따라, ±X에 배치
92
+ for (const xSign of [-1, 1]) {
93
+ const rail = new THREE.BoxGeometry(railW, frameH, height);
94
+ rail.translate(xSign * (width / 2 - railW / 2), railY, 0);
95
+ frameGeometries.push(rail);
96
+ }
97
+ }
98
+ else {
99
+ // 레일이 X축(가로)을 따라, ±Z에 배치
100
+ for (const zSign of [-1, 1]) {
101
+ const rail = new THREE.BoxGeometry(width, frameH, railW);
102
+ rail.translate(0, railY, zSign * (height / 2 - railW / 2));
103
+ frameGeometries.push(rail);
104
+ }
90
105
  }
91
106
  // 4 legs at corners
92
107
  if (legH > 0) {
93
108
  const legTopY = depth - frameH;
94
109
  const legCenterY = legTopY - legH / 2;
110
+ // 다리 위치: 레일 안쪽에 정렬
111
+ const legXPos = isVertical ? (width / 2 - railW / 2) : (width / 2 - legThickness / 2);
112
+ const legZPos = isVertical ? (height / 2 - legThickness / 2) : (height / 2 - railW / 2);
95
113
  for (const xSign of [-1, 1]) {
96
114
  for (const zSign of [-1, 1]) {
97
- const lx = xSign * (width / 2 - legThickness / 2);
98
- const lz = zSign * (height / 2 - railW / 2);
99
115
  const leg = new THREE.BoxGeometry(legThickness, legH, legThickness);
100
- leg.translate(lx, legCenterY, lz);
116
+ leg.translate(xSign * legXPos, legCenterY, zSign * legZPos);
101
117
  frameGeometries.push(leg);
102
118
  }
103
119
  }
104
- // Cross-bracing: 앞뒤 다리 연결 (Z 방향)
120
+ // Cross-bracing
105
121
  const braceH = legThickness * 0.6;
106
122
  const braceW = legThickness * 0.6;
107
- const braceLen = height - railW;
108
123
  const braceY = legTopY - legH * 0.35;
109
- for (const xSign of [-1, 1]) {
110
- const brace = new THREE.BoxGeometry(braceW, braceH, braceLen);
111
- brace.translate(xSign * (width / 2 - legThickness / 2), braceY, 0);
112
- frameGeometries.push(brace);
124
+ // 이송 방향 브레이스 (레일과 평행)
125
+ if (isVertical) {
126
+ for (const xSign of [-1, 1]) {
127
+ const brace = new THREE.BoxGeometry(braceW, braceH, height - legThickness);
128
+ brace.translate(xSign * legXPos, braceY, 0);
129
+ frameGeometries.push(brace);
130
+ }
113
131
  }
114
- // Cross-bracing: 좌우 다리 연결 (X 방향)
115
- const braceLenX = width - legThickness;
116
- for (const zSign of [-1, 1]) {
117
- const brace = new THREE.BoxGeometry(braceLenX, braceH, braceW);
118
- brace.translate(0, braceY, zSign * (height / 2 - railW / 2));
119
- frameGeometries.push(brace);
132
+ else {
133
+ for (const zSign of [-1, 1]) {
134
+ const brace = new THREE.BoxGeometry(width - legThickness, braceH, braceW);
135
+ brace.translate(0, braceY, zSign * legZPos);
136
+ frameGeometries.push(brace);
137
+ }
138
+ }
139
+ // 벨트폭 방향 브레이스 (레일과 수직)
140
+ if (isVertical) {
141
+ for (const zSign of [-1, 1]) {
142
+ const brace = new THREE.BoxGeometry(width - railW, braceH, braceW);
143
+ brace.translate(0, braceY, zSign * legZPos);
144
+ frameGeometries.push(brace);
145
+ }
146
+ }
147
+ else {
148
+ for (const xSign of [-1, 1]) {
149
+ const brace = new THREE.BoxGeometry(braceW, braceH, height - railW);
150
+ brace.translate(xSign * legXPos, braceY, 0);
151
+ frameGeometries.push(brace);
152
+ }
120
153
  }
121
154
  }
122
155
  const frameMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(frameGeometries), frameMaterial);
@@ -125,10 +158,10 @@ export class Conveyor3D extends RealObjectGroup {
125
158
  this.object3d.add(frameMesh);
126
159
  // --- Rollers, Belt, or Rails ---
127
160
  if (conveyorType === 'rail') {
128
- this.buildRails(width, height, depth, frameH, railW);
161
+ this.buildRails(width, height, depth, frameH, railW, isVertical);
129
162
  }
130
163
  else if (conveyorType === 'belt') {
131
- this.buildBelt(width, height, depth, frameH, railW);
164
+ this.buildBelt(width, height, depth, frameH, railW, isVertical);
132
165
  }
133
166
  else {
134
167
  this.buildRollers(width, height, depth, frameH, railW, rollWidth, isVertical);
@@ -175,20 +208,29 @@ export class Conveyor3D extends RealObjectGroup {
175
208
  this.object3d.add(rollerMesh);
176
209
  }
177
210
  }
178
- buildBelt(width, height, depth, frameH, railW) {
211
+ buildBelt(width, height, depth, frameH, railW, isVertical = false) {
179
212
  const drumRadius = frameH * 0.38;
180
- const drumLength = height - railW * 2;
181
213
  const beltY = depth - drumRadius;
182
214
  const color = this.resolveColor();
215
+ // 이송 방향 길이와 벨트폭
216
+ const transportLen = isVertical ? height : width;
217
+ const beltWidth = isVertical ? width : height;
218
+ const drumLength = beltWidth - railW * 2;
183
219
  // Two end drums
184
220
  const drumGeometries = [];
185
221
  const drumInset = drumRadius * 1.5;
186
- const leftDrumX = -width / 2 + drumInset;
187
- const rightDrumX = width / 2 - drumInset;
188
- for (const dx of [leftDrumX, rightDrumX]) {
222
+ const drum1 = -transportLen / 2 + drumInset;
223
+ const drum2 = transportLen / 2 - drumInset;
224
+ for (const dp of [drum1, drum2]) {
189
225
  const drum = new THREE.CylinderGeometry(drumRadius, drumRadius, drumLength, 16);
190
- drum.rotateX(Math.PI / 2);
191
- drum.translate(dx, beltY, 0);
226
+ if (isVertical) {
227
+ drum.rotateZ(Math.PI / 2);
228
+ drum.translate(0, beltY, dp);
229
+ }
230
+ else {
231
+ drum.rotateX(Math.PI / 2);
232
+ drum.translate(dp, beltY, 0);
233
+ }
192
234
  drumGeometries.push(drum);
193
235
  }
194
236
  const drumMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(drumGeometries), new THREE.MeshStandardMaterial({ color: ROLLER_COLOR, metalness: 0.9, roughness: 0.2 }));
@@ -202,49 +244,63 @@ export class Conveyor3D extends RealObjectGroup {
202
244
  roughness: 0.9,
203
245
  });
204
246
  // Flat belt top surface spanning between drums
205
- const flatLen = rightDrumX - leftDrumX;
206
- const flatGeo = new THREE.BoxGeometry(flatLen, beltThickness, drumLength);
247
+ const flatLen = drum2 - drum1;
248
+ const flatGeo = isVertical
249
+ ? new THREE.BoxGeometry(drumLength, beltThickness, flatLen)
250
+ : new THREE.BoxGeometry(flatLen, beltThickness, drumLength);
207
251
  const flatMesh = new THREE.Mesh(flatGeo, beltMaterial);
208
252
  flatMesh.position.set(0, beltY + drumRadius, 0);
209
253
  flatMesh.castShadow = true;
210
254
  this.object3d.add(flatMesh);
211
255
  // Belt wrap around drums (segmented half-cylinder)
212
256
  const wrapSegments = 12;
213
- for (const [dx, angleStart] of [
214
- [leftDrumX, Math.PI / 2],
215
- [rightDrumX, -Math.PI / 2],
257
+ for (const [dp, angleStart] of [
258
+ [drum1, Math.PI / 2],
259
+ [drum2, -Math.PI / 2],
216
260
  ]) {
217
261
  const wrapGeometries = [];
218
262
  for (let i = 0; i < wrapSegments; i++) {
219
263
  const a0 = angleStart + (Math.PI * i) / wrapSegments;
220
264
  const a1 = angleStart + (Math.PI * (i + 1)) / wrapSegments;
221
265
  const r = drumRadius + beltThickness / 2;
222
- const x0 = dx + Math.cos(a0) * r;
266
+ const p0 = dp + Math.cos(a0) * r;
223
267
  const y0 = beltY + Math.sin(a0) * r;
224
- const x1 = dx + Math.cos(a1) * r;
268
+ const p1 = dp + Math.cos(a1) * r;
225
269
  const y1 = beltY + Math.sin(a1) * r;
226
- const segLen = Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2) * 1.05;
227
- const segAngle = Math.atan2(y1 - y0, x1 - x0);
228
- const seg = new THREE.BoxGeometry(segLen, beltThickness, drumLength);
229
- seg.rotateZ(segAngle);
230
- seg.translate((x0 + x1) / 2, (y0 + y1) / 2, 0);
231
- wrapGeometries.push(seg);
270
+ const segLen = Math.sqrt((p1 - p0) ** 2 + (y1 - y0) ** 2) * 1.05;
271
+ const segAngle = Math.atan2(y1 - y0, p1 - p0);
272
+ if (isVertical) {
273
+ const seg = new THREE.BoxGeometry(drumLength, beltThickness, segLen);
274
+ seg.rotateX(-segAngle);
275
+ seg.translate(0, (y0 + y1) / 2, (p0 + p1) / 2);
276
+ wrapGeometries.push(seg);
277
+ }
278
+ else {
279
+ const seg = new THREE.BoxGeometry(segLen, beltThickness, drumLength);
280
+ seg.rotateZ(segAngle);
281
+ seg.translate((p0 + p1) / 2, (y0 + y1) / 2, 0);
282
+ wrapGeometries.push(seg);
283
+ }
232
284
  }
233
285
  const wrapMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(wrapGeometries), beltMaterial);
234
286
  wrapMesh.castShadow = true;
235
287
  this.object3d.add(wrapMesh);
236
288
  }
237
289
  // Flat belt bottom (return path)
238
- const bottomGeo = new THREE.BoxGeometry(flatLen, beltThickness, drumLength * 0.85);
290
+ const bottomGeo = isVertical
291
+ ? new THREE.BoxGeometry(drumLength * 0.85, beltThickness, flatLen)
292
+ : new THREE.BoxGeometry(flatLen, beltThickness, drumLength * 0.85);
239
293
  const bottomMesh = new THREE.Mesh(bottomGeo, beltMaterial);
240
294
  bottomMesh.position.set(0, beltY - drumRadius, 0);
241
295
  this.object3d.add(bottomMesh);
242
296
  }
243
- buildRails(width, height, depth, frameH, railW) {
297
+ buildRails(width, height, depth, frameH, railW, isVertical = false) {
244
298
  const color = this.resolveColor();
245
299
  const railHeight = frameH * 0.3;
246
300
  const railY = depth - railHeight / 2;
247
- const innerWidth = height - railW * 2;
301
+ const beltWidth = isVertical ? width : height;
302
+ const transportLen = isVertical ? height : width;
303
+ const innerWidth = beltWidth - railW * 2;
248
304
  // 두 레일 간격: 내부 폭의 60%
249
305
  const railSpacing = innerWidth * 0.6;
250
306
  const railMaterial = new THREE.MeshStandardMaterial({
@@ -253,11 +309,20 @@ export class Conveyor3D extends RealObjectGroup {
253
309
  roughness: 0.3,
254
310
  });
255
311
  const railGeometries = [];
256
- // Two parallel rails running along X axis
257
- for (const zSign of [-1, 1]) {
258
- const rail = new THREE.BoxGeometry(width - railW * 2, railHeight, railW * 0.8);
259
- rail.translate(0, railY, zSign * (railSpacing / 2));
260
- railGeometries.push(rail);
312
+ // Two parallel rails running along transport direction
313
+ if (isVertical) {
314
+ for (const xSign of [-1, 1]) {
315
+ const rail = new THREE.BoxGeometry(railW * 0.8, railHeight, transportLen - railW * 2);
316
+ rail.translate(xSign * (railSpacing / 2), railY, 0);
317
+ railGeometries.push(rail);
318
+ }
319
+ }
320
+ else {
321
+ for (const zSign of [-1, 1]) {
322
+ const rail = new THREE.BoxGeometry(transportLen - railW * 2, railHeight, railW * 0.8);
323
+ rail.translate(0, railY, zSign * (railSpacing / 2));
324
+ railGeometries.push(rail);
325
+ }
261
326
  }
262
327
  const railMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(railGeometries), railMaterial);
263
328
  railMesh.castShadow = true;
@@ -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' && 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 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, 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.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 '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"]}
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' && 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.object3d.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.object3d.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.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 = 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.object3d.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.object3d.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.object3d.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.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 '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"]}