@fmsim/machine 2.0.0-beta.7 → 2.0.0-beta.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agv.js +2 -2
- package/dist/agv.js.map +1 -1
- package/dist/carrier-manager.js +93 -0
- package/dist/carrier-manager.js.map +1 -0
- package/dist/carrier.js +81 -5
- package/dist/carrier.js.map +1 -1
- package/dist/conveyor-join.js +8 -2
- package/dist/conveyor-join.js.map +1 -1
- package/dist/conveyor.js +8 -0
- package/dist/conveyor.js.map +1 -1
- package/dist/crane.js.map +1 -1
- package/dist/factories/agv-3d.js +8 -9
- package/dist/factories/agv-3d.js.map +1 -1
- package/dist/factories/conveyor-3d.js +166 -81
- package/dist/factories/conveyor-3d.js.map +1 -1
- package/dist/factories/crane-3d.js +5 -7
- package/dist/factories/crane-3d.js.map +1 -1
- package/dist/factories/crane-rail-3d.js +4 -6
- package/dist/factories/crane-rail-3d.js.map +1 -1
- package/dist/factories/machine-3d.js +384 -133
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/factories/oht-3d.js +20 -20
- package/dist/factories/oht-3d.js.map +1 -1
- package/dist/factories/shuttle-3d.js +2 -5
- package/dist/factories/shuttle-3d.js.map +1 -1
- package/dist/features/mcs-status-mixin.js +22 -0
- package/dist/features/mcs-status-mixin.js.map +1 -1
- package/dist/index.js +50 -34
- package/dist/index.js.map +1 -1
- package/dist/mcs-carrier-holder.js +39 -22
- package/dist/mcs-carrier-holder.js.map +1 -1
- package/dist/mcs-machine.js +28 -0
- package/dist/mcs-machine.js.map +1 -1
- package/dist/mcs-unit.js +10 -0
- package/dist/mcs-unit.js.map +1 -1
- package/dist/mcs-vehicle.js +8 -3
- package/dist/mcs-vehicle.js.map +1 -1
- package/dist/node.js +1 -1
- package/dist/node.js.map +1 -1
- package/dist/oht.js.map +1 -1
- package/dist/scanner.js +30 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scene/root-container-override.js +9 -0
- package/dist/scene/root-container-override.js.map +1 -1
- package/dist/stocker-abnormal-bar.js +1 -1
- package/dist/stocker-abnormal-bar.js.map +1 -1
- package/dist/stocker-capacity-bar.js +3 -3
- package/dist/stocker-capacity-bar.js.map +1 -1
- package/dist/stocker-carrier-bar.js +1 -1
- package/dist/stocker-carrier-bar.js.map +1 -1
- package/dist/stocker.js +6 -0
- package/dist/stocker.js.map +1 -1
- package/dist/templates/backdrop.js +30 -0
- package/dist/templates/backdrop.js.map +1 -0
- package/dist/templates/decal.js +21 -0
- package/dist/templates/decal.js.map +1 -0
- package/dist/templates/index.js +7 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/scanner.js +20 -0
- package/dist/templates/scanner.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/get-value-on-ranges.js +2 -4
- package/dist/utils/get-value-on-ranges.js.map +1 -1
- package/dist/zone-capacity-bar.js +3 -3
- package/dist/zone-capacity-bar.js.map +1 -1
- package/icons/backdrop.png +0 -0
- package/icons/decal.png +0 -0
- package/jest.config.js +10 -0
- package/package.json +4 -3
- package/src/agv.ts +2 -2
- package/src/carrier-manager.ts +117 -0
- package/src/carrier.ts +80 -3
- package/src/conveyor-join.ts +5 -2
- package/src/conveyor.ts +8 -0
- package/src/crane.ts +1 -1
- package/src/factories/agv-3d.ts +9 -10
- package/src/factories/conveyor-3d.ts +168 -84
- package/src/factories/crane-3d.ts +6 -8
- package/src/factories/crane-rail-3d.ts +5 -7
- package/src/factories/machine-3d.ts +452 -145
- package/src/factories/oht-3d.ts +20 -20
- package/src/factories/shuttle-3d.ts +2 -6
- package/src/features/mcs-status-mixin.ts +21 -0
- package/src/index.ts +56 -34
- package/src/mcs-carrier-holder.ts +38 -34
- package/src/mcs-machine.ts +29 -0
- package/src/mcs-unit.ts +11 -0
- package/src/mcs-vehicle.ts +9 -6
- package/src/node.ts +1 -1
- package/src/oht.ts +1 -1
- package/src/scanner.ts +29 -0
- package/src/scene/root-container-override.ts +12 -0
- package/src/stocker-abnormal-bar.ts +1 -1
- package/src/stocker-capacity-bar.ts +3 -3
- package/src/stocker-carrier-bar.ts +1 -1
- package/src/stocker.ts +7 -0
- package/src/templates/backdrop.ts +30 -0
- package/src/templates/decal.ts +21 -0
- package/src/templates/index.ts +7 -1
- package/src/templates/scanner.ts +20 -0
- package/src/utils/get-value-on-ranges.ts +2 -4
- package/src/zone-capacity-bar.ts +3 -3
- package/translations/en.json +16 -1
- package/translations/ja.json +16 -1
- package/translations/ko.json +16 -1
- package/translations/ms.json +16 -1
- package/translations/zh.json +16 -1
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
* - Unit: 낮은 depth, 불투명
|
|
9
9
|
*/
|
|
10
10
|
import * as THREE from 'three';
|
|
11
|
+
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
11
12
|
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene';
|
|
12
13
|
import { LEGEND_CAPACITY } from '../features/mcs-status-default';
|
|
13
|
-
import {
|
|
14
|
+
import { getValueOnRanges } from '../utils/get-value-on-ranges';
|
|
14
15
|
/**
|
|
15
16
|
* 포트 기본 높이 (절대값).
|
|
16
17
|
* 모든 이송장치의 상단 높이 = 이 값.
|
|
@@ -33,15 +34,28 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
33
34
|
this._updatePending = false;
|
|
34
35
|
this._config = config;
|
|
35
36
|
}
|
|
36
|
-
// width, height 절대값 (음수 값은 양수로 취급)
|
|
37
|
+
// width, height 절대값 (음수 값은 양수로 취급).
|
|
38
|
+
// `resizable:false` 컴포넌트(예: Scanner)는 state.width/height 가 없고
|
|
39
|
+
// bounds 만 있을 수 있으므로 bounds 를 fallback 으로 사용.
|
|
40
|
+
//
|
|
41
|
+
// `??` only catches null/undefined — a property-panel input that produced
|
|
42
|
+
// NaN would slip through and corrupt every downstream Math.* call. Use a
|
|
43
|
+
// finite check so NaN falls through to bounds → 10 like missing values do.
|
|
37
44
|
get absSize() {
|
|
38
|
-
const
|
|
45
|
+
const state = (this.component.state || {});
|
|
46
|
+
const bounds = (this.component.bounds || {});
|
|
47
|
+
const width = pickFinite(state.width, bounds.width, 10);
|
|
48
|
+
const height = pickFinite(state.height, bounds.height, 10);
|
|
39
49
|
return { w: Math.abs(width), h: Math.abs(height) };
|
|
40
50
|
}
|
|
41
51
|
get effectiveDepth() {
|
|
42
52
|
const { depth } = this.component.state;
|
|
43
|
-
if (depth)
|
|
53
|
+
// Truthy `if (depth)` already rejected NaN/0/undefined, but be explicit
|
|
54
|
+
// so the intent (and the resolution chain) reads cleanly: only an
|
|
55
|
+
// explicit positive finite number wins; everything else → defaults.
|
|
56
|
+
if (typeof depth === 'number' && Number.isFinite(depth) && depth > 0) {
|
|
44
57
|
return depth;
|
|
58
|
+
}
|
|
45
59
|
if (this._config.defaultDepth)
|
|
46
60
|
return this._config.defaultDepth;
|
|
47
61
|
const { w, h } = this.absSize;
|
|
@@ -49,20 +63,33 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
49
63
|
}
|
|
50
64
|
get syncZPosOffset() {
|
|
51
65
|
var _a, _b;
|
|
66
|
+
// syncZPosOffset = placement + geometric (center origin = effectiveDepth/2)
|
|
67
|
+
// 중첩 보정(parent shift 상쇄)은 base RealObject.cz가 parent.geometricOffsetY를
|
|
68
|
+
// 빼는 방식으로 처리하므로 여기선 placement + 본인 geometric만 반환
|
|
69
|
+
const halfD = this.effectiveDepth / 2;
|
|
70
|
+
// zPosOverride: 고정 높이 (OHTLine 등)
|
|
71
|
+
if (this._config.zPosOverride != null) {
|
|
72
|
+
return this._config.zPosOverride + halfD;
|
|
73
|
+
}
|
|
52
74
|
// Port: 이송 표면 높이에 배치
|
|
53
75
|
if (this._config.defaultZPosFromParent) {
|
|
54
|
-
return TRANSPORT_SURFACE_HEIGHT;
|
|
76
|
+
return TRANSPORT_SURFACE_HEIGHT + halfD;
|
|
55
77
|
}
|
|
56
|
-
// Carrier:
|
|
78
|
+
// Carrier: 부모 유형에 따라 배치 위치 결정
|
|
57
79
|
if (this._config.sizeRatio && this._config.sizeRatio < 1) {
|
|
58
80
|
const parentType = (_b = (_a = this.component.parent) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.type;
|
|
59
|
-
|
|
60
|
-
|
|
81
|
+
// OHT: 그리퍼 아래에 매달림 — placement = -effectiveDepth(carrier top이 OHT bottom)
|
|
82
|
+
if (parentType === 'OHT') {
|
|
83
|
+
return -this.effectiveDepth + halfD;
|
|
84
|
+
}
|
|
85
|
+
if (parentType === 'Port' || parentType === 'Shelf' || parentType === 'Shuttle') {
|
|
86
|
+
return halfD;
|
|
61
87
|
}
|
|
62
|
-
return TRANSPORT_SURFACE_HEIGHT;
|
|
88
|
+
return TRANSPORT_SURFACE_HEIGHT + halfD;
|
|
63
89
|
}
|
|
64
|
-
return
|
|
90
|
+
return halfD;
|
|
65
91
|
}
|
|
92
|
+
// geometric 부분(center origin: depth/2)은 base 기본값 사용 — override 불필요
|
|
66
93
|
// 컴포넌트의 상태 색상(statusColor) 또는 fillStyle → Three.js 색상 변환
|
|
67
94
|
resolveColor() {
|
|
68
95
|
const comp = this.component;
|
|
@@ -70,7 +97,7 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
70
97
|
// '#F0F0F0'은 상태 미설정 시 기본 fallback — 무시하고 defaultColor 사용
|
|
71
98
|
try {
|
|
72
99
|
const sc = comp.statusColor;
|
|
73
|
-
if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0')
|
|
100
|
+
if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0' && sc !== 'transparent')
|
|
74
101
|
return new THREE.Color(sc).getHex();
|
|
75
102
|
}
|
|
76
103
|
catch (_a) {
|
|
@@ -78,7 +105,7 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
78
105
|
}
|
|
79
106
|
// 2) state.fillStyle fallback
|
|
80
107
|
const { fillStyle } = this.component.state;
|
|
81
|
-
if (fillStyle && typeof fillStyle === 'string') {
|
|
108
|
+
if (fillStyle && typeof fillStyle === 'string' && fillStyle !== 'transparent') {
|
|
82
109
|
try {
|
|
83
110
|
return new THREE.Color(fillStyle).getHex();
|
|
84
111
|
}
|
|
@@ -118,7 +145,7 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
118
145
|
// ── Geometry builders ──
|
|
119
146
|
buildBoxGeometry(w, d, h) {
|
|
120
147
|
const geo = new THREE.BoxGeometry(w, d, h);
|
|
121
|
-
return this.removeBottomFace(geo);
|
|
148
|
+
return this._config.keepBottomFace ? geo : this.removeBottomFace(geo);
|
|
122
149
|
}
|
|
123
150
|
buildHexagonGeometry(w, d, h) {
|
|
124
151
|
// Buffer의 2D 형태: 좌우 끝이 꺾인 육각형
|
|
@@ -143,6 +170,37 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
143
170
|
geo.rotateX(Math.PI / 2);
|
|
144
171
|
return this.removeBottomFace(geo);
|
|
145
172
|
}
|
|
173
|
+
buildPlaneGeometry(w, h) {
|
|
174
|
+
const geo = new THREE.PlaneGeometry(w, h);
|
|
175
|
+
geo.rotateX(-Math.PI / 2);
|
|
176
|
+
return geo;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 삼각형 prism — 2D Triangle 의 꼭짓점(state.x1/y1, x2/y2, x3/y3) 을 그대로 사용.
|
|
180
|
+
* things-scene 의 TriangleExtrude 와 동일한 방식 — 각 Scanner 인스턴스의
|
|
181
|
+
* 실제 정점 배치(방향, 비대칭 등) 가 3D 에 반영된다.
|
|
182
|
+
*
|
|
183
|
+
* `geometry.center()` 로 로컬 원점을 bounding-box 중심으로 맞추므로
|
|
184
|
+
* 후속 placement 와 정합.
|
|
185
|
+
*/
|
|
186
|
+
buildTriangleGeometry(d) {
|
|
187
|
+
const state = (this.component.state || {});
|
|
188
|
+
const { x1 = 0, y1 = 0, x2 = 0, y2 = 0, x3 = 0, y3 = 0 } = state;
|
|
189
|
+
const shape = new THREE.Shape();
|
|
190
|
+
shape.moveTo(x1, y1);
|
|
191
|
+
shape.lineTo(x2, y2);
|
|
192
|
+
shape.lineTo(x3, y3);
|
|
193
|
+
shape.closePath();
|
|
194
|
+
const geo = new THREE.ExtrudeGeometry(shape, {
|
|
195
|
+
steps: 1,
|
|
196
|
+
depth: d,
|
|
197
|
+
bevelEnabled: false,
|
|
198
|
+
});
|
|
199
|
+
// bounding box 중심으로 원점 정렬 후 Y-up 좌표계로 회전
|
|
200
|
+
geo.center();
|
|
201
|
+
geo.rotateX(Math.PI / 2);
|
|
202
|
+
return this.removeBottomFace(geo);
|
|
203
|
+
}
|
|
146
204
|
build() {
|
|
147
205
|
var _a;
|
|
148
206
|
super.build();
|
|
@@ -159,18 +217,25 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
159
217
|
const depth = rawDepth * ratio;
|
|
160
218
|
const color = this.resolveColor();
|
|
161
219
|
const { opacity, metalness, roughness, shape } = this._config;
|
|
162
|
-
const geometry = shape === '
|
|
163
|
-
? this.
|
|
164
|
-
:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
220
|
+
const geometry = shape === 'plane'
|
|
221
|
+
? this.buildPlaneGeometry(w, h)
|
|
222
|
+
: shape === 'hexagon'
|
|
223
|
+
? this.buildHexagonGeometry(w, depth, h)
|
|
224
|
+
: shape === 'triangle'
|
|
225
|
+
? this.buildTriangleGeometry(depth)
|
|
226
|
+
: this.buildBoxGeometry(w, depth, h);
|
|
227
|
+
const isPlane = shape === 'plane';
|
|
228
|
+
const material = isPlane
|
|
229
|
+
? new THREE.MeshBasicMaterial({
|
|
230
|
+
color,
|
|
231
|
+
opacity,
|
|
232
|
+
transparent: opacity < 1,
|
|
233
|
+
side: THREE.DoubleSide,
|
|
234
|
+
})
|
|
235
|
+
: new THREE.MeshStandardMaterial(Object.assign({ color,
|
|
236
|
+
metalness,
|
|
237
|
+
roughness,
|
|
238
|
+
opacity, transparent: opacity < 1, side: opacity < 1 ? THREE.DoubleSide : THREE.FrontSide }, (this._config.emissiveIntensity ? { emissive: color, emissiveIntensity: this._config.emissiveIntensity } : {})));
|
|
174
239
|
// z-fighting 방지: 바닥면(floor grid)보다 항상 앞에 렌더링
|
|
175
240
|
material.polygonOffset = true;
|
|
176
241
|
material.polygonOffsetFactor = -1;
|
|
@@ -195,11 +260,14 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
195
260
|
}
|
|
196
261
|
}
|
|
197
262
|
this._ownMesh = new THREE.Mesh(geometry, meshMaterial);
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
263
|
+
// ownGroup 기준(bottom-up): mesh 바닥을 y=0에 맞추고 ownGroup이 -d/2로 shift해
|
|
264
|
+
// center-origin object3d에 정렬됨.
|
|
265
|
+
// box/hexagon (center-local geometry): y=rawDepth/2 (바닥을 0에 맞춤)
|
|
266
|
+
// plane: y=1 (바닥 살짝 띄움)
|
|
267
|
+
this._ownMesh.position.y = isPlane ? 1 : rawDepth / 2;
|
|
268
|
+
this._ownMesh.castShadow = !isPlane;
|
|
201
269
|
this._ownMesh.receiveShadow = true;
|
|
202
|
-
this.
|
|
270
|
+
this.components3D.add(this._ownMesh);
|
|
203
271
|
}
|
|
204
272
|
onchange(after, before) {
|
|
205
273
|
if ('width' in after ||
|
|
@@ -283,7 +351,7 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
283
351
|
update() {
|
|
284
352
|
// 자신의 mesh만 제거 (자식 컴포넌트 3D 오브젝트 보존)
|
|
285
353
|
if (this._ownMesh) {
|
|
286
|
-
this.
|
|
354
|
+
this.components3D.remove(this._ownMesh);
|
|
287
355
|
disposeObject3D(this._ownMesh);
|
|
288
356
|
this._ownMesh = undefined;
|
|
289
357
|
}
|
|
@@ -295,9 +363,9 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
295
363
|
// RealObjectGroup 기본 동작 비활성화
|
|
296
364
|
updateDimension() { }
|
|
297
365
|
updateAlpha() {
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
const effective =
|
|
366
|
+
const state = this.component.state;
|
|
367
|
+
// alpha가 명시적으로 설정되어 있으면 그 값을 사용, 없으면 config 기본값 적용
|
|
368
|
+
const effective = ('alpha' in state) ? state.alpha : this._config.opacity;
|
|
301
369
|
this.object3d.traverse((o) => {
|
|
302
370
|
if (!o.material)
|
|
303
371
|
return;
|
|
@@ -313,6 +381,19 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
313
381
|
});
|
|
314
382
|
}
|
|
315
383
|
}
|
|
384
|
+
// ── 헬퍼 ────────────────────────────────────────────────
|
|
385
|
+
/**
|
|
386
|
+
* Returns the first argument that is a finite number. NaN slips past `??`
|
|
387
|
+
* (which only catches null/undefined), so without this a property-panel
|
|
388
|
+
* input producing NaN would corrupt absSize and cascade through Math.*.
|
|
389
|
+
*/
|
|
390
|
+
function pickFinite(...candidates) {
|
|
391
|
+
for (const c of candidates) {
|
|
392
|
+
if (typeof c === 'number' && Number.isFinite(c))
|
|
393
|
+
return c;
|
|
394
|
+
}
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
316
397
|
// ── 팩토리 등록 헬퍼 ─────────────────────────────────────
|
|
317
398
|
function register(type, overrides = {}) {
|
|
318
399
|
const config = Object.assign(Object.assign({}, DEFAULTS), overrides);
|
|
@@ -337,17 +418,89 @@ register('Buffer', {
|
|
|
337
418
|
shape: 'hexagon',
|
|
338
419
|
});
|
|
339
420
|
register('AGVLine', {
|
|
340
|
-
depthRatio: 0
|
|
341
|
-
|
|
342
|
-
roughness: 0.3,
|
|
343
|
-
defaultColor: 0x888899,
|
|
344
|
-
});
|
|
345
|
-
register('OHTLine', {
|
|
346
|
-
depthRatio: 0.12,
|
|
421
|
+
depthRatio: 0,
|
|
422
|
+
defaultDepth: 0.1,
|
|
347
423
|
metalness: 0.6,
|
|
348
424
|
roughness: 0.3,
|
|
349
425
|
defaultColor: 0x888899,
|
|
350
426
|
});
|
|
427
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
428
|
+
// OHTLine — 천장 I-beam 레일
|
|
429
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
430
|
+
const OHT_LINE_RAIL_COLOR = 0xcccccc;
|
|
431
|
+
const OHT_LINE_HEIGHT = 7 * TRANSPORT_SURFACE_HEIGHT; // OHT 상단과 일치
|
|
432
|
+
class OHTLineRealObject3D extends RealObjectGroup {
|
|
433
|
+
get effectiveDepth() { return 0; }
|
|
434
|
+
get syncZPosOffset() { return OHT_LINE_HEIGHT; }
|
|
435
|
+
build() {
|
|
436
|
+
super.build();
|
|
437
|
+
const { width = 10, height = 10 } = this.component.state;
|
|
438
|
+
const w = Math.abs(width);
|
|
439
|
+
const h = Math.abs(height);
|
|
440
|
+
if (!w || !h)
|
|
441
|
+
return;
|
|
442
|
+
// I-beam 치수: 이송 방향 = 긴 변, 폭은 2D의 1/2
|
|
443
|
+
const isVertical = w < h;
|
|
444
|
+
const shortSide = isVertical ? w : h; // 2D 원본 짧은 쪽 폭
|
|
445
|
+
const beltW = shortSide * 0.25; // I-beam 폭: 원본의 1/4
|
|
446
|
+
const beamH = Math.max(beltW * 0.5, 3); // I-beam 전체 높이
|
|
447
|
+
const inset = shortSide * 0.5; // 양끝 여백: 원본 폭의 1/2
|
|
448
|
+
const railLen = (isVertical ? h : w) - inset * 2;
|
|
449
|
+
const flangeH = beamH * 0.2; // 플랜지 두께
|
|
450
|
+
const flangeW = beltW * 0.8; // 플랜지 폭
|
|
451
|
+
const webH = beamH - flangeH * 2; // 웹 높이
|
|
452
|
+
const webW = Math.max(flangeW * 0.25, 1); // 웹 두께
|
|
453
|
+
const mat = new THREE.MeshStandardMaterial({
|
|
454
|
+
color: OHT_LINE_RAIL_COLOR,
|
|
455
|
+
metalness: 0.5,
|
|
456
|
+
roughness: 0.4,
|
|
457
|
+
});
|
|
458
|
+
const geoms = [];
|
|
459
|
+
// 상부 플랜지
|
|
460
|
+
const topFlange = isVertical
|
|
461
|
+
? new THREE.BoxGeometry(flangeW, flangeH, railLen)
|
|
462
|
+
: new THREE.BoxGeometry(railLen, flangeH, flangeW);
|
|
463
|
+
topFlange.translate(0, -flangeH / 2, 0);
|
|
464
|
+
geoms.push(topFlange);
|
|
465
|
+
// 웹 (수직판)
|
|
466
|
+
const web = isVertical
|
|
467
|
+
? new THREE.BoxGeometry(webW, webH, railLen)
|
|
468
|
+
: new THREE.BoxGeometry(railLen, webH, webW);
|
|
469
|
+
web.translate(0, -flangeH - webH / 2, 0);
|
|
470
|
+
geoms.push(web);
|
|
471
|
+
// 하부 플랜지
|
|
472
|
+
const botFlange = isVertical
|
|
473
|
+
? new THREE.BoxGeometry(flangeW, flangeH, railLen)
|
|
474
|
+
: new THREE.BoxGeometry(railLen, flangeH, flangeW);
|
|
475
|
+
botFlange.translate(0, -flangeH - webH - flangeH / 2, 0);
|
|
476
|
+
geoms.push(botFlange);
|
|
477
|
+
this._mesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(geoms), mat);
|
|
478
|
+
this._mesh.castShadow = true;
|
|
479
|
+
this.object3d.add(this._mesh);
|
|
480
|
+
}
|
|
481
|
+
update() {
|
|
482
|
+
if (this._mesh) {
|
|
483
|
+
this.object3d.remove(this._mesh);
|
|
484
|
+
this._mesh.geometry.dispose();
|
|
485
|
+
this._mesh.material.dispose();
|
|
486
|
+
this._mesh = undefined;
|
|
487
|
+
}
|
|
488
|
+
this.build();
|
|
489
|
+
this.updateTransform();
|
|
490
|
+
this.updateHidden();
|
|
491
|
+
}
|
|
492
|
+
onchange(after, before) {
|
|
493
|
+
if ('width' in after || 'height' in after || 'depth' in after ||
|
|
494
|
+
'fillStyle' in after || 'strokeStyle' in after) {
|
|
495
|
+
this.update();
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
super.onchange(after, before);
|
|
499
|
+
}
|
|
500
|
+
updateDimension() { }
|
|
501
|
+
updateAlpha() { }
|
|
502
|
+
}
|
|
503
|
+
registerRealObjectFactory('OHTLine', (component) => new OHTLineRealObject3D(component));
|
|
351
504
|
// STOCKER: 벽면에 용량 게이지 바를 포함하는 커스텀 3D 오브젝트
|
|
352
505
|
const STOCKER_CONFIG = Object.assign(Object.assign({}, DEFAULTS), { defaultDepth: 5 * TRANSPORT_SURFACE_HEIGHT, opacity: 0.75, metalness: 0.3, roughness: 0.6, defaultColor: 0x8899bb });
|
|
353
506
|
class StockerRealObject3D extends MCSRealObject3D {
|
|
@@ -423,8 +576,9 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
423
576
|
mat.polygonOffsetFactor = -2;
|
|
424
577
|
mat.polygonOffsetUnit = -2;
|
|
425
578
|
this.gaugeMesh = new THREE.Mesh(geo, mat);
|
|
579
|
+
// ownGroup 기준(bottom-up): 볼륨 상단 = +depth. 그 위로 0.5 띄움.
|
|
426
580
|
this.gaugeMesh.position.set(0, depth + 0.5, 0);
|
|
427
|
-
this.
|
|
581
|
+
this.components3D.add(this.gaugeMesh);
|
|
428
582
|
}
|
|
429
583
|
resolveGauge(data, mode) {
|
|
430
584
|
var _a, _b;
|
|
@@ -469,7 +623,7 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
469
623
|
const fullup = data.FULLUP === 'T';
|
|
470
624
|
const usage = Math.round((cur / max) * 100);
|
|
471
625
|
const legend = ((_b = (_a = this.component.root) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b.LEGEND_CAPACITY) || LEGEND_CAPACITY;
|
|
472
|
-
const color = fullup ? 'red' : (
|
|
626
|
+
const color = fullup ? 'red' : (getValueOnRanges(usage, legend) || '#44aa44');
|
|
473
627
|
return {
|
|
474
628
|
segments: [{ ratio: usage / 100, color }],
|
|
475
629
|
borderColor: color,
|
|
@@ -478,6 +632,10 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
478
632
|
}
|
|
479
633
|
}
|
|
480
634
|
}
|
|
635
|
+
update() {
|
|
636
|
+
this.clearGauge();
|
|
637
|
+
super.update();
|
|
638
|
+
}
|
|
481
639
|
onchange(after, before) {
|
|
482
640
|
if ('data' in after) {
|
|
483
641
|
this.clearGauge();
|
|
@@ -492,7 +650,7 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
492
650
|
clearGauge() {
|
|
493
651
|
var _a, _b;
|
|
494
652
|
if (this.gaugeMesh) {
|
|
495
|
-
this.
|
|
653
|
+
this.components3D.remove(this.gaugeMesh);
|
|
496
654
|
(_a = this.gaugeMesh.geometry) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
497
655
|
const mat = this.gaugeMesh.material;
|
|
498
656
|
(_b = mat === null || mat === void 0 ? void 0 : mat.map) === null || _b === void 0 ? void 0 : _b.dispose();
|
|
@@ -505,8 +663,8 @@ registerRealObjectFactory('STOCKER', (component) => new StockerRealObject3D(comp
|
|
|
505
663
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
506
664
|
// Unit (이동체 · 홀더) — 낮은 depth, 불투명
|
|
507
665
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
508
|
-
// Port:
|
|
509
|
-
const PORT_CONFIG = Object.assign(Object.assign({}, DEFAULTS), { depthRatio: 0.
|
|
666
|
+
// Port: 바닥판 (PlaneGeometry)
|
|
667
|
+
const PORT_CONFIG = Object.assign(Object.assign({}, DEFAULTS), { depthRatio: 0.01, defaultZPosFromParent: true, opacity: 0.1, metalness: 0.3, roughness: 0.8, defaultColor: 0xb0a090, shape: 'plane' });
|
|
510
668
|
registerRealObjectFactory('Port', (component) => new MCSRealObject3D(component, PORT_CONFIG));
|
|
511
669
|
register('Shelf', {
|
|
512
670
|
depthRatio: 0.25,
|
|
@@ -515,113 +673,206 @@ register('Shelf', {
|
|
|
515
673
|
defaultColor: 0xaabbcc,
|
|
516
674
|
});
|
|
517
675
|
// Carrier: CARRIERSTATUS에 따른 3D 애니메이션 지원
|
|
518
|
-
const CARRIER_CONFIG = Object.assign(Object.assign({}, DEFAULTS), { depthRatio: 0.5, metalness: 0
|
|
676
|
+
const CARRIER_CONFIG = Object.assign(Object.assign({}, DEFAULTS), { depthRatio: 0.5, metalness: 0, roughness: 1, defaultColor: 0xf0f0f0, isVisible: (c) => !!c.state.id, sizeRatio: 0.8, texturedTop: true });
|
|
519
677
|
class CarrierRealObject3D extends MCSRealObject3D {
|
|
520
678
|
constructor(component) {
|
|
521
679
|
super(component, CARRIER_CONFIG);
|
|
522
|
-
|
|
523
|
-
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
registerRealObjectFactory('Carrier', (component) => new CarrierRealObject3D(component));
|
|
683
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
684
|
+
// Gauge (용량 게이지) — 얇은 바
|
|
685
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
686
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
687
|
+
// Gauge (용량 게이지) — 2D 렌더링을 Plane 텍스처로 3D 표시
|
|
688
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
689
|
+
// Gauge 컴포넌트는 3D에서 표시하지 않음 (부모 STOCKER의 3D 모델에서 직접 게이지 렌더링)
|
|
690
|
+
// null 반환으로 3D 오브젝트 생성을 억제하고, default factory fallback도 방지
|
|
691
|
+
registerRealObjectFactory('StockerCapacityBar', () => undefined);
|
|
692
|
+
registerRealObjectFactory('MCSGaugeCapacityBar', () => undefined);
|
|
693
|
+
registerRealObjectFactory('ZoneCapacityBar', () => undefined);
|
|
694
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
695
|
+
// PortFlow — 2D render를 Plane 텍스처로 3D 표시
|
|
696
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
697
|
+
class PortFlowRealObject3D extends RealObjectGroup {
|
|
698
|
+
get effectiveDepth() { return 0; }
|
|
699
|
+
get syncZPosOffset() {
|
|
700
|
+
var _a;
|
|
701
|
+
// 부모 Port/Conveyor의 zPos와 동일하게
|
|
702
|
+
const parent = this.component.parentObject;
|
|
703
|
+
if (((_a = parent === null || parent === void 0 ? void 0 : parent.state) === null || _a === void 0 ? void 0 : _a.zPos) != null)
|
|
704
|
+
return parent.state.zPos;
|
|
705
|
+
return TRANSPORT_SURFACE_HEIGHT;
|
|
524
706
|
}
|
|
525
707
|
build() {
|
|
526
708
|
super.build();
|
|
527
|
-
this.
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
* things-scene의 delta 시스템과 독립적으로 동작.
|
|
532
|
-
*/
|
|
533
|
-
startCarrierAnimation() {
|
|
534
|
-
var _a, _b, _c;
|
|
535
|
-
this.stopCarrierAnimation();
|
|
536
|
-
const comp = this.component;
|
|
537
|
-
if (!((_a = comp.app) === null || _a === void 0 ? void 0 : _a.isViewMode))
|
|
538
|
-
return;
|
|
539
|
-
const carrierStatus = (_b = comp.state) === null || _b === void 0 ? void 0 : _b.CARRIERSTATUS;
|
|
540
|
-
if (!carrierStatus)
|
|
541
|
-
return;
|
|
542
|
-
// 애니메이션 설정 가져오기
|
|
543
|
-
const config = (_c = comp.getAnimationConfig) === null || _c === void 0 ? void 0 : _c.call(comp, carrierStatus);
|
|
544
|
-
if (!(config === null || config === void 0 ? void 0 : config.animation))
|
|
545
|
-
return;
|
|
546
|
-
const { type, duration = 2000, scale: animScale = 1.3 } = config.animation;
|
|
547
|
-
if (!type)
|
|
709
|
+
const { width = 10, height = 10 } = this.component.state;
|
|
710
|
+
const w = Math.abs(width);
|
|
711
|
+
const h = Math.abs(height);
|
|
712
|
+
if (!w || !h)
|
|
548
713
|
return;
|
|
549
|
-
|
|
550
|
-
//
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
case 'heartbeat': {
|
|
561
|
-
const t = progress < 0.5 ? progress * 2 : (1 - progress) * 2;
|
|
562
|
-
const s = 1 + (animScale - 1) * t;
|
|
563
|
-
obj.scale.set(s, 1, s);
|
|
714
|
+
// 2D render를 캔버스에 캡처
|
|
715
|
+
// 화살표 날개 + 텍스트 영역이 bounds 밖으로 나가므로 전체 영역 계산
|
|
716
|
+
const { left, top } = this.component.bounds;
|
|
717
|
+
const { fontSize = 24, labelPosition = 'top' } = this.component.state;
|
|
718
|
+
const arrowPad = Math.min(w, h) * 0.4;
|
|
719
|
+
// 텍스트 영역 확장
|
|
720
|
+
let padLeft = arrowPad, padRight = arrowPad, padTop = arrowPad, padBottom = arrowPad;
|
|
721
|
+
if (this.component.hasTextProperty) {
|
|
722
|
+
switch (labelPosition) {
|
|
723
|
+
case 'top':
|
|
724
|
+
padTop = Math.max(padTop, fontSize + 4);
|
|
564
725
|
break;
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
obj.rotation.y = progress * Math.PI * 2;
|
|
726
|
+
case 'bottom':
|
|
727
|
+
padBottom = Math.max(padBottom, fontSize + 4);
|
|
568
728
|
break;
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const theta = (_a = config.animation.theta) !== null && _a !== void 0 ? _a : 0.26;
|
|
572
|
-
const current = Math.sin(progress * Math.PI * 2) * theta;
|
|
573
|
-
obj.rotation.y = current;
|
|
729
|
+
case 'left':
|
|
730
|
+
padLeft = Math.max(padLeft, fontSize * 4 + 4);
|
|
574
731
|
break;
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const alpha = 0.3 + 0.7 * (progress < 0.5 ? progress * 2 : (1 - progress) * 2);
|
|
578
|
-
obj.traverse((o) => {
|
|
579
|
-
if (o.material && !o.material.map) {
|
|
580
|
-
o.material.opacity = alpha;
|
|
581
|
-
o.material.transparent = true;
|
|
582
|
-
}
|
|
583
|
-
});
|
|
732
|
+
case 'right':
|
|
733
|
+
padRight = Math.max(padRight, fontSize * 4 + 4);
|
|
584
734
|
break;
|
|
585
|
-
}
|
|
586
735
|
}
|
|
587
|
-
this._animRaf = requestAnimationFrame(animate);
|
|
588
|
-
};
|
|
589
|
-
this._animRaf = requestAnimationFrame(animate);
|
|
590
|
-
}
|
|
591
|
-
// 자체 애니메이션이 실행 중이면 things-scene의 syncAnimationDelta 비활성화
|
|
592
|
-
syncAnimationDelta() {
|
|
593
|
-
if (!this._animRaf) {
|
|
594
|
-
super.syncAnimationDelta();
|
|
595
736
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
737
|
+
const totalW = w + padLeft + padRight;
|
|
738
|
+
const totalH = h + padTop + padBottom;
|
|
739
|
+
const scale = 2;
|
|
740
|
+
const canvas = document.createElement('canvas');
|
|
741
|
+
canvas.width = totalW * scale;
|
|
742
|
+
canvas.height = totalH * scale;
|
|
743
|
+
const ctx = canvas.getContext('2d');
|
|
744
|
+
ctx.save();
|
|
745
|
+
ctx.scale(scale, scale);
|
|
746
|
+
ctx.translate(-left + padLeft, -top + padTop);
|
|
747
|
+
try {
|
|
748
|
+
// 화살표 렌더
|
|
749
|
+
this.component.render(ctx);
|
|
750
|
+
ctx.fillStyle = this.component.state.strokeStyle || '#666666';
|
|
751
|
+
ctx.fill();
|
|
752
|
+
// 텍스트 렌더
|
|
753
|
+
if (this.component.hasTextProperty && this.component.drawText) {
|
|
754
|
+
;
|
|
755
|
+
this.component.drawText(ctx);
|
|
756
|
+
}
|
|
601
757
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
this.
|
|
758
|
+
catch ( /* render 실패 시 빈 텍스처 */_a) { /* render 실패 시 빈 텍스처 */ }
|
|
759
|
+
ctx.restore();
|
|
760
|
+
this._texture = new THREE.CanvasTexture(canvas);
|
|
761
|
+
this._texture.colorSpace = THREE.SRGBColorSpace;
|
|
762
|
+
const geo = new THREE.PlaneGeometry(totalW, totalH);
|
|
763
|
+
geo.rotateX(-Math.PI / 2);
|
|
764
|
+
const mat = new THREE.MeshBasicMaterial({
|
|
765
|
+
map: this._texture,
|
|
766
|
+
alphaTest: 0.1,
|
|
767
|
+
side: THREE.DoubleSide,
|
|
768
|
+
});
|
|
769
|
+
this._mesh = new THREE.Mesh(geo, mat);
|
|
770
|
+
// 비대칭 패딩 보정: 플레인 센터를 컴포넌트 bounds 센터에 맞춤
|
|
771
|
+
this._mesh.position.x = (padRight - padLeft) / 2;
|
|
772
|
+
this._mesh.position.y = 1;
|
|
773
|
+
this._mesh.position.z = (padBottom - padTop) / 2;
|
|
774
|
+
this.object3d.add(this._mesh);
|
|
605
775
|
}
|
|
606
776
|
update() {
|
|
607
|
-
|
|
608
|
-
|
|
777
|
+
var _a;
|
|
778
|
+
if (this._mesh) {
|
|
779
|
+
this.object3d.remove(this._mesh);
|
|
780
|
+
this._mesh.geometry.dispose();
|
|
781
|
+
this._mesh.material.dispose();
|
|
782
|
+
(_a = this._texture) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
783
|
+
this._mesh = undefined;
|
|
784
|
+
this._texture = undefined;
|
|
785
|
+
}
|
|
786
|
+
this.build();
|
|
787
|
+
this.updateTransform();
|
|
788
|
+
this.updateHidden();
|
|
609
789
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
790
|
+
onchange(after, before) {
|
|
791
|
+
if ('width' in after || 'height' in after || 'orientation' in after ||
|
|
792
|
+
'direction' in after || 'strokeStyle' in after || 'INOUTTYPE' in after) {
|
|
793
|
+
this.update();
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
super.onchange(after, before);
|
|
613
797
|
}
|
|
798
|
+
updateDimension() { }
|
|
799
|
+
updateAlpha() { }
|
|
614
800
|
}
|
|
615
|
-
registerRealObjectFactory('
|
|
616
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
617
|
-
// Gauge (용량 게이지) — 얇은 바
|
|
801
|
+
registerRealObjectFactory('PortFlow', (component) => new PortFlowRealObject3D(component));
|
|
618
802
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
803
|
+
// Node — 2D render를 바닥 Plane 텍스처로 3D 표시
|
|
619
804
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
805
|
+
class NodeRealObject3D extends RealObjectGroup {
|
|
806
|
+
get effectiveDepth() { return 0; }
|
|
807
|
+
get syncZPosOffset() { return 0; }
|
|
808
|
+
build() {
|
|
809
|
+
super.build();
|
|
810
|
+
const { cx, cy } = this.component.state;
|
|
811
|
+
const radius = 8;
|
|
812
|
+
const size = radius * 2;
|
|
813
|
+
const scale = 2;
|
|
814
|
+
const pad = 2;
|
|
815
|
+
const canvas = document.createElement('canvas');
|
|
816
|
+
canvas.width = (size + pad * 2) * scale;
|
|
817
|
+
canvas.height = (size + pad * 2) * scale;
|
|
818
|
+
const ctx = canvas.getContext('2d');
|
|
819
|
+
ctx.save();
|
|
820
|
+
ctx.scale(scale, scale);
|
|
821
|
+
ctx.translate(-cx + radius + pad, -cy + radius + pad);
|
|
822
|
+
try {
|
|
823
|
+
this.component.render(ctx);
|
|
824
|
+
const { fillStyle = '#888888' } = this.component.state;
|
|
825
|
+
ctx.fillStyle = typeof fillStyle === 'string' ? fillStyle : '#888888';
|
|
826
|
+
ctx.fill();
|
|
827
|
+
const { strokeStyle } = this.component.state;
|
|
828
|
+
if (strokeStyle && typeof strokeStyle === 'string') {
|
|
829
|
+
ctx.strokeStyle = strokeStyle;
|
|
830
|
+
ctx.lineWidth = 1;
|
|
831
|
+
ctx.stroke();
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
catch ( /* render 실패 시 빈 텍스처 */_a) { /* render 실패 시 빈 텍스처 */ }
|
|
835
|
+
ctx.restore();
|
|
836
|
+
this._texture = new THREE.CanvasTexture(canvas);
|
|
837
|
+
this._texture.colorSpace = THREE.SRGBColorSpace;
|
|
838
|
+
const geo = new THREE.PlaneGeometry(size + pad * 2, size + pad * 2);
|
|
839
|
+
geo.rotateX(-Math.PI / 2);
|
|
840
|
+
const mat = new THREE.MeshBasicMaterial({
|
|
841
|
+
map: this._texture,
|
|
842
|
+
alphaTest: 0.1,
|
|
843
|
+
side: THREE.DoubleSide,
|
|
844
|
+
});
|
|
845
|
+
this._mesh = new THREE.Mesh(geo, mat);
|
|
846
|
+
this._mesh.position.y = 1;
|
|
847
|
+
this.object3d.add(this._mesh);
|
|
848
|
+
}
|
|
849
|
+
update() {
|
|
850
|
+
var _a;
|
|
851
|
+
if (this._mesh) {
|
|
852
|
+
this.object3d.remove(this._mesh);
|
|
853
|
+
this._mesh.geometry.dispose();
|
|
854
|
+
this._mesh.material.dispose();
|
|
855
|
+
(_a = this._texture) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
856
|
+
this._mesh = undefined;
|
|
857
|
+
this._texture = undefined;
|
|
858
|
+
}
|
|
859
|
+
this.build();
|
|
860
|
+
this.updateTransform();
|
|
861
|
+
this.updateHidden();
|
|
862
|
+
}
|
|
863
|
+
onchange(after, before) {
|
|
864
|
+
if ('fillStyle' in after || 'strokeStyle' in after) {
|
|
865
|
+
this.update();
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
super.onchange(after, before);
|
|
869
|
+
}
|
|
870
|
+
updateDimension() { }
|
|
871
|
+
updateAlpha() { }
|
|
872
|
+
}
|
|
873
|
+
registerRealObjectFactory('Node', (component) => new NodeRealObject3D(component));
|
|
874
|
+
// Scanner — 2D Triangle 실루엣과 정합되는 삼각형 prism.
|
|
875
|
+
// 이송 표면 높이의 0.2배(=6) 얇은 기둥 — 원래 값 유지, shape 만 'box' → 'triangle'.
|
|
876
|
+
const SCANNER_CONFIG = Object.assign(Object.assign({}, DEFAULTS), { defaultDepth: TRANSPORT_SURFACE_HEIGHT * 0.2, defaultZPosFromParent: true, opacity: 1, metalness: 0.3, roughness: 0.7, defaultColor: 0x888888, shape: 'triangle' });
|
|
877
|
+
registerRealObjectFactory('Scanner', (component) => new MCSRealObject3D(component, SCANNER_CONFIG));
|
|
627
878
|
//# sourceMappingURL=machine-3d.js.map
|