@fmsim/machine 2.0.0-beta.99 → 2.0.1
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/carrier-manager.js +9 -1
- package/dist/carrier-manager.js.map +1 -1
- package/dist/carrier.js +69 -52
- package/dist/carrier.js.map +1 -1
- package/dist/crane.js +15 -6
- package/dist/crane.js.map +1 -1
- package/dist/factories/agv-3d.js +9 -27
- package/dist/factories/agv-3d.js.map +1 -1
- package/dist/factories/conveyor-3d.js +11 -19
- package/dist/factories/conveyor-3d.js.map +1 -1
- package/dist/factories/crane-3d.js +8 -17
- package/dist/factories/crane-3d.js.map +1 -1
- package/dist/factories/crane-rail-3d.js +8 -12
- package/dist/factories/crane-rail-3d.js.map +1 -1
- package/dist/factories/machine-3d.js +100 -63
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/factories/oht-3d.js +9 -27
- package/dist/factories/oht-3d.js.map +1 -1
- package/dist/factories/shuttle-3d.js +11 -27
- package/dist/factories/shuttle-3d.js.map +1 -1
- package/dist/features/capacity-gauge-mixin.js +520 -0
- package/dist/features/capacity-gauge-mixin.js.map +1 -0
- package/dist/features/mcs-status-default.js +13 -0
- package/dist/features/mcs-status-default.js.map +1 -1
- package/dist/features/parent-object-mixin.js +5 -1
- package/dist/features/parent-object-mixin.js.map +1 -1
- package/dist/mcs-machine.js +4 -23
- package/dist/mcs-machine.js.map +1 -1
- package/dist/mcs-unit.js +2 -5
- package/dist/mcs-unit.js.map +1 -1
- package/dist/mcs-vehicle.js +3 -1
- package/dist/mcs-vehicle.js.map +1 -1
- package/dist/scene/root-container-override.js +5 -2
- package/dist/scene/root-container-override.js.map +1 -1
- package/dist/stocker-capacity-bar.js +11 -372
- package/dist/stocker-capacity-bar.js.map +1 -1
- package/dist/stocker.js +29 -1
- package/dist/stocker.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/crane-position.js +23 -14
- package/dist/utils/crane-position.js.map +1 -1
- package/dist/utils/stocker-gauge.js +48 -10
- package/dist/utils/stocker-gauge.js.map +1 -1
- package/dist/zone-capacity-bar.js +13 -213
- package/dist/zone-capacity-bar.js.map +1 -1
- package/package.json +4 -4
- package/src/carrier-manager.ts +9 -1
- package/src/carrier.ts +73 -52
- package/src/crane.ts +14 -5
- package/src/factories/agv-3d.ts +10 -26
- package/src/factories/conveyor-3d.ts +12 -19
- package/src/factories/crane-3d.ts +9 -17
- package/src/factories/crane-rail-3d.ts +9 -13
- package/src/factories/machine-3d.ts +125 -66
- package/src/factories/oht-3d.ts +10 -26
- package/src/factories/shuttle-3d.ts +12 -26
- package/src/features/capacity-gauge-mixin.ts +642 -0
- package/src/features/mcs-status-default.ts +13 -0
- package/src/features/parent-object-mixin.ts +5 -1
- package/src/mcs-machine.ts +4 -23
- package/src/mcs-unit.ts +2 -5
- package/src/mcs-vehicle.ts +3 -1
- package/src/scene/root-container-override.ts +5 -2
- package/src/stocker-capacity-bar.ts +15 -476
- package/src/stocker.ts +29 -1
- package/src/utils/crane-position.ts +46 -14
- package/src/utils/stocker-gauge.ts +63 -10
- package/src/zone-capacity-bar.ts +17 -282
- package/translations/en.json +4 -1
- package/translations/ja.json +4 -1
- package/translations/ko.json +4 -1
- package/translations/ms.json +4 -1
- package/translations/zh.json +4 -1
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
import * as THREE from 'three'
|
|
12
12
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
|
13
|
-
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene'
|
|
13
|
+
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene'
|
|
14
14
|
import type { Component } from '@hatiolab/things-scene'
|
|
15
|
-
import { LEGEND_CAPACITY } from '../features/mcs-status-default'
|
|
15
|
+
import { LEGEND_CAPACITY, resolveCarrierLegend } from '../features/mcs-status-default'
|
|
16
16
|
import { getValueOnRanges } from '../utils/get-value-on-ranges'
|
|
17
17
|
import { fitGaugeScale } from '../utils/fit-gauge-scale'
|
|
18
18
|
import {
|
|
19
19
|
carrierSegmentsFromData,
|
|
20
20
|
abnormalSegmentsFromData,
|
|
21
|
-
|
|
21
|
+
carrierColorsFromLegend,
|
|
22
22
|
ABNORMAL_COLORS
|
|
23
23
|
} from '../utils/stocker-gauge'
|
|
24
24
|
import { Z_FMSIM } from './z-priority'
|
|
@@ -312,23 +312,30 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
312
312
|
: this.buildBoxGeometry(w, depth, h)
|
|
313
313
|
|
|
314
314
|
const isPlane = shape === 'plane'
|
|
315
|
-
|
|
315
|
+
// Standard body 는 MaterialBank 공유(색 변형) — 인스턴스 dedup + faint 안전(코어가 공유 base 의
|
|
316
|
+
// 변형만 스왑, 원본 base 불변). plane 은 unlit MeshBasicMaterial 이라 bank(Standard 전용) 비대상 → raw.
|
|
317
|
+
const material: THREE.Material = isPlane
|
|
316
318
|
? new THREE.MeshBasicMaterial({
|
|
317
319
|
color,
|
|
318
320
|
opacity,
|
|
319
321
|
transparent: opacity < 1,
|
|
320
322
|
side: THREE.DoubleSide,
|
|
321
323
|
})
|
|
322
|
-
:
|
|
324
|
+
: MaterialBank.material(
|
|
325
|
+
'mcs|body',
|
|
326
|
+
{
|
|
327
|
+
metalness,
|
|
328
|
+
roughness,
|
|
329
|
+
opacity,
|
|
330
|
+
side: opacity < 1 ? THREE.DoubleSide : THREE.FrontSide,
|
|
331
|
+
...(this._config.emissiveIntensity
|
|
332
|
+
? { emissive: color, emissiveIntensity: this._config.emissiveIntensity }
|
|
333
|
+
: {}),
|
|
334
|
+
},
|
|
323
335
|
color,
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
transparent: opacity < 1,
|
|
328
|
-
side: opacity < 1 ? THREE.DoubleSide : THREE.FrontSide,
|
|
329
|
-
...(this._config.emissiveIntensity ? { emissive: color, emissiveIntensity: this._config.emissiveIntensity } : {}),
|
|
330
|
-
})
|
|
331
|
-
// z-fighting 방지: 바닥면(floor grid)보다 항상 앞에 렌더링
|
|
336
|
+
)
|
|
337
|
+
// z-fighting 방지: 바닥면(floor grid)보다 앞. bank body 는 공유 base 라 polygonOffset 은 모든 mcs
|
|
338
|
+
// body 공통 상수 — 반환 material 에 세팅(같은 값이라 멱등, 인스턴스별 발산 없음).
|
|
332
339
|
material.polygonOffset = true
|
|
333
340
|
material.polygonOffsetFactor = -1
|
|
334
341
|
material.polygonOffsetUnit = -1
|
|
@@ -360,6 +367,11 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
360
367
|
this._ownMesh.castShadow = !isPlane
|
|
361
368
|
this._ownMesh.receiveShadow = true
|
|
362
369
|
this.components3D.add(this._ownMesh)
|
|
370
|
+
|
|
371
|
+
// mesh 는 super.build() *이후* 생성되므로, 코어의 build-시점 faint 적용이 이 mesh 에 못 닿는다.
|
|
372
|
+
// mesh 생성 직후 명시 적용 — 코어 applyMaterialOpacity 가 content-alpha × faint 를 일괄 처리.
|
|
373
|
+
// view 모드에선 factor=1(=state.alpha 없음) → canonical(config.opacity) 그대로, 고객 화면 불변.
|
|
374
|
+
this.updateAlpha()
|
|
363
375
|
}
|
|
364
376
|
|
|
365
377
|
// ── Change handling ──
|
|
@@ -375,7 +387,8 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
375
387
|
'strokeStyle' in after ||
|
|
376
388
|
'id' in after ||
|
|
377
389
|
'EMPTYTYPE' in after ||
|
|
378
|
-
'CARRIERSTATUS' in after
|
|
390
|
+
'CARRIERSTATUS' in after ||
|
|
391
|
+
'showText' in after
|
|
379
392
|
) {
|
|
380
393
|
// 연속 setState에 의한 다중 rebuild 방지: 한 프레임에 한 번만 실행
|
|
381
394
|
if (!this._updatePending) {
|
|
@@ -415,8 +428,9 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
415
428
|
ctx.fillStyle = statusColor
|
|
416
429
|
ctx.fillRect(0, 0, usedW, usedH)
|
|
417
430
|
|
|
418
|
-
// 텍스트 (사용 영역 중앙)
|
|
419
|
-
|
|
431
|
+
// 텍스트 (사용 영역 중앙) — showText(= 현재 홀더의 showCarrierText) off 면 미표시. 2D 와 동일.
|
|
432
|
+
const showText = !!(this.component.state as any)?.showText
|
|
433
|
+
if (text && showText) {
|
|
420
434
|
const fontSize = Math.round(Math.min(usedW, usedH) * 0.7)
|
|
421
435
|
ctx.font = `bold ${fontSize}px Arial`
|
|
422
436
|
ctx.textAlign = 'center'
|
|
@@ -468,22 +482,10 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
468
482
|
// RealObjectGroup 기본 동작 비활성화
|
|
469
483
|
updateDimension() {}
|
|
470
484
|
|
|
471
|
-
updateAlpha()
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
this.object3d.traverse((o: any) => {
|
|
477
|
-
if (!o.material) return
|
|
478
|
-
const materials = Array.isArray(o.material) ? o.material : [o.material]
|
|
479
|
-
for (const m of materials) {
|
|
480
|
-
if (!m.visible) continue // openTop invisible face 무시
|
|
481
|
-
if (m.map) continue // 텍스처 material은 자체 transparent 유지
|
|
482
|
-
m.opacity = effective
|
|
483
|
-
m.transparent = effective < 1
|
|
484
|
-
}
|
|
485
|
-
})
|
|
486
|
-
}
|
|
485
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.
|
|
486
|
+
// 코어가 canonical(=build 시 config.opacity 로 만든 material) 위에 factor = state.alpha × faint 를
|
|
487
|
+
// 공유 변형 스왑으로 일괄 적용한다(원본 base 불변). 기존 in-place 변이는 (1) faint 미적용,
|
|
488
|
+
// (2) 코어 applyFocusDim 과 이중 적용 소지가 있어 방언이었다. config.opacity 는 canonical 로 보존.
|
|
487
489
|
}
|
|
488
490
|
|
|
489
491
|
// ── 헬퍼 ────────────────────────────────────────────────
|
|
@@ -573,11 +575,9 @@ class OHTLineRealObject3D extends RealObjectGroup {
|
|
|
573
575
|
const webH = beamH - flangeH * 2 // 웹 높이
|
|
574
576
|
const webW = Math.max(flangeW * 0.25, 1) // 웹 두께
|
|
575
577
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
roughness: 0.4,
|
|
580
|
-
})
|
|
578
|
+
// 공유 base(MaterialBank) — OHTLine rail 은 정적 색이라 전 인스턴스 dedup. faint 는 코어가
|
|
579
|
+
// 공유 base 의 변형을 스왑(원본 불변). dispose 는 bank-safe(update 에서 material.dispose 안 함).
|
|
580
|
+
const mat = MaterialBank.material('oht-line|rail', { metalness: 0.5, roughness: 0.4 }, OHT_LINE_RAIL_COLOR)
|
|
581
581
|
|
|
582
582
|
const geoms: THREE.BufferGeometry[] = []
|
|
583
583
|
|
|
@@ -605,13 +605,16 @@ class OHTLineRealObject3D extends RealObjectGroup {
|
|
|
605
605
|
this._mesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(geoms), mat)
|
|
606
606
|
this._mesh.castShadow = true
|
|
607
607
|
this.object3d.add(this._mesh)
|
|
608
|
+
|
|
609
|
+
// mesh 가 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
610
|
+
this.updateAlpha()
|
|
608
611
|
}
|
|
609
612
|
|
|
610
613
|
update() {
|
|
611
614
|
if (this._mesh) {
|
|
612
615
|
this.object3d.remove(this._mesh)
|
|
613
616
|
this._mesh.geometry.dispose()
|
|
614
|
-
|
|
617
|
+
// material 은 MaterialBank 공유(앱 수명) — 여기서 dispose 하면 다른 인스턴스가 깨진다.
|
|
615
618
|
this._mesh = undefined
|
|
616
619
|
}
|
|
617
620
|
this.build()
|
|
@@ -628,8 +631,8 @@ class OHTLineRealObject3D extends RealObjectGroup {
|
|
|
628
631
|
super.onchange(after, before)
|
|
629
632
|
}
|
|
630
633
|
|
|
634
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴(faint).
|
|
631
635
|
updateDimension() {}
|
|
632
|
-
updateAlpha() {}
|
|
633
636
|
}
|
|
634
637
|
|
|
635
638
|
registerRealObjectFactory('OHTLine', (component: Component) => new OHTLineRealObject3D(component))
|
|
@@ -702,24 +705,32 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
702
705
|
ctx.lineWidth = 12
|
|
703
706
|
ctx.strokeRect(6, 6, canvasW - 12, canvasH - 12)
|
|
704
707
|
|
|
705
|
-
// 세그먼트 채움 (왼→오)
|
|
708
|
+
// 세그먼트 채움 (왼→오) + carrier/abnormal 은 세그먼트별 숫자(%) 표기 (2D 와 동일)
|
|
706
709
|
const innerW = canvasW - pad * 2
|
|
707
710
|
const innerH = canvasH - pad * 2
|
|
711
|
+
// 컴포넌트 폰트 설정(fontFamily/bold/italic) 을 그대로, 크기만 텍스처 scale 배 (2D 와 상대 크기 일치).
|
|
712
|
+
const fontSize = (Number((this.component as any).state?.fontSize) || 15) * scale
|
|
713
|
+
const font = this.gaugeFont(fontSize)
|
|
708
714
|
let x = pad
|
|
709
715
|
for (const seg of gauge.segments) {
|
|
710
716
|
const segW = Math.max(innerW * seg.ratio, seg.ratio > 0 ? 1 : 0)
|
|
711
717
|
if (segW <= 0) continue
|
|
712
718
|
ctx.fillStyle = seg.color
|
|
713
719
|
ctx.fillRect(x, pad, segW, innerH)
|
|
720
|
+
if (gauge.perSegment) {
|
|
721
|
+
this.drawGaugeSegmentNumber(ctx, seg.ratio, x, pad, segW, innerH, font, fontSize)
|
|
722
|
+
}
|
|
714
723
|
x += segW
|
|
715
724
|
}
|
|
716
725
|
|
|
717
|
-
//
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
726
|
+
// capacity: 전체 중앙에 usage% 한 개 (2D 와 동일). carrier/abnormal 은 위 세그먼트별 숫자 사용.
|
|
727
|
+
if (gauge.label) {
|
|
728
|
+
ctx.font = font
|
|
729
|
+
ctx.textAlign = 'center'
|
|
730
|
+
ctx.textBaseline = 'middle'
|
|
731
|
+
ctx.fillStyle = '#333333'
|
|
732
|
+
ctx.fillText(gauge.label, canvasW / 2, canvasH / 2)
|
|
733
|
+
}
|
|
723
734
|
|
|
724
735
|
// 텍스처 → Plane
|
|
725
736
|
const texture = new THREE.CanvasTexture(canvas)
|
|
@@ -744,34 +755,45 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
744
755
|
this.components3D.add(this.gaugeMesh)
|
|
745
756
|
}
|
|
746
757
|
|
|
747
|
-
private resolveGauge(
|
|
758
|
+
private resolveGauge(
|
|
759
|
+
data: any,
|
|
760
|
+
mode: string
|
|
761
|
+
): { segments: { ratio: number; color: string }[]; borderColor: string; label: string | null; perSegment: boolean } {
|
|
748
762
|
switch (mode) {
|
|
749
763
|
case 'carrier': {
|
|
750
|
-
// 세그먼트 비율/색상은 2D 와 공유되는 단일 소스에서 계산 (필드/색상 drift 방지)
|
|
751
|
-
|
|
764
|
+
// 세그먼트 비율/색상은 2D 와 공유되는 단일 소스에서 계산 (필드/색상 drift 방지).
|
|
765
|
+
// 색상은 carrier 컴포넌트와 동일한 legend(테마)에서 취득.
|
|
766
|
+
const colors = carrierColorsFromLegend(resolveCarrierLegend(this.component))
|
|
767
|
+
const [f, e, ee] = carrierSegmentsFromData(data, colors)
|
|
752
768
|
const total = f.count + e.count + ee.count
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
769
|
+
// 접두사 없는 세그먼트별 숫자(%)로 표기 (capacity·2D 와 동일). label(중앙 한 개) 미사용.
|
|
770
|
+
return total > 0
|
|
771
|
+
? {
|
|
772
|
+
segments: [f, e, ee].map(s => ({ ratio: s.ratio, color: s.color })),
|
|
773
|
+
borderColor: colors.full,
|
|
774
|
+
label: null,
|
|
775
|
+
perSegment: true,
|
|
776
|
+
}
|
|
777
|
+
: { segments: [{ ratio: 1, color: '#dddddd' }], borderColor: colors.full, label: null, perSegment: false }
|
|
761
778
|
}
|
|
762
779
|
case 'abnormal': {
|
|
763
780
|
const [n, a] = abnormalSegmentsFromData(data)
|
|
764
781
|
const total = n.count + a.count
|
|
765
|
-
|
|
766
|
-
?
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
782
|
+
return total > 0
|
|
783
|
+
? {
|
|
784
|
+
segments: [n, a].map(s => ({ ratio: s.ratio, color: s.color })),
|
|
785
|
+
borderColor: a.count > 0 ? ABNORMAL_COLORS.abnormal : ABNORMAL_COLORS.normal,
|
|
786
|
+
label: null,
|
|
787
|
+
perSegment: true,
|
|
788
|
+
}
|
|
789
|
+
: {
|
|
790
|
+
segments: [{ ratio: 1, color: '#dddddd' }],
|
|
791
|
+
borderColor: ABNORMAL_COLORS.normal,
|
|
792
|
+
label: null,
|
|
793
|
+
perSegment: false,
|
|
794
|
+
}
|
|
773
795
|
}
|
|
774
|
-
default: { // capacity
|
|
796
|
+
default: { // capacity — 전체 중앙에 usage% 한 개
|
|
775
797
|
const cur = Number(data.CURRENTCAPACITY) || 0
|
|
776
798
|
const max = Number(data.MAXCAPACITY) || 100
|
|
777
799
|
const fullup = data.FULLUP === 'T'
|
|
@@ -782,11 +804,48 @@ class StockerRealObject3D extends MCSRealObject3D {
|
|
|
782
804
|
segments: [{ ratio: usage / 100, color }],
|
|
783
805
|
borderColor: color,
|
|
784
806
|
label: `${usage}%`,
|
|
807
|
+
perSegment: false,
|
|
785
808
|
}
|
|
786
809
|
}
|
|
787
810
|
}
|
|
788
811
|
}
|
|
789
812
|
|
|
813
|
+
/** 컴포넌트 폰트 설정(fontFamily/bold/italic)으로 CSS font 문자열 조합. size 는 텍스처 스케일 반영값. */
|
|
814
|
+
private gaugeFont(sizePx: number): string {
|
|
815
|
+
const st = (this.component as any).state || {}
|
|
816
|
+
const parts: string[] = []
|
|
817
|
+
if (st.bold) parts.push('bold')
|
|
818
|
+
if (st.italic) parts.push('italic')
|
|
819
|
+
parts.push(`${sizePx}px`)
|
|
820
|
+
parts.push(st.fontFamily || 'sans-serif')
|
|
821
|
+
return parts.join(' ')
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/** 세그먼트 영역 중심에 숫자(%)만 표기. 좁으면 값% → 값 → 생략 (2D _drawSegmentLabel 과 동일 정책). */
|
|
825
|
+
private drawGaugeSegmentNumber(
|
|
826
|
+
ctx: CanvasRenderingContext2D,
|
|
827
|
+
ratio: number,
|
|
828
|
+
x: number,
|
|
829
|
+
y: number,
|
|
830
|
+
w: number,
|
|
831
|
+
h: number,
|
|
832
|
+
font: string,
|
|
833
|
+
fontSize: number
|
|
834
|
+
) {
|
|
835
|
+
ctx.font = font
|
|
836
|
+
ctx.textAlign = 'center'
|
|
837
|
+
ctx.textBaseline = 'middle'
|
|
838
|
+
ctx.fillStyle = '#333333'
|
|
839
|
+
const value = `${Math.round(ratio * 100)}`
|
|
840
|
+
const cx = x + w / 2
|
|
841
|
+
const cy = y + h / 2
|
|
842
|
+
if (fontSize <= h && ctx.measureText(`${value}%`).width <= w) {
|
|
843
|
+
ctx.fillText(`${value}%`, cx, cy)
|
|
844
|
+
} else if (fontSize <= h && ctx.measureText(value).width <= w) {
|
|
845
|
+
ctx.fillText(value, cx, cy)
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
790
849
|
update() {
|
|
791
850
|
this.clearGauge()
|
|
792
851
|
super.update()
|
package/src/factories/oht-3d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import * as THREE from 'three'
|
|
13
13
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
|
14
14
|
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js'
|
|
15
|
-
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene'
|
|
15
|
+
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene'
|
|
16
16
|
import type { Component } from '@hatiolab/things-scene'
|
|
17
17
|
import { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'
|
|
18
18
|
|
|
@@ -77,11 +77,8 @@ export class OHT3D extends RealObjectGroup {
|
|
|
77
77
|
const armThickness = Math.min(w, h) * 0.12
|
|
78
78
|
const bodyCornerR = Math.min(w, h, bodyH) * 0.1
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
roughness: 0.5,
|
|
83
|
-
metalness: 0.4,
|
|
84
|
-
})
|
|
80
|
+
// MaterialBank 공유 — dedup + faint 안전(코어 변형 스왑, disposeObject3D 가 __bankOwned 스킵).
|
|
81
|
+
const frameMat = MaterialBank.material('oht|frame', { roughness: 0.5, metalness: 0.4 }, FRAME_COLOR)
|
|
85
82
|
|
|
86
83
|
// ── 1. 하단 그리퍼 바 (캐리어를 집는 가로 바 — 이동 방향과 직교) ──
|
|
87
84
|
const gripperBarW = w * 0.7
|
|
@@ -116,11 +113,7 @@ export class OHT3D extends RealObjectGroup {
|
|
|
116
113
|
const bodyBaseY = armBaseY + armH
|
|
117
114
|
const bodyColor = this.resolveColor()
|
|
118
115
|
const bodyGeo = new RoundedBoxGeometry(w, bodyH, h, 4, bodyCornerR)
|
|
119
|
-
const bodyMat =
|
|
120
|
-
color: bodyColor,
|
|
121
|
-
roughness: 0.3,
|
|
122
|
-
metalness: 0.5,
|
|
123
|
-
})
|
|
116
|
+
const bodyMat = MaterialBank.material('oht|body', { roughness: 0.3, metalness: 0.5 }, bodyColor)
|
|
124
117
|
const bodyMesh = new THREE.Mesh(bodyGeo, bodyMat)
|
|
125
118
|
bodyMesh.position.y = bodyBaseY + bodyH / 2
|
|
126
119
|
bodyMesh.castShadow = true
|
|
@@ -131,11 +124,7 @@ export class OHT3D extends RealObjectGroup {
|
|
|
131
124
|
const trolleyW = w * 0.6
|
|
132
125
|
const trolleyD = h * 1.15
|
|
133
126
|
const trolleyGeo = new THREE.BoxGeometry(trolleyW, trolleyH, trolleyD)
|
|
134
|
-
const trolleyMat =
|
|
135
|
-
color: TROLLEY_COLOR,
|
|
136
|
-
roughness: 0.6,
|
|
137
|
-
metalness: 0.3,
|
|
138
|
-
})
|
|
127
|
+
const trolleyMat = MaterialBank.material('oht|trolley', { roughness: 0.6, metalness: 0.3 }, TROLLEY_COLOR)
|
|
139
128
|
const trolleyMesh = new THREE.Mesh(trolleyGeo, trolleyMat)
|
|
140
129
|
trolleyMesh.position.y = trolleyBaseY + trolleyH / 2
|
|
141
130
|
trolleyMesh.castShadow = true
|
|
@@ -156,6 +145,9 @@ export class OHT3D extends RealObjectGroup {
|
|
|
156
145
|
|
|
157
146
|
this._ownMeshes = [gripperMesh, armMesh, bodyMesh, trolleyMesh, ...wheelMeshes]
|
|
158
147
|
for (const m of this._ownMeshes) this.components3D.add(m)
|
|
148
|
+
|
|
149
|
+
// mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
150
|
+
this.updateAlpha()
|
|
159
151
|
}
|
|
160
152
|
|
|
161
153
|
updateDimension() {}
|
|
@@ -172,16 +164,8 @@ export class OHT3D extends RealObjectGroup {
|
|
|
172
164
|
this.updateHidden()
|
|
173
165
|
}
|
|
174
166
|
|
|
175
|
-
updateAlpha()
|
|
176
|
-
|
|
177
|
-
for (const m of this._ownMeshes) {
|
|
178
|
-
const materials = Array.isArray(m.material) ? m.material : [m.material]
|
|
179
|
-
for (const mat of materials) {
|
|
180
|
-
;(mat as THREE.MeshStandardMaterial).opacity = alpha
|
|
181
|
-
;(mat as THREE.MeshStandardMaterial).transparent = alpha < 1
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
167
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.
|
|
168
|
+
// canonical(bank base opacity 1) × state.alpha × faint. view 모드 불변, 편집 모드 faint 추가.
|
|
185
169
|
|
|
186
170
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>) {
|
|
187
171
|
if ('width' in after || 'height' in after || 'depth' in after ||
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import * as THREE from 'three'
|
|
14
14
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
|
15
|
-
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene'
|
|
15
|
+
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene'
|
|
16
16
|
import type { Component } from '@hatiolab/things-scene'
|
|
17
17
|
import { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'
|
|
18
18
|
|
|
@@ -68,11 +68,9 @@ export class Shuttle3D extends RealObjectGroup {
|
|
|
68
68
|
// ── 1. 하단 베이스 플레이트 (납작하고 넓은 주행부) ──
|
|
69
69
|
const basePad = Math.min(w, h) * 0.05
|
|
70
70
|
const baseGeo = new THREE.BoxGeometry(w + basePad * 2, baseH, h + basePad * 2)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
metalness: 0.3,
|
|
75
|
-
})
|
|
71
|
+
// MaterialBank 공유 — 정적/상태색 dedup + faint 안전(코어가 공유 base 변형 스왑). dispose 는
|
|
72
|
+
// disposeObject3D 가 __bankOwned 를 건너뛰어 안전.
|
|
73
|
+
const baseMat = MaterialBank.material('shuttle|base', { roughness: 0.7, metalness: 0.3 }, BASE_COLOR)
|
|
76
74
|
const baseMesh = new THREE.Mesh(baseGeo, baseMat)
|
|
77
75
|
baseMesh.position.y = baseH / 2
|
|
78
76
|
baseMesh.castShadow = true
|
|
@@ -81,11 +79,7 @@ export class Shuttle3D extends RealObjectGroup {
|
|
|
81
79
|
// ── 2. 좌우 마스트 기둥 (statusColor 적용) ──
|
|
82
80
|
const mastBaseY = baseH
|
|
83
81
|
const bodyColor = this.resolveColor()
|
|
84
|
-
const mastMat =
|
|
85
|
-
color: bodyColor,
|
|
86
|
-
roughness: 0.4,
|
|
87
|
-
metalness: 0.5,
|
|
88
|
-
})
|
|
82
|
+
const mastMat = MaterialBank.material('shuttle|mast', { roughness: 0.4, metalness: 0.5 }, bodyColor)
|
|
89
83
|
|
|
90
84
|
const mastGeometries: THREE.BufferGeometry[] = []
|
|
91
85
|
|
|
@@ -108,11 +102,7 @@ export class Shuttle3D extends RealObjectGroup {
|
|
|
108
102
|
|
|
109
103
|
// ── 3. 상단 포크 (2개의 평행 암 + 백 플레이트) ──
|
|
110
104
|
const forkBaseY = mastBaseY + mastH
|
|
111
|
-
const forkMat =
|
|
112
|
-
color: FORK_COLOR,
|
|
113
|
-
roughness: 0.4,
|
|
114
|
-
metalness: 0.6,
|
|
115
|
-
})
|
|
105
|
+
const forkMat = MaterialBank.material('shuttle|fork', { roughness: 0.4, metalness: 0.6 }, FORK_COLOR)
|
|
116
106
|
|
|
117
107
|
const forkGeometries: THREE.BufferGeometry[] = []
|
|
118
108
|
|
|
@@ -139,6 +129,9 @@ export class Shuttle3D extends RealObjectGroup {
|
|
|
139
129
|
|
|
140
130
|
this._ownMeshes = [baseMesh, mastMesh, forkMesh]
|
|
141
131
|
for (const m of this._ownMeshes) this.components3D.add(m)
|
|
132
|
+
|
|
133
|
+
// mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
134
|
+
this.updateAlpha()
|
|
142
135
|
}
|
|
143
136
|
|
|
144
137
|
updateDimension() {}
|
|
@@ -155,16 +148,9 @@ export class Shuttle3D extends RealObjectGroup {
|
|
|
155
148
|
this.updateHidden()
|
|
156
149
|
}
|
|
157
150
|
|
|
158
|
-
updateAlpha()
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const materials = Array.isArray(m.material) ? m.material : [m.material]
|
|
162
|
-
for (const mat of materials) {
|
|
163
|
-
;(mat as THREE.MeshStandardMaterial).opacity = alpha
|
|
164
|
-
;(mat as THREE.MeshStandardMaterial).transparent = alpha < 1
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
151
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.
|
|
152
|
+
// 코어가 canonical(=bank base, opacity 1) 위에 state.alpha × faint 를 일괄. view 모드는
|
|
153
|
+
// 1×state.alpha = 기존 in-place 결과와 동일 → 불변, 편집 모드만 faint 추가.
|
|
168
154
|
|
|
169
155
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>) {
|
|
170
156
|
if ('width' in after || 'height' in after || 'depth' in after ||
|