@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
package/src/carrier.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { getPopupData } from '@fmsim/api'
|
|
|
4
4
|
import { BOUNDS, Component, ComponentNature, Properties, Shape, sceneComponent } from '@hatiolab/things-scene'
|
|
5
5
|
import { ANIMATION_DEFAULT, AnimationPreset, AnimationConfig } from './features/animation-default.js'
|
|
6
6
|
import { ParentObjectMixin } from './features/parent-object-mixin.js'
|
|
7
|
-
import {
|
|
7
|
+
import { Legend, resolveCarrierLegend } from './features/mcs-status-default.js'
|
|
8
8
|
import { MCSStatusMixin } from './features/mcs-status-mixin.js'
|
|
9
9
|
|
|
10
10
|
const NATURE: ComponentNature = {
|
|
@@ -56,6 +56,8 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
|
|
|
56
56
|
|
|
57
57
|
set path(path) {}
|
|
58
58
|
|
|
59
|
+
/** 마지막으로 적용한 (애니메이션 테마 | CARRIERSTATUS) 조합. 같은 조합 재적용을 막는다. */
|
|
60
|
+
private _appliedAnimationKey?: string
|
|
59
61
|
|
|
60
62
|
added(parent: any) {
|
|
61
63
|
super.added(parent)
|
|
@@ -74,6 +76,11 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
|
|
|
74
76
|
|
|
75
77
|
this.syncStateFromData()
|
|
76
78
|
|
|
79
|
+
/* 부모가 정해진 지금 다시 해석한다 — 생성 시(data 선할당) 판정에는 parent 가 없어
|
|
80
|
+
* 홀더의 carrierAnimationName 이 반영되지 않았다. reparent 로 홀더가 바뀐 경우도 여기서
|
|
81
|
+
* 새 홀더 테마로 갱신된다. (같은 조합이면 내부 가드로 no-op) */
|
|
82
|
+
this._applyStatusAnimation()
|
|
83
|
+
|
|
77
84
|
// 3D 오브젝트 최초 생성 (동기적 — reparent()에서 _realObject 필요)
|
|
78
85
|
if ((this.root as any)?.threed && !this._realObject) {
|
|
79
86
|
;(this.root as any).model_layer?._threeCapability?.addObject(this)
|
|
@@ -102,52 +109,60 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
|
|
|
102
109
|
this.setState('CARRIERTYPE', CARRIERTYPE)
|
|
103
110
|
this.setState('CARRIERSTATUS', CARRIERSTATUS)
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (lastAnimationConfig) {
|
|
111
|
-
let { animation, decorator, border, arrow } = lastAnimationConfig
|
|
112
|
-
if (animation) {
|
|
113
|
-
this.resetAnimation()
|
|
114
|
-
this.setState('animation', {
|
|
115
|
-
oncreate: null
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (decorator || border || arrow) {
|
|
120
|
-
this.trigger('deco-off')
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const animationConfig = this.getAnimationConfig(this.getState('CARRIERSTATUS'))
|
|
112
|
+
/* 상태에 매핑된 애니메이션/데코 적용. 조건 판정은 _applyStatusAnimation 내부의
|
|
113
|
+
* (테마|상태) 키 비교로 하므로 여기서 status 변경 여부를 따지지 않는다 —
|
|
114
|
+
* 상태는 그대로여도 홀더가 바뀌어(=테마가 바뀌어) 재적용이 필요할 수 있다. */
|
|
115
|
+
this._applyStatusAnimation()
|
|
116
|
+
}
|
|
125
117
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
/**
|
|
119
|
+
* 현재 (해석된 애니메이션 테마 | CARRIERSTATUS) 조합에 맞는 애니메이션/데코를 적용한다.
|
|
120
|
+
*
|
|
121
|
+
* 같은 조합이면 아무것도 하지 않는다 — data 는 매 update 마다 재할당되므로, 가드가 없으면
|
|
122
|
+
* 매번 애니메이션이 재시작되어 끊긴다.
|
|
123
|
+
*
|
|
124
|
+
* 호출 시점이 둘인 이유:
|
|
125
|
+
* 1) onchangeData — 상태(또는 테마) 변화 반영
|
|
126
|
+
* 2) added() — **부모가 생긴 직후**. CarrierManager 는 `carrier.data = item` 을
|
|
127
|
+
* addComponent 보다 먼저 하므로, 생성 시 첫 판정에는 parent 가 없어 홀더의
|
|
128
|
+
* carrierAnimationName 이 무시된다(루트 폴백만 보임). 부모가 붙은 뒤 한 번 더
|
|
129
|
+
* 해석해야 홀더 테마가 반영된다.
|
|
130
|
+
*/
|
|
131
|
+
private _applyStatusAnimation(): void {
|
|
132
|
+
const status = this.getState('CARRIERSTATUS')
|
|
133
|
+
const key = `${this.resolvedCarrierAnimationName}|${status ?? ''}`
|
|
134
|
+
if (key === this._appliedAnimationKey) return
|
|
135
|
+
this._appliedAnimationKey = key
|
|
136
|
+
|
|
137
|
+
/* 이전 적용분 해제 — 이전 테마/상태를 되짚지 않고 끈다(끄는 동작은 멱등).
|
|
138
|
+
* 테마가 바뀐 경우 이전 config 로는 무엇이 켜져 있었는지 정확히 알 수 없다. */
|
|
139
|
+
this.resetAnimation()
|
|
140
|
+
this.setState('animation', { oncreate: null })
|
|
141
|
+
this.trigger('deco-off')
|
|
142
|
+
|
|
143
|
+
const animationConfig = this.getAnimationConfig(status)
|
|
144
|
+
if (!animationConfig) return
|
|
145
|
+
|
|
146
|
+
const { animation, decorator, border, arrow } = animationConfig
|
|
147
|
+
|
|
148
|
+
if (animation) {
|
|
149
|
+
this.setState('animation', { oncreate: animation })
|
|
150
|
+
this.started = true
|
|
151
|
+
}
|
|
135
152
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
153
|
+
if (decorator) {
|
|
154
|
+
this.trigger('deco-icon-off')
|
|
155
|
+
this.trigger('deco-icon', decorator)
|
|
156
|
+
}
|
|
140
157
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
158
|
+
if (border) {
|
|
159
|
+
this.trigger('deco-border-off')
|
|
160
|
+
this.trigger('deco-border', border)
|
|
161
|
+
}
|
|
145
162
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
}
|
|
163
|
+
if (arrow) {
|
|
164
|
+
this.trigger('deco-arrow-off')
|
|
165
|
+
this.trigger('deco-arrow', arrow)
|
|
151
166
|
}
|
|
152
167
|
}
|
|
153
168
|
|
|
@@ -156,13 +171,8 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
|
|
|
156
171
|
}
|
|
157
172
|
|
|
158
173
|
get legend(): Legend {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (carrierLegendName) {
|
|
162
|
-
return (this.root as any)?.style[carrierLegendName]
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return (this.root as any)?.carrierLegendTheme || LEGEND_CARRIER
|
|
174
|
+
// carrier·스토커 게이지(2D/3D) 공유 단일 소스.
|
|
175
|
+
return resolveCarrierLegend(this)
|
|
166
176
|
}
|
|
167
177
|
|
|
168
178
|
getAnimationConfig(carrierStatus): AnimationConfig | null {
|
|
@@ -170,9 +180,20 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
|
|
|
170
180
|
return (config as AnimationConfig) || null
|
|
171
181
|
}
|
|
172
182
|
|
|
183
|
+
/**
|
|
184
|
+
* 적용할 애니메이션 테마 이름 — 소유 홀더(부모) 설정이 우선, 없으면 보드(rootModel) 폴백.
|
|
185
|
+
*
|
|
186
|
+
* 부모가 아직 없는 시점(생성 직후 data 선할당)에는 홀더 설정을 알 수 없어 루트 폴백만
|
|
187
|
+
* 보인다. 그래서 added() 에서 한 번 더 해석한다 — _applyStatusAnimation 참조.
|
|
188
|
+
*/
|
|
189
|
+
get resolvedCarrierAnimationName(): string {
|
|
190
|
+
return (
|
|
191
|
+
this.parent?.state.carrierAnimationName || (this.root as any)?.rootModel?.state?.carrierAnimationName || ''
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
173
195
|
get animationPreset(): AnimationPreset {
|
|
174
|
-
const carrierAnimationName = this.
|
|
175
|
-
this.root?.rootModel.state.carrierAnimationName || ''
|
|
196
|
+
const carrierAnimationName = this.resolvedCarrierAnimationName
|
|
176
197
|
|
|
177
198
|
if (carrierAnimationName) {
|
|
178
199
|
// 캐시에서 먼저 확인
|
package/src/crane.ts
CHANGED
|
@@ -165,10 +165,12 @@ export default class Crane extends MCSVehicle {
|
|
|
165
165
|
* cranePosition (current + limit) → state.left/top + forkZ.
|
|
166
166
|
*
|
|
167
167
|
* 부모 오브젝트 (보통은 Stocker 이지만 다른 컨테이너일 수도) 의 width/height
|
|
168
|
-
* 만 사용해 좌표 매핑 — 타입 무관 일반화.
|
|
169
|
-
*
|
|
170
|
-
* - current.
|
|
171
|
-
* - current.
|
|
168
|
+
* 만 사용해 좌표 매핑 — 타입 무관 일반화. 축 배정과 원점은 host 의 내부 좌표계 설정을
|
|
169
|
+
* 따른다 (coordAxisX / coordInvertX / coordInvertY, 미설정 시 아래 폴백):
|
|
170
|
+
* - current.x → X축 (기본: host 의 긴쪽, 자동 판별 max(width, height))
|
|
171
|
+
* - current.y → Y축 (기본: 짧은쪽)
|
|
172
|
+
* - current.z → forkZ (state 에 0~100 으로 저장, 3D 측에서 mast 비율로 매핑).
|
|
173
|
+
* Z 는 바닥 기준이 물리적으로 고정이라 반전 설정을 두지 않는다.
|
|
172
174
|
*
|
|
173
175
|
* 각 축 비율 = (current - limit.min) / (limit.max - limit.min) ∈ [0,1].
|
|
174
176
|
*
|
|
@@ -196,7 +198,14 @@ export default class Crane extends MCSVehicle {
|
|
|
196
198
|
const ry = axisRatio(cp.current?.y, cp.limit?.y)
|
|
197
199
|
const rz = axisRatio(cp.current?.z, cp.limit?.z)
|
|
198
200
|
|
|
199
|
-
|
|
201
|
+
// 축 배정/원점은 host(보통 STOCKER) 의 내부 좌표계 설정을 따른다. 타입을 묻지 않고
|
|
202
|
+
// state 만 읽어 어떤 컨테이너가 host 여도 동일하게 동작한다 — 미설정이면 종전
|
|
203
|
+
// 폴백(긴쪽=X, 원점 left/top).
|
|
204
|
+
const { left, top } = mapCraneLocalPosition(hw, hh, cw, ch, rx, ry, {
|
|
205
|
+
axisX: host.state?.coordAxisX,
|
|
206
|
+
invertX: !!host.state?.coordInvertX,
|
|
207
|
+
invertY: !!host.state?.coordInvertY
|
|
208
|
+
})
|
|
200
209
|
|
|
201
210
|
return {
|
|
202
211
|
left,
|
package/src/factories/agv-3d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import * as THREE from 'three'
|
|
17
17
|
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js'
|
|
18
|
-
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene'
|
|
18
|
+
import { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene'
|
|
19
19
|
import type { Component } from '@hatiolab/things-scene'
|
|
20
20
|
import { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'
|
|
21
21
|
|
|
@@ -76,11 +76,8 @@ export class AGV3D extends RealObjectGroup {
|
|
|
76
76
|
const bumperPad = Math.min(w, h) * 0.03
|
|
77
77
|
const bumperCornerR = Math.min(w + bumperPad * 2, h + bumperPad * 2, bumperH) * 0.2
|
|
78
78
|
const bumperGeo = new RoundedBoxGeometry(w + bumperPad * 2, bumperH, h + bumperPad * 2, 4, bumperCornerR)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
roughness: 0.8,
|
|
82
|
-
metalness: 0.1,
|
|
83
|
-
})
|
|
79
|
+
// MaterialBank 공유 — dedup + faint 안전(코어 변형 스왑, disposeObject3D 가 __bankOwned 스킵).
|
|
80
|
+
const bumperMat = MaterialBank.material('agv|bumper', { roughness: 0.8, metalness: 0.1 }, BUMPER_COLOR)
|
|
84
81
|
const bumper = new THREE.Mesh(bumperGeo, bumperMat)
|
|
85
82
|
bumper.position.y = bumperH / 2
|
|
86
83
|
bumper.castShadow = true
|
|
@@ -89,11 +86,7 @@ export class AGV3D extends RealObjectGroup {
|
|
|
89
86
|
// ── 본체 (둥근 모서리, statusColor 적용) ──
|
|
90
87
|
const bodyColor = this.resolveColor()
|
|
91
88
|
const bodyGeo = new RoundedBoxGeometry(w, bodyH, h, 4, cornerR)
|
|
92
|
-
const bodyMat =
|
|
93
|
-
color: bodyColor,
|
|
94
|
-
roughness: 0.3,
|
|
95
|
-
metalness: 0.5,
|
|
96
|
-
})
|
|
89
|
+
const bodyMat = MaterialBank.material('agv|body', { roughness: 0.3, metalness: 0.5 }, bodyColor)
|
|
97
90
|
const body = new THREE.Mesh(bodyGeo, bodyMat)
|
|
98
91
|
body.position.y = bumperH + bodyH / 2
|
|
99
92
|
body.castShadow = true
|
|
@@ -103,11 +96,7 @@ export class AGV3D extends RealObjectGroup {
|
|
|
103
96
|
const platInset = Math.min(w, h) * 0.04
|
|
104
97
|
const platCornerR = Math.min(w - platInset * 2, h - platInset * 2, platH) * 0.15
|
|
105
98
|
const platGeo = new RoundedBoxGeometry(w - platInset * 2, platH, h - platInset * 2, 4, platCornerR)
|
|
106
|
-
const platMat =
|
|
107
|
-
color: PLATFORM_COLOR,
|
|
108
|
-
roughness: 0.5,
|
|
109
|
-
metalness: 0.15,
|
|
110
|
-
})
|
|
99
|
+
const platMat = MaterialBank.material('agv|platform', { roughness: 0.5, metalness: 0.15 }, PLATFORM_COLOR)
|
|
111
100
|
const plat = new THREE.Mesh(platGeo, platMat)
|
|
112
101
|
plat.position.y = bumperH + bodyH + gapH + platH / 2
|
|
113
102
|
plat.castShadow = true
|
|
@@ -116,6 +105,9 @@ export class AGV3D extends RealObjectGroup {
|
|
|
116
105
|
this._ownMeshes = [bumper, body, plat]
|
|
117
106
|
// ownGroup이 center-origin ↔ bottom-up 좌표계 변환을 자동 처리
|
|
118
107
|
for (const m of this._ownMeshes) this.components3D.add(m)
|
|
108
|
+
|
|
109
|
+
// mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
110
|
+
this.updateAlpha()
|
|
119
111
|
}
|
|
120
112
|
|
|
121
113
|
updateDimension() {}
|
|
@@ -134,16 +126,8 @@ export class AGV3D extends RealObjectGroup {
|
|
|
134
126
|
this.updateHidden()
|
|
135
127
|
}
|
|
136
128
|
|
|
137
|
-
updateAlpha()
|
|
138
|
-
|
|
139
|
-
for (const m of this._ownMeshes) {
|
|
140
|
-
const materials = Array.isArray(m.material) ? m.material : [m.material]
|
|
141
|
-
for (const mat of materials) {
|
|
142
|
-
;(mat as THREE.MeshStandardMaterial).opacity = alpha
|
|
143
|
-
;(mat as THREE.MeshStandardMaterial).transparent = alpha < 1
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
129
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.
|
|
130
|
+
// canonical(bank base opacity 1) × state.alpha × faint. view 모드 불변, 편집 모드 faint 추가.
|
|
147
131
|
|
|
148
132
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>) {
|
|
149
133
|
if ('width' in after || 'height' in after || 'depth' in after ||
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import * as THREE from 'three'
|
|
15
15
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
|
16
|
-
import { RealObjectGroup, registerRealObjectFactory } from '@hatiolab/things-scene'
|
|
16
|
+
import { RealObjectGroup, registerRealObjectFactory, MaterialBank } from '@hatiolab/things-scene'
|
|
17
17
|
import type { Component } from '@hatiolab/things-scene'
|
|
18
18
|
import { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'
|
|
19
19
|
import { Z_FMSIM } from './z-priority'
|
|
@@ -85,11 +85,8 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
85
85
|
const railW = Math.max(beltWidth * 0.06, 2)
|
|
86
86
|
const legThickness = Math.max(railW * 0.8, 2)
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
metalness: 0.85,
|
|
91
|
-
roughness: 0.35,
|
|
92
|
-
})
|
|
88
|
+
// MaterialBank 공유(dedup). 자체 update() 없이 base 생명주기(guarded disposeObject3D) 상속 → 안전.
|
|
89
|
+
const frameMaterial = MaterialBank.material('conveyor|frame', { metalness: 0.85, roughness: 0.35 }, FRAME_COLOR)
|
|
93
90
|
|
|
94
91
|
// --- Frame: side rails + legs + cross-bracing ---
|
|
95
92
|
// Y 좌표: y=0(바닥) ~ y=depth(상단). 레일 상단이 depth에 위치.
|
|
@@ -179,6 +176,9 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
179
176
|
} else {
|
|
180
177
|
this.buildRollers(width, height, depth, frameH, railW, rollWidth, isVertical)
|
|
181
178
|
}
|
|
179
|
+
|
|
180
|
+
// mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
181
|
+
this.updateAlpha()
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
private buildRollers(
|
|
@@ -231,7 +231,7 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
231
231
|
if (rollerGeometries.length > 0) {
|
|
232
232
|
const rollerMesh = new THREE.Mesh(
|
|
233
233
|
BufferGeometryUtils.mergeGeometries(rollerGeometries),
|
|
234
|
-
|
|
234
|
+
MaterialBank.material('conveyor|roller', { metalness: 0.9, roughness: 0.2 }, color)
|
|
235
235
|
)
|
|
236
236
|
rollerMesh.castShadow = true
|
|
237
237
|
this.components3D.add(rollerMesh)
|
|
@@ -268,18 +268,14 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
268
268
|
|
|
269
269
|
const drumMesh = new THREE.Mesh(
|
|
270
270
|
BufferGeometryUtils.mergeGeometries(drumGeometries),
|
|
271
|
-
|
|
271
|
+
MaterialBank.material('conveyor|drum', { metalness: 0.9, roughness: 0.2 }, ROLLER_COLOR)
|
|
272
272
|
)
|
|
273
273
|
drumMesh.castShadow = true
|
|
274
274
|
this.components3D.add(drumMesh)
|
|
275
275
|
|
|
276
276
|
// Belt surface
|
|
277
277
|
const beltThickness = drumRadius * 0.12
|
|
278
|
-
const beltMaterial =
|
|
279
|
-
color: color,
|
|
280
|
-
metalness: 0.0,
|
|
281
|
-
roughness: 0.9,
|
|
282
|
-
})
|
|
278
|
+
const beltMaterial = MaterialBank.material('conveyor|belt', { metalness: 0.0, roughness: 0.9 }, color)
|
|
283
279
|
|
|
284
280
|
// Flat belt top surface spanning between drums
|
|
285
281
|
const flatLen = drum2 - drum1
|
|
@@ -347,11 +343,7 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
347
343
|
// 두 레일 간격: 내부 폭의 60%
|
|
348
344
|
const railSpacing = innerWidth * 0.6
|
|
349
345
|
|
|
350
|
-
const railMaterial =
|
|
351
|
-
color,
|
|
352
|
-
metalness: 0.7,
|
|
353
|
-
roughness: 0.3,
|
|
354
|
-
})
|
|
346
|
+
const railMaterial = MaterialBank.material('conveyor|rail', { metalness: 0.7, roughness: 0.3 }, color)
|
|
355
347
|
|
|
356
348
|
const railGeometries: THREE.BufferGeometry[] = []
|
|
357
349
|
|
|
@@ -376,7 +368,8 @@ export class Conveyor3D extends RealObjectGroup {
|
|
|
376
368
|
}
|
|
377
369
|
|
|
378
370
|
updateDimension() {}
|
|
379
|
-
updateAlpha()
|
|
371
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴(faint).
|
|
372
|
+
// view 모드는 factor=1 → build-시 opacity 그대로(불변), 편집 모드만 faint 추가.
|
|
380
373
|
|
|
381
374
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>) {
|
|
382
375
|
if (
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import * as THREE from 'three'
|
|
15
15
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
|
16
|
-
import { RealObjectGroup, registerRealObjectFactory } from '@hatiolab/things-scene'
|
|
16
|
+
import { RealObjectGroup, registerRealObjectFactory, MaterialBank } from '@hatiolab/things-scene'
|
|
17
17
|
import type { Component } from '@hatiolab/things-scene'
|
|
18
18
|
import { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'
|
|
19
19
|
|
|
@@ -84,11 +84,8 @@ export class Crane3D extends RealObjectGroup {
|
|
|
84
84
|
|
|
85
85
|
const pillarThick = Math.min(w, h) * 0.12
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
roughness: 0.5,
|
|
90
|
-
metalness: 0.5,
|
|
91
|
-
})
|
|
87
|
+
// MaterialBank 공유(dedup). 자체 update() 없이 base 생명주기(guarded disposeObject3D) 상속 → 안전.
|
|
88
|
+
const clampMat = MaterialBank.material('crane|clamp', { roughness: 0.5, metalness: 0.5 }, CLAMP_COLOR)
|
|
92
89
|
|
|
93
90
|
// ── 1. 하단 클램프 (바닥 레일에 물리는 바퀴부) ──
|
|
94
91
|
const clampW = w * 0.7
|
|
@@ -102,11 +99,7 @@ export class Crane3D extends RealObjectGroup {
|
|
|
102
99
|
// ── 2. 수직 마스트 (좌우 기둥 + 상·하 가로 연결 바) ──
|
|
103
100
|
const mastBaseY = clampH
|
|
104
101
|
const bodyColor = this.resolveColor()
|
|
105
|
-
const mastMat =
|
|
106
|
-
color: bodyColor,
|
|
107
|
-
roughness: 0.3,
|
|
108
|
-
metalness: 0.6,
|
|
109
|
-
})
|
|
102
|
+
const mastMat = MaterialBank.material('crane|mast', { roughness: 0.3, metalness: 0.6 }, bodyColor)
|
|
110
103
|
|
|
111
104
|
const mastGeometries: THREE.BufferGeometry[] = []
|
|
112
105
|
|
|
@@ -137,11 +130,7 @@ export class Crane3D extends RealObjectGroup {
|
|
|
137
130
|
// 포크 geometry 는 *_mastBaseY 기준 local 0 에 둠* — 즉 group.position.y = 0
|
|
138
131
|
// 일 때 mast 바닥 자리에. forkZ 0~100 에 따라 group.position.y 만 변경하면
|
|
139
132
|
// mast 따라 위/아래 이동. mesh 재구축 불필요.
|
|
140
|
-
const forkMat =
|
|
141
|
-
color: FORK_COLOR,
|
|
142
|
-
roughness: 0.4,
|
|
143
|
-
metalness: 0.5,
|
|
144
|
-
})
|
|
133
|
+
const forkMat = MaterialBank.material('crane|fork', { roughness: 0.4, metalness: 0.5 }, FORK_COLOR)
|
|
145
134
|
|
|
146
135
|
const forkGeometries: THREE.BufferGeometry[] = []
|
|
147
136
|
|
|
@@ -188,10 +177,13 @@ export class Crane3D extends RealObjectGroup {
|
|
|
188
177
|
topClamp.position.y = mastBaseY + mastH + clampH / 2
|
|
189
178
|
topClamp.castShadow = true
|
|
190
179
|
inner.add(topClamp)
|
|
180
|
+
|
|
181
|
+
// mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
182
|
+
this.updateAlpha()
|
|
191
183
|
}
|
|
192
184
|
|
|
193
185
|
updateDimension() {}
|
|
194
|
-
updateAlpha()
|
|
186
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴(faint).
|
|
195
187
|
|
|
196
188
|
/** build() 직전 호출. forkGroup 참조를 깨끗이 비워둔다 — 새 build 가 다시 set. */
|
|
197
189
|
clear() {
|
|
@@ -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 } from '@hatiolab/things-scene'
|
|
15
|
+
import { RealObjectGroup, registerRealObjectFactory, 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
|
import { Z_FMSIM } from './z-priority'
|
|
@@ -46,17 +46,10 @@ export class CraneRail3D extends RealObjectGroup {
|
|
|
46
46
|
const railW = Math.max(h * 0.06, 2) // 레일 폭
|
|
47
47
|
const supportThick = Math.max(Math.min(w, h) * 0.05, 2)
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
const supportMat = new THREE.MeshStandardMaterial({
|
|
56
|
-
color: SUPPORT_COLOR,
|
|
57
|
-
roughness: 0.5,
|
|
58
|
-
metalness: 0.5,
|
|
59
|
-
})
|
|
49
|
+
// MaterialBank 공유(정적 색 dedup). 자체 update() 없이 base 생명주기(guarded disposeObject3D)
|
|
50
|
+
// 상속 → 공유 base dispose 안전.
|
|
51
|
+
const railMat = MaterialBank.material('crane-rail|rail', { roughness: 0.3, metalness: 0.7 }, RAIL_COLOR)
|
|
52
|
+
const supportMat = MaterialBank.material('crane-rail|support', { roughness: 0.5, metalness: 0.5 }, SUPPORT_COLOR)
|
|
60
53
|
|
|
61
54
|
// ── 바닥 레일 (2개 평행 트랙) ──
|
|
62
55
|
const bottomGeometries: THREE.BufferGeometry[] = []
|
|
@@ -98,10 +91,13 @@ export class CraneRail3D extends RealObjectGroup {
|
|
|
98
91
|
const supportMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(supportGeometries), supportMat)
|
|
99
92
|
supportMesh.castShadow = true
|
|
100
93
|
inner.add(supportMesh)
|
|
94
|
+
|
|
95
|
+
// mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
|
|
96
|
+
this.updateAlpha()
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
updateDimension() {}
|
|
104
|
-
updateAlpha()
|
|
100
|
+
// updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴(faint).
|
|
105
101
|
|
|
106
102
|
onchange(after: Record<string, unknown>, before: Record<string, unknown>) {
|
|
107
103
|
if ('width' in after || 'height' in after || 'depth' in after || 'fillStyle' in after || 'strokeStyle' in after) {
|