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

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.
@@ -1 +1 @@
1
- {"version":3,"file":"oht-3d.js","sourceRoot":"","sources":["../../src/factories/oht-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OHT (Overhead Hoist Transport) 3D Factory\n *\n * 천장 레일에 매달려 이동하는 반송 차량. 캐리어를 호이스트로 운반.\n * 구조 (바닥 원점, y=0~depth):\n * - 하단 그리퍼 바 (캐리어 집는 가로 바)\n * - 호이스트 암 (좌우 수직 기둥)\n * - 본체 하우징 (statusColor 적용)\n * - 상단 트롤리 (납작+넓은 레일 클램프 + 좌우 바퀴)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js'\nimport { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\n\nconst TROLLEY_COLOR = 0x222222\nconst FRAME_COLOR = 0x444444\nconst DEFAULT_BODY_COLOR = 0x6688cc\n\n/** OHT 레일 높이: 스토커(5x) 위 */\nconst OHT_RAIL_HEIGHT = 6 * TRANSPORT_SURFACE_HEIGHT\n\nexport class OHT3D extends RealObjectGroup {\n private _ownMeshes: THREE.Mesh[] = []\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n /** 천장 레일 높이에 배치 (center origin 기준: 레일 높이 + 볼륨 중심 반깊이) */\n protected get syncZPosOffset(): number {\n return OHT_RAIL_HEIGHT + this.effectiveDepth / 2\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_BODY_COLOR\n }\n\n build() {\n super.build()\n\n const { width = 10, height = 10 } = this.component.state\n const w = Math.abs(width)\n const h = Math.abs(height)\n const d = this.effectiveDepth\n if (!w || !h) return\n\n // 높이 배분: 그리퍼 8%, 호이스트암 22%, 본체 45%, 간격 5%, 트롤리 12%, 바퀴 8%\n const gripperBarH = d * 0.08\n const armH = d * 0.22\n const bodyH = d * 0.45\n const gap = d * 0.05\n const trolleyH = d * 0.12\n const wheelH = d * 0.08\n\n const armThickness = Math.min(w, h) * 0.12\n const bodyCornerR = Math.min(w, h, bodyH) * 0.1\n\n const frameMat = new THREE.MeshStandardMaterial({\n color: FRAME_COLOR,\n roughness: 0.5,\n metalness: 0.4,\n })\n\n // ── 1. 하단 그리퍼 바 (캐리어를 집는 가로 바 — 이동 방향과 직교) ──\n const gripperBarW = w * 0.7\n const gripperBarD = h * 0.15\n const gripperGeo = new THREE.BoxGeometry(gripperBarW, gripperBarH, gripperBarD)\n const gripperMesh = new THREE.Mesh(gripperGeo, frameMat)\n gripperMesh.position.y = gripperBarH / 2\n gripperMesh.castShadow = true\n\n // ── 2. 호이스트 암 (전후 수직 기둥 + 하단 연결 바) ──\n const armBaseY = gripperBarH\n const armGeometries: THREE.BufferGeometry[] = []\n\n // 전후 수직 기둥 (레일 방향 = Z축)\n for (const zSign of [-1, 1]) {\n const pillar = new THREE.BoxGeometry(armThickness, armH, armThickness)\n pillar.translate(0, armBaseY + armH / 2, zSign * (h * 0.3))\n armGeometries.push(pillar)\n }\n\n // 하단 연결 바 (레일 방향으로 연결)\n const crossBarD = h * 0.6 + armThickness\n const crossBarH = armThickness * 0.6\n const crossBar = new THREE.BoxGeometry(armThickness, crossBarH, crossBarD)\n crossBar.translate(0, armBaseY + crossBarH / 2, 0)\n armGeometries.push(crossBar)\n\n const armMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(armGeometries), frameMat)\n armMesh.castShadow = true\n\n // ── 3. 본체 하우징 (statusColor 적용) ──\n const bodyBaseY = armBaseY + armH\n const bodyColor = this.resolveColor()\n const bodyGeo = new RoundedBoxGeometry(w, bodyH, h, 4, bodyCornerR)\n const bodyMat = new THREE.MeshStandardMaterial({\n color: bodyColor,\n roughness: 0.3,\n metalness: 0.5,\n })\n const bodyMesh = new THREE.Mesh(bodyGeo, bodyMat)\n bodyMesh.position.y = bodyBaseY + bodyH / 2\n bodyMesh.castShadow = true\n bodyMesh.receiveShadow = true\n\n // ── 4. 상단 트롤리 플레이트 (레일 방향으로 길게 — Z축) ──\n const trolleyBaseY = bodyBaseY + bodyH + gap\n const trolleyW = w * 0.6\n const trolleyD = h * 1.15\n const trolleyGeo = new THREE.BoxGeometry(trolleyW, trolleyH, trolleyD)\n const trolleyMat = new THREE.MeshStandardMaterial({\n color: TROLLEY_COLOR,\n roughness: 0.6,\n metalness: 0.3,\n })\n const trolleyMesh = new THREE.Mesh(trolleyGeo, trolleyMat)\n trolleyMesh.position.y = trolleyBaseY + trolleyH / 2\n trolleyMesh.castShadow = true\n\n // ── 5. 전후 바퀴 (트롤리 위, 레일 방향 = Z축) ──\n const wheelBaseY = trolleyBaseY + trolleyH\n const wheelRadius = wheelH / 2\n const wheelDepth = w * 0.2\n const wheelMeshes: THREE.Mesh[] = []\n for (const zSign of [-1, 1]) {\n const wheelGeo = new THREE.CylinderGeometry(wheelRadius, wheelRadius, wheelDepth, 12)\n wheelGeo.rotateZ(Math.PI / 2)\n const wheelMesh = new THREE.Mesh(wheelGeo, trolleyMat)\n wheelMesh.position.set(0, wheelBaseY + wheelRadius, zSign * (trolleyD * 0.35))\n wheelMesh.castShadow = true\n wheelMeshes.push(wheelMesh)\n }\n\n this._ownMeshes = [gripperMesh, armMesh, bodyMesh, trolleyMesh, ...wheelMeshes]\n for (const m of this._ownMeshes) this.components3D.add(m)\n }\n\n updateDimension() {}\n\n update() {\n for (const m of this._ownMeshes) {\n this.components3D.remove(m)\n disposeObject3D(m)\n }\n this._ownMeshes = []\n this.build()\n this.updateTransform()\n this.updateAlpha()\n this.updateHidden()\n }\n\n updateAlpha() {\n const { alpha = 1 } = this.component.state\n for (const m of this._ownMeshes) {\n const materials = Array.isArray(m.material) ? m.material : [m.material]\n for (const mat of materials) {\n ;(mat as THREE.MeshStandardMaterial).opacity = alpha\n ;(mat as THREE.MeshStandardMaterial).transparent = alpha < 1\n }\n }\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after ||\n 'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('OHT', (component: Component) => new OHT3D(component))\n"]}
1
+ {"version":3,"file":"oht-3d.js","sourceRoot":"","sources":["../../src/factories/oht-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OHT (Overhead Hoist Transport) 3D Factory\n *\n * 천장 레일에 매달려 이동하는 반송 차량. 캐리어를 호이스트로 운반.\n * 구조 (바닥 원점, y=0~depth):\n * - 하단 그리퍼 바 (캐리어 집는 가로 바)\n * - 호이스트 암 (좌우 수직 기둥)\n * - 본체 하우징 (statusColor 적용)\n * - 상단 트롤리 (납작+넓은 레일 클램프 + 좌우 바퀴)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js'\nimport { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\n\nconst TROLLEY_COLOR = 0x222222\nconst FRAME_COLOR = 0x444444\nconst DEFAULT_BODY_COLOR = 0x6688cc\n\n/** OHT 레일 높이: 스토커(5x) 위 */\nconst OHT_RAIL_HEIGHT = 6 * TRANSPORT_SURFACE_HEIGHT\n\nexport class OHT3D extends RealObjectGroup {\n private _ownMeshes: THREE.Mesh[] = []\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n /** 천장 레일 높이에 배치 (center origin 기준: 레일 높이 + 볼륨 중심 반깊이) */\n protected get syncZPosOffset(): number {\n return OHT_RAIL_HEIGHT + this.effectiveDepth / 2\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_BODY_COLOR\n }\n\n build() {\n super.build()\n\n const { width = 10, height = 10 } = this.component.state\n const w = Math.abs(width)\n const h = Math.abs(height)\n const d = this.effectiveDepth\n if (!w || !h) return\n\n // 높이 배분: 그리퍼 8%, 호이스트암 22%, 본체 45%, 간격 5%, 트롤리 12%, 바퀴 8%\n const gripperBarH = d * 0.08\n const armH = d * 0.22\n const bodyH = d * 0.45\n const gap = d * 0.05\n const trolleyH = d * 0.12\n const wheelH = d * 0.08\n\n const armThickness = Math.min(w, h) * 0.12\n const bodyCornerR = Math.min(w, h, bodyH) * 0.1\n\n // MaterialBank 공유 — dedup + faint 안전(코어 변형 스왑, disposeObject3D 가 __bankOwned 스킵).\n const frameMat = MaterialBank.material('oht|frame', { roughness: 0.5, metalness: 0.4 }, FRAME_COLOR)\n\n // ── 1. 하단 그리퍼 바 (캐리어를 집는 가로 바 — 이동 방향과 직교) ──\n const gripperBarW = w * 0.7\n const gripperBarD = h * 0.15\n const gripperGeo = new THREE.BoxGeometry(gripperBarW, gripperBarH, gripperBarD)\n const gripperMesh = new THREE.Mesh(gripperGeo, frameMat)\n gripperMesh.position.y = gripperBarH / 2\n gripperMesh.castShadow = true\n\n // ── 2. 호이스트 암 (전후 수직 기둥 + 하단 연결 바) ──\n const armBaseY = gripperBarH\n const armGeometries: THREE.BufferGeometry[] = []\n\n // 전후 수직 기둥 (레일 방향 = Z축)\n for (const zSign of [-1, 1]) {\n const pillar = new THREE.BoxGeometry(armThickness, armH, armThickness)\n pillar.translate(0, armBaseY + armH / 2, zSign * (h * 0.3))\n armGeometries.push(pillar)\n }\n\n // 하단 연결 바 (레일 방향으로 연결)\n const crossBarD = h * 0.6 + armThickness\n const crossBarH = armThickness * 0.6\n const crossBar = new THREE.BoxGeometry(armThickness, crossBarH, crossBarD)\n crossBar.translate(0, armBaseY + crossBarH / 2, 0)\n armGeometries.push(crossBar)\n\n const armMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(armGeometries), frameMat)\n armMesh.castShadow = true\n\n // ── 3. 본체 하우징 (statusColor 적용) ──\n const bodyBaseY = armBaseY + armH\n const bodyColor = this.resolveColor()\n const bodyGeo = new RoundedBoxGeometry(w, bodyH, h, 4, bodyCornerR)\n const bodyMat = MaterialBank.material('oht|body', { roughness: 0.3, metalness: 0.5 }, bodyColor)\n const bodyMesh = new THREE.Mesh(bodyGeo, bodyMat)\n bodyMesh.position.y = bodyBaseY + bodyH / 2\n bodyMesh.castShadow = true\n bodyMesh.receiveShadow = true\n\n // ── 4. 상단 트롤리 플레이트 (레일 방향으로 길게 — Z축) ──\n const trolleyBaseY = bodyBaseY + bodyH + gap\n const trolleyW = w * 0.6\n const trolleyD = h * 1.15\n const trolleyGeo = new THREE.BoxGeometry(trolleyW, trolleyH, trolleyD)\n const trolleyMat = MaterialBank.material('oht|trolley', { roughness: 0.6, metalness: 0.3 }, TROLLEY_COLOR)\n const trolleyMesh = new THREE.Mesh(trolleyGeo, trolleyMat)\n trolleyMesh.position.y = trolleyBaseY + trolleyH / 2\n trolleyMesh.castShadow = true\n\n // ── 5. 전후 바퀴 (트롤리 위, 레일 방향 = Z축) ──\n const wheelBaseY = trolleyBaseY + trolleyH\n const wheelRadius = wheelH / 2\n const wheelDepth = w * 0.2\n const wheelMeshes: THREE.Mesh[] = []\n for (const zSign of [-1, 1]) {\n const wheelGeo = new THREE.CylinderGeometry(wheelRadius, wheelRadius, wheelDepth, 12)\n wheelGeo.rotateZ(Math.PI / 2)\n const wheelMesh = new THREE.Mesh(wheelGeo, trolleyMat)\n wheelMesh.position.set(0, wheelBaseY + wheelRadius, zSign * (trolleyD * 0.35))\n wheelMesh.castShadow = true\n wheelMeshes.push(wheelMesh)\n }\n\n this._ownMeshes = [gripperMesh, armMesh, bodyMesh, trolleyMesh, ...wheelMeshes]\n for (const m of this._ownMeshes) this.components3D.add(m)\n\n // mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).\n this.updateAlpha()\n }\n\n updateDimension() {}\n\n update() {\n for (const m of this._ownMeshes) {\n this.components3D.remove(m)\n disposeObject3D(m)\n }\n this._ownMeshes = []\n this.build()\n this.updateTransform()\n this.updateAlpha()\n this.updateHidden()\n }\n\n // updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.\n // canonical(bank base opacity 1) × state.alpha × faint. view 모드 불변, 편집 모드 faint 추가.\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after ||\n 'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('OHT', (component: Component) => new OHT3D(component))\n"]}
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import * as THREE from 'three';
13
13
  import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
14
- import { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene';
14
+ import { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene';
15
15
  import { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js';
16
16
  const BASE_COLOR = 0x333333;
17
17
  const FORK_COLOR = 0x777777;
@@ -62,11 +62,9 @@ export class Shuttle3D extends RealObjectGroup {
62
62
  // ── 1. 하단 베이스 플레이트 (납작하고 넓은 주행부) ──
63
63
  const basePad = Math.min(w, h) * 0.05;
64
64
  const baseGeo = new THREE.BoxGeometry(w + basePad * 2, baseH, h + basePad * 2);
65
- const baseMat = new THREE.MeshStandardMaterial({
66
- color: BASE_COLOR,
67
- roughness: 0.7,
68
- metalness: 0.3,
69
- });
65
+ // MaterialBank 공유 — 정적/상태색 dedup + faint 안전(코어가 공유 base 변형 스왑). dispose 는
66
+ // disposeObject3D 가 __bankOwned 를 건너뛰어 안전.
67
+ const baseMat = MaterialBank.material('shuttle|base', { roughness: 0.7, metalness: 0.3 }, BASE_COLOR);
70
68
  const baseMesh = new THREE.Mesh(baseGeo, baseMat);
71
69
  baseMesh.position.y = baseH / 2;
72
70
  baseMesh.castShadow = true;
@@ -74,11 +72,7 @@ export class Shuttle3D extends RealObjectGroup {
74
72
  // ── 2. 좌우 마스트 기둥 (statusColor 적용) ──
75
73
  const mastBaseY = baseH;
76
74
  const bodyColor = this.resolveColor();
77
- const mastMat = new THREE.MeshStandardMaterial({
78
- color: bodyColor,
79
- roughness: 0.4,
80
- metalness: 0.5,
81
- });
75
+ const mastMat = MaterialBank.material('shuttle|mast', { roughness: 0.4, metalness: 0.5 }, bodyColor);
82
76
  const mastGeometries = [];
83
77
  // 좌우 수직 기둥
84
78
  for (const xSign of [-1, 1]) {
@@ -96,11 +90,7 @@ export class Shuttle3D extends RealObjectGroup {
96
90
  mastMesh.castShadow = true;
97
91
  // ── 3. 상단 포크 (2개의 평행 암 + 백 플레이트) ──
98
92
  const forkBaseY = mastBaseY + mastH;
99
- const forkMat = new THREE.MeshStandardMaterial({
100
- color: FORK_COLOR,
101
- roughness: 0.4,
102
- metalness: 0.6,
103
- });
93
+ const forkMat = MaterialBank.material('shuttle|fork', { roughness: 0.4, metalness: 0.6 }, FORK_COLOR);
104
94
  const forkGeometries = [];
105
95
  // 백 플레이트 (마스트 상단을 잇는 가로판)
106
96
  const backPlateH = forkH * 0.4;
@@ -122,6 +112,8 @@ export class Shuttle3D extends RealObjectGroup {
122
112
  this._ownMeshes = [baseMesh, mastMesh, forkMesh];
123
113
  for (const m of this._ownMeshes)
124
114
  this.components3D.add(m);
115
+ // mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).
116
+ this.updateAlpha();
125
117
  }
126
118
  updateDimension() { }
127
119
  update() {
@@ -135,17 +127,9 @@ export class Shuttle3D extends RealObjectGroup {
135
127
  this.updateAlpha();
136
128
  this.updateHidden();
137
129
  }
138
- updateAlpha() {
139
- const { alpha = 1 } = this.component.state;
140
- for (const m of this._ownMeshes) {
141
- const materials = Array.isArray(m.material) ? m.material : [m.material];
142
- for (const mat of materials) {
143
- ;
144
- mat.opacity = alpha;
145
- mat.transparent = alpha < 1;
146
- }
147
- }
148
- }
130
+ // updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.
131
+ // 코어가 canonical(=bank base, opacity 1) 위에 state.alpha × faint 를 일괄. view 모드는
132
+ // 1×state.alpha = 기존 in-place 결과와 동일 → 불변, 편집 모드만 faint 추가.
149
133
  onchange(after, before) {
150
134
  if ('width' in after || 'height' in after || 'depth' in after ||
151
135
  'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {
@@ -1 +1 @@
1
- {"version":3,"file":"shuttle-3d.js","sourceRoot":"","sources":["../../src/factories/shuttle-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shuttle 3D Factory\n *\n * 스토커 내부에서 캐리어를 선반↔포트 간 이송하는 포크 캐리지.\n * 수평 레일 위를 이동하며, 포크를 밀어넣어 캐리어를 적재/하역.\n *\n * 구조 (바닥 원점, y=0~depth):\n * - 하단 베이스 플레이트 (납작, 넓음, 레일 위 주행부)\n * - 중앙 마스트 (좌우 수직 기둥, statusColor 적용)\n * - 상단 포크 (2개의 평행한 가로 암, 캐리어 적재면)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\n\nconst BASE_COLOR = 0x333333\nconst FORK_COLOR = 0x777777\nconst DEFAULT_BODY_COLOR = 0x6688cc\n\nexport class Shuttle3D extends RealObjectGroup {\n private _ownMeshes: THREE.Mesh[] = []\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_BODY_COLOR\n }\n\n build() {\n super.build()\n\n const { width = 10, height = 10 } = this.component.state\n const w = Math.abs(width)\n const h = Math.abs(height)\n const d = this.effectiveDepth\n if (!w || !h) return\n\n // 높이 배분: 베이스 15%, 마스트 55%, 포크 30%\n const baseH = d * 0.15\n const mastH = d * 0.55\n const forkH = d * 0.30\n\n const mastThickness = Math.min(w, h) * 0.15\n\n // ── 1. 하단 베이스 플레이트 (납작하고 넓은 주행부) ──\n const basePad = Math.min(w, h) * 0.05\n const baseGeo = new THREE.BoxGeometry(w + basePad * 2, baseH, h + basePad * 2)\n const baseMat = new THREE.MeshStandardMaterial({\n color: BASE_COLOR,\n roughness: 0.7,\n metalness: 0.3,\n })\n const baseMesh = new THREE.Mesh(baseGeo, baseMat)\n baseMesh.position.y = baseH / 2\n baseMesh.castShadow = true\n baseMesh.receiveShadow = true\n\n // ── 2. 좌우 마스트 기둥 (statusColor 적용) ──\n const mastBaseY = baseH\n const bodyColor = this.resolveColor()\n const mastMat = new THREE.MeshStandardMaterial({\n color: bodyColor,\n roughness: 0.4,\n metalness: 0.5,\n })\n\n const mastGeometries: THREE.BufferGeometry[] = []\n\n // 좌우 수직 기둥\n for (const xSign of [-1, 1]) {\n const pillar = new THREE.BoxGeometry(mastThickness, mastH, mastThickness)\n pillar.translate(xSign * (w / 2 - mastThickness / 2), mastBaseY + mastH / 2, 0)\n mastGeometries.push(pillar)\n }\n\n // 중간 가로 연결 바 (구조 강성)\n const crossBarW = w - mastThickness\n const crossBarH = mastThickness * 0.5\n const crossBar = new THREE.BoxGeometry(crossBarW, crossBarH, mastThickness)\n crossBar.translate(0, mastBaseY + mastH * 0.4, 0)\n mastGeometries.push(crossBar)\n\n const mastMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(mastGeometries), mastMat)\n mastMesh.castShadow = true\n\n // ── 3. 상단 포크 (2개의 평행 암 + 백 플레이트) ──\n const forkBaseY = mastBaseY + mastH\n const forkMat = new THREE.MeshStandardMaterial({\n color: FORK_COLOR,\n roughness: 0.4,\n metalness: 0.6,\n })\n\n const forkGeometries: THREE.BufferGeometry[] = []\n\n // 백 플레이트 (마스트 상단을 잇는 가로판)\n const backPlateH = forkH * 0.4\n const backPlate = new THREE.BoxGeometry(w, backPlateH, mastThickness * 0.8)\n backPlate.translate(0, forkBaseY + backPlateH / 2, -h * 0.35)\n forkGeometries.push(backPlate)\n\n // 2개의 포크 암 (앞으로 뻗은 평행 막대)\n const forkArmW = mastThickness * 0.7\n const forkArmH = forkH * 0.25\n const forkArmD = h * 0.85\n const forkSpacing = w * 0.55\n\n for (const xSign of [-1, 1]) {\n const arm = new THREE.BoxGeometry(forkArmW, forkArmH, forkArmD)\n arm.translate(xSign * (forkSpacing / 2), forkBaseY + forkArmH / 2, forkArmD / 2 - h * 0.35)\n forkGeometries.push(arm)\n }\n\n const forkMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(forkGeometries), forkMat)\n forkMesh.castShadow = true\n\n this._ownMeshes = [baseMesh, mastMesh, forkMesh]\n for (const m of this._ownMeshes) this.components3D.add(m)\n }\n\n updateDimension() {}\n\n update() {\n for (const m of this._ownMeshes) {\n this.components3D.remove(m)\n disposeObject3D(m)\n }\n this._ownMeshes = []\n this.build()\n this.updateTransform()\n this.updateAlpha()\n this.updateHidden()\n }\n\n updateAlpha() {\n const { alpha = 1 } = this.component.state\n for (const m of this._ownMeshes) {\n const materials = Array.isArray(m.material) ? m.material : [m.material]\n for (const mat of materials) {\n ;(mat as THREE.MeshStandardMaterial).opacity = alpha\n ;(mat as THREE.MeshStandardMaterial).transparent = alpha < 1\n }\n }\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after ||\n 'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('Shuttle', (component: Component) => new Shuttle3D(component))\n"]}
1
+ {"version":3,"file":"shuttle-3d.js","sourceRoot":"","sources":["../../src/factories/shuttle-3d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,mBAAmB,MAAM,iDAAiD,CAAA;AACtF,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAElH,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,MAAM,UAAU,GAAG,QAAQ,CAAA;AAC3B,MAAM,UAAU,GAAG,QAAQ,CAAA;AAC3B,MAAM,kBAAkB,GAAG,QAAQ,CAAA;AAEnC,MAAM,OAAO,SAAU,SAAQ,eAAe;IAA9C;;QACU,eAAU,GAAiB,EAAE,CAAA;IA2IvC,CAAC;IA1IC,IAAI,cAAc;QAChB,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAA;QACtC,OAAO,KAAK,IAAI,wBAAwB,CAAA;IAC1C,CAAC;IAEO,YAAY;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAgB,CAAA;QAElC,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAA;YAC3B,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,SAAS;gBAAE,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;QACzG,CAAC;QAAC,WAAM,CAAC;YACP,0BAA0B;QAC5B,CAAC;QAED,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAA;QAC1C,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/C,IAAI,CAAC;gBACH,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAA;YAC5C,CAAC;YAAC,WAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;QACH,CAAC;QAED,OAAO,kBAAkB,CAAA;IAC3B,CAAC;IAED,KAAK;QACH,KAAK,CAAC,KAAK,EAAE,CAAA;QAEb,MAAM,EAAE,KAAK,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAA;QACxD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC1B,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAA;QAC7B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,OAAM;QAEpB,kCAAkC;QAClC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAA;QACtB,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAA;QACtB,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAA;QAEtB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;QAE3C,qCAAqC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;QACrC,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAA;QAC9E,0EAA0E;QAC1E,2CAA2C;QAC3C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,UAAU,CAAC,CAAA;QACrG,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACjD,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAA;QAC/B,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAA;QAC1B,QAAQ,CAAC,aAAa,GAAG,IAAI,CAAA;QAE7B,sCAAsC;QACtC,MAAM,SAAS,GAAG,KAAK,CAAA;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;QACrC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,CAAA;QAEpG,MAAM,cAAc,GAA2B,EAAE,CAAA;QAEjD,WAAW;QACX,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,aAAa,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;YACzE,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,aAAa,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;YAC/E,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC7B,CAAC;QAED,qBAAqB;QACrB,MAAM,SAAS,GAAG,CAAC,GAAG,aAAa,CAAA;QACnC,MAAM,SAAS,GAAG,aAAa,GAAG,GAAG,CAAA;QACrC,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,CAAC,CAAA;QAC3E,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,CAAC,CAAA;QACjD,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAE7B,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,CAAA;QAC7F,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAA;QAE1B,qCAAqC;QACrC,MAAM,SAAS,GAAG,SAAS,GAAG,KAAK,CAAA;QACnC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,UAAU,CAAC,CAAA;QAErG,MAAM,cAAc,GAA2B,EAAE,CAAA;QAEjD,0BAA0B;QAC1B,MAAM,UAAU,GAAG,KAAK,GAAG,GAAG,CAAA;QAC9B,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,aAAa,GAAG,GAAG,CAAC,CAAA;QAC3E,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;QAC7D,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAE9B,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,aAAa,GAAG,GAAG,CAAA;QACpC,MAAM,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAA;QAC7B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAA;QACzB,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAA;QAE5B,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;YAC/D,GAAG,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;YAC3F,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC1B,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,CAAA;QAC7F,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAA;QAE1B,IAAI,CAAC,UAAU,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAChD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QAEzD,mEAAmE;QACnE,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED,eAAe,KAAI,CAAC;IAEpB,MAAM;QACJ,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAChC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YAC3B,eAAe,CAAC,CAAC,CAAC,CAAA;QACpB,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,EAAE,CAAA;QACpB,IAAI,CAAC,KAAK,EAAE,CAAA;QACZ,IAAI,CAAC,eAAe,EAAE,CAAA;QACtB,IAAI,CAAC,WAAW,EAAE,CAAA;QAClB,IAAI,CAAC,YAAY,EAAE,CAAA;IACrB,CAAC;IAED,kFAAkF;IAClF,6EAA6E;IAC7E,4DAA4D;IAE5D,QAAQ,CAAC,KAA8B,EAAE,MAA+B;QACtE,IAAI,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK;YACzD,WAAW,IAAI,KAAK,IAAI,aAAa,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;YACpE,IAAI,CAAC,MAAM,EAAE,CAAA;YACb,OAAM;QACR,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC/B,CAAC;CACF;AAED,yBAAyB,CAAC,SAAS,EAAE,CAAC,SAAoB,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,SAAS,CAAC,CAAC,CAAA","sourcesContent":["/**\n * Shuttle 3D Factory\n *\n * 스토커 내부에서 캐리어를 선반↔포트 간 이송하는 포크 캐리지.\n * 수평 레일 위를 이동하며, 포크를 밀어넣어 캐리어를 적재/하역.\n *\n * 구조 (바닥 원점, y=0~depth):\n * - 하단 베이스 플레이트 (납작, 넓음, 레일 위 주행부)\n * - 중앙 마스트 (좌우 수직 기둥, statusColor 적용)\n * - 상단 포크 (2개의 평행한 가로 암, 캐리어 적재면)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\n\nconst BASE_COLOR = 0x333333\nconst FORK_COLOR = 0x777777\nconst DEFAULT_BODY_COLOR = 0x6688cc\n\nexport class Shuttle3D extends RealObjectGroup {\n private _ownMeshes: THREE.Mesh[] = []\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_BODY_COLOR\n }\n\n build() {\n super.build()\n\n const { width = 10, height = 10 } = this.component.state\n const w = Math.abs(width)\n const h = Math.abs(height)\n const d = this.effectiveDepth\n if (!w || !h) return\n\n // 높이 배분: 베이스 15%, 마스트 55%, 포크 30%\n const baseH = d * 0.15\n const mastH = d * 0.55\n const forkH = d * 0.30\n\n const mastThickness = Math.min(w, h) * 0.15\n\n // ── 1. 하단 베이스 플레이트 (납작하고 넓은 주행부) ──\n const basePad = Math.min(w, h) * 0.05\n const baseGeo = new THREE.BoxGeometry(w + basePad * 2, baseH, h + basePad * 2)\n // MaterialBank 공유 — 정적/상태색 dedup + faint 안전(코어가 공유 base 변형 스왑). dispose 는\n // disposeObject3D 가 __bankOwned 를 건너뛰어 안전.\n const baseMat = MaterialBank.material('shuttle|base', { roughness: 0.7, metalness: 0.3 }, BASE_COLOR)\n const baseMesh = new THREE.Mesh(baseGeo, baseMat)\n baseMesh.position.y = baseH / 2\n baseMesh.castShadow = true\n baseMesh.receiveShadow = true\n\n // ── 2. 좌우 마스트 기둥 (statusColor 적용) ──\n const mastBaseY = baseH\n const bodyColor = this.resolveColor()\n const mastMat = MaterialBank.material('shuttle|mast', { roughness: 0.4, metalness: 0.5 }, bodyColor)\n\n const mastGeometries: THREE.BufferGeometry[] = []\n\n // 좌우 수직 기둥\n for (const xSign of [-1, 1]) {\n const pillar = new THREE.BoxGeometry(mastThickness, mastH, mastThickness)\n pillar.translate(xSign * (w / 2 - mastThickness / 2), mastBaseY + mastH / 2, 0)\n mastGeometries.push(pillar)\n }\n\n // 중간 가로 연결 바 (구조 강성)\n const crossBarW = w - mastThickness\n const crossBarH = mastThickness * 0.5\n const crossBar = new THREE.BoxGeometry(crossBarW, crossBarH, mastThickness)\n crossBar.translate(0, mastBaseY + mastH * 0.4, 0)\n mastGeometries.push(crossBar)\n\n const mastMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(mastGeometries), mastMat)\n mastMesh.castShadow = true\n\n // ── 3. 상단 포크 (2개의 평행 암 + 백 플레이트) ──\n const forkBaseY = mastBaseY + mastH\n const forkMat = MaterialBank.material('shuttle|fork', { roughness: 0.4, metalness: 0.6 }, FORK_COLOR)\n\n const forkGeometries: THREE.BufferGeometry[] = []\n\n // 백 플레이트 (마스트 상단을 잇는 가로판)\n const backPlateH = forkH * 0.4\n const backPlate = new THREE.BoxGeometry(w, backPlateH, mastThickness * 0.8)\n backPlate.translate(0, forkBaseY + backPlateH / 2, -h * 0.35)\n forkGeometries.push(backPlate)\n\n // 2개의 포크 암 (앞으로 뻗은 평행 막대)\n const forkArmW = mastThickness * 0.7\n const forkArmH = forkH * 0.25\n const forkArmD = h * 0.85\n const forkSpacing = w * 0.55\n\n for (const xSign of [-1, 1]) {\n const arm = new THREE.BoxGeometry(forkArmW, forkArmH, forkArmD)\n arm.translate(xSign * (forkSpacing / 2), forkBaseY + forkArmH / 2, forkArmD / 2 - h * 0.35)\n forkGeometries.push(arm)\n }\n\n const forkMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(forkGeometries), forkMat)\n forkMesh.castShadow = true\n\n this._ownMeshes = [baseMesh, mastMesh, forkMesh]\n for (const m of this._ownMeshes) this.components3D.add(m)\n\n // mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).\n this.updateAlpha()\n }\n\n updateDimension() {}\n\n update() {\n for (const m of this._ownMeshes) {\n this.components3D.remove(m)\n disposeObject3D(m)\n }\n this._ownMeshes = []\n this.build()\n this.updateTransform()\n this.updateAlpha()\n this.updateHidden()\n }\n\n // updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.\n // 코어가 canonical(=bank base, opacity 1) 위에 state.alpha × faint 를 일괄. view 모드는\n // 1×state.alpha = 기존 in-place 결과와 동일 → 불변, 편집 모드만 faint 추가.\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after ||\n 'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('Shuttle', (component: Component) => new Shuttle3D(component))\n"]}
@@ -69,32 +69,13 @@ export default class MCSMachine extends MCSStatusMixin(ContainerAbstract) {
69
69
  return LEGEND_MACHINE;
70
70
  }
71
71
  /**
72
- * In 2D, ignore `state.alpha`; preserve everything else.
73
- *
74
- * things-scene's base prerender (component-drawing.ts:31, 44) does:
75
- * const fade = (alpha || 1) - this._delta.fade
76
- * if (fade) context.globalAlpha = fade
77
- *
78
- * It *overwrites* `globalAlpha` (not multiplies), so the inherited
79
- * parent alpha is also lost — that's a base-layer quirk we match the
80
- * recovery for. Then at line 48 it applies editMode+hidden dimming
81
- * with a *= 0.5.
82
- *
83
- * Our recovery:
84
- * 1. Capture the inherited `globalAlpha` *before* super runs.
85
- * 2. Let super do its work (transforms / shadow / dim flags).
86
- * 3. Restore globalAlpha to inherited × editMode-hidden dim factor —
87
- * i.e., what super would have produced if our state.alpha were 1.
88
- *
89
- * 3D path is unaffected: 3D factories read `state.alpha` directly via
90
- * their `updateAlpha()` implementations and continue to honor it (with
91
- * per-config opacity fallback).
72
+ * 2D 에서는 `state.alpha`(3D material 전용)를 무시한다. hidden/모델링 dim 흐림은 **코어(draw)가
73
+ * 일괄 처리**하므로 여기서 손대지 않는다 (component-drawing.ts draw 가 prerender 직후 faint 적용).
74
+ * fade 애니메이션은 보존. 3D 는 factory `updateAlpha()` 가 state.alpha 를 별도로 존중.
92
75
  */
93
76
  prerender(ctx) {
94
- const inherited = ctx.globalAlpha;
95
77
  super.prerender(ctx);
96
- const dim = this.app.isEditMode && this.hidden ? 0.5 : 1;
97
- ctx.globalAlpha = inherited * dim;
78
+ ctx.globalAlpha = 1 - this._delta.fade;
98
79
  }
99
80
  render(ctx) {
100
81
  var { round = 0, lineWidth } = this.state;
@@ -1 +1 @@
1
- {"version":3,"file":"mcs-machine.js","sourceRoot":"","sources":["../src/mcs-machine.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ContainerAbstract } from '@hatiolab/things-scene'\nimport { getPopupData } from '@fmsim/api'\n\nimport { MCSStatusMixin, MCSStatusMixinProperties } from './features/mcs-status-mixin'\n\nimport { LEGEND_MACHINE, Legend } from './features/mcs-status-default'\n\n/**\n * MCS Machine의 공통 속성을 정의한 최상위 오브젝트\n * - Container\n * -\n */\nexport default class MCSMachine extends MCSStatusMixin(ContainerAbstract) {\n static get properties(): any {\n return [\n ...MCSStatusMixinProperties,\n {\n type: 'number',\n name: 'round',\n label: 'round'\n }\n ]\n }\n\n get showMoveHandle() {\n return false\n }\n\n /**\n * id 후보를 조회할 때 백엔드에 보낼 타입명. 값이 있으면 검색(lookup) 대상.\n * 기본은 자기 타입. 자기 타입과 다른 타입으로 조회해야 하는 특수 케이스는 override.\n */\n get idLookupType(): string | undefined {\n return this.state.type\n }\n\n /** MCS 머신 — parentObject(부모 머신) 후보 수집 시 머신으로 식별. */\n get isMcsMachine(): boolean {\n return true\n }\n\n containable(component: Component) {\n return ['rect', 'ellipse'].includes(component.state.type)\n }\n\n get decorators() {\n return ['decotag']\n }\n\n getTheme() {\n const { legendName } = this.state\n\n if (legendName) {\n return (this.root as any)?.style?.[legendName]\n }\n }\n\n get hasTextProperty() {\n return true\n }\n\n get status() {\n return this.data?.STATUS\n }\n\n get auxStatus() {\n return this.data?.STATUS\n }\n\n get legend(): Legend {\n return this.getTheme() || this.getLegendFallback()\n }\n\n get auxLegend() {\n return LEGEND_MACHINE\n }\n\n get auxColor() {\n return this.state.strokeStyle\n }\n\n getLegendFallback() {\n return LEGEND_MACHINE\n }\n\n /**\n * In 2D, ignore `state.alpha`; preserve everything else.\n *\n * things-scene's base prerender (component-drawing.ts:31, 44) does:\n * const fade = (alpha || 1) - this._delta.fade\n * if (fade) context.globalAlpha = fade\n *\n * It *overwrites* `globalAlpha` (not multiplies), so the inherited\n * parent alpha is also lost — that's a base-layer quirk we match the\n * recovery for. Then at line 48 it applies editMode+hidden dimming\n * with a *= 0.5.\n *\n * Our recovery:\n * 1. Capture the inherited `globalAlpha` *before* super runs.\n * 2. Let super do its work (transforms / shadow / dim flags).\n * 3. Restore globalAlpha to inherited × editMode-hidden dim factor —\n * i.e., what super would have produced if our state.alpha were 1.\n *\n * 3D path is unaffected: 3D factories read `state.alpha` directly via\n * their `updateAlpha()` implementations and continue to honor it (with\n * per-config opacity fallback).\n */\n prerender(ctx: any) {\n const inherited = ctx.globalAlpha\n super.prerender(ctx)\n const dim = this.app.isEditMode && this.hidden ? 0.5 : 1\n ctx.globalAlpha = inherited * dim\n }\n\n render(ctx) {\n var { round = 0, lineWidth } = this.state\n var { left, top, width, height } = this.bounds\n\n ctx.beginPath()\n\n ctx.strokeStyle = this.auxColor\n ctx.fillStyle = this.statusColor!\n ctx.lineWidth = lineWidth || 1\n ctx.lineCap = 'round'\n ctx.setLineDash([])\n\n if (round > 0) {\n ctx.roundRect(left, top, width, height, round)\n } else {\n ctx.rect(left, top, width, height)\n }\n\n ctx.fill()\n ctx.stroke()\n }\n\n // get text() {\n // if (this.data?.LOCALENAME) {\n // // TODO 왜 LOCALENAME 데이타는 문자열인가 ? 오브젝트로 보내달라.\n // return JSON.parse(this.data.LOCALENAME)[getLocaleLanguage()]\n // }\n\n // return super.text\n // }\n\n // set text(text) {\n // super.text = text\n // }\n\n async detailInfo() {\n const { type, id } = this.state\n\n if (!id) {\n return\n }\n\n return await getPopupData(type, id)\n }\n}\n"]}
1
+ {"version":3,"file":"mcs-machine.js","sourceRoot":"","sources":["../src/mcs-machine.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ContainerAbstract } from '@hatiolab/things-scene'\nimport { getPopupData } from '@fmsim/api'\n\nimport { MCSStatusMixin, MCSStatusMixinProperties } from './features/mcs-status-mixin'\n\nimport { LEGEND_MACHINE, Legend } from './features/mcs-status-default'\n\n/**\n * MCS Machine의 공통 속성을 정의한 최상위 오브젝트\n * - Container\n * -\n */\nexport default class MCSMachine extends MCSStatusMixin(ContainerAbstract) {\n static get properties(): any {\n return [\n ...MCSStatusMixinProperties,\n {\n type: 'number',\n name: 'round',\n label: 'round'\n }\n ]\n }\n\n get showMoveHandle() {\n return false\n }\n\n /**\n * id 후보를 조회할 때 백엔드에 보낼 타입명. 값이 있으면 검색(lookup) 대상.\n * 기본은 자기 타입. 자기 타입과 다른 타입으로 조회해야 하는 특수 케이스는 override.\n */\n get idLookupType(): string | undefined {\n return this.state.type\n }\n\n /** MCS 머신 — parentObject(부모 머신) 후보 수집 시 머신으로 식별. */\n get isMcsMachine(): boolean {\n return true\n }\n\n containable(component: Component) {\n return ['rect', 'ellipse'].includes(component.state.type)\n }\n\n get decorators() {\n return ['decotag']\n }\n\n getTheme() {\n const { legendName } = this.state\n\n if (legendName) {\n return (this.root as any)?.style?.[legendName]\n }\n }\n\n get hasTextProperty() {\n return true\n }\n\n get status() {\n return this.data?.STATUS\n }\n\n get auxStatus() {\n return this.data?.STATUS\n }\n\n get legend(): Legend {\n return this.getTheme() || this.getLegendFallback()\n }\n\n get auxLegend() {\n return LEGEND_MACHINE\n }\n\n get auxColor() {\n return this.state.strokeStyle\n }\n\n getLegendFallback() {\n return LEGEND_MACHINE\n }\n\n /**\n * 2D 에서는 `state.alpha`(3D material 전용)를 무시한다. hidden/모델링 dim 흐림은 **코어(draw)가\n * 일괄 처리**하므로 여기서 손대지 않는다 (component-drawing.ts draw 가 prerender 직후 faint 적용).\n * fade 애니메이션은 보존. 3D 는 factory `updateAlpha()` 가 state.alpha 를 별도로 존중.\n */\n prerender(ctx: any) {\n super.prerender(ctx)\n ctx.globalAlpha = 1 - this._delta.fade\n }\n\n render(ctx) {\n var { round = 0, lineWidth } = this.state\n var { left, top, width, height } = this.bounds\n\n ctx.beginPath()\n\n ctx.strokeStyle = this.auxColor\n ctx.fillStyle = this.statusColor!\n ctx.lineWidth = lineWidth || 1\n ctx.lineCap = 'round'\n ctx.setLineDash([])\n\n if (round > 0) {\n ctx.roundRect(left, top, width, height, round)\n } else {\n ctx.rect(left, top, width, height)\n }\n\n ctx.fill()\n ctx.stroke()\n }\n\n // get text() {\n // if (this.data?.LOCALENAME) {\n // // TODO 왜 LOCALENAME 데이타는 문자열인가 ? 오브젝트로 보내달라.\n // return JSON.parse(this.data.LOCALENAME)[getLocaleLanguage()]\n // }\n\n // return super.text\n // }\n\n // set text(text) {\n // super.text = text\n // }\n\n async detailInfo() {\n const { type, id } = this.state\n\n if (!id) {\n return\n }\n\n return await getPopupData(type, id)\n }\n}\n"]}
package/dist/mcs-unit.js CHANGED
@@ -51,14 +51,11 @@ export default class MCSUnit extends MCSStatusMixin(ParentObjectMixin(ContainerA
51
51
  return LEGEND_UNIT;
52
52
  }
53
53
  /**
54
- * In 2D, ignore `state.alpha`; preserve inherited alpha + editMode-hidden dim.
55
- * See MCSMachine.prerender for the full rationale.
54
+ * 2D 에서는 state.alpha 무시. hidden/모델링 dim 흐림은 코어(draw)가 일괄 처리 (MCSMachine 동일).
56
55
  */
57
56
  prerender(ctx) {
58
- const inherited = ctx.globalAlpha;
59
57
  super.prerender(ctx);
60
- const dim = this.app.isEditMode && this.hidden ? 0.5 : 1;
61
- ctx.globalAlpha = inherited * dim;
58
+ ctx.globalAlpha = 1 - this._delta.fade;
62
59
  }
63
60
  contains(x, y) {
64
61
  var { left, top, width, height, lineWidth = 0 } = this.state;
@@ -1 +1 @@
1
- {"version":3,"file":"mcs-unit.js","sourceRoot":"","sources":["../src/mcs-unit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AACtF,OAAO,EAAE,WAAW,EAAU,MAAM,+BAA+B,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AAE/F;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,cAAc,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IACvF,MAAM,KAAK,UAAU;QACnB,OAAO,CAAC,GAAG,wBAAwB,EAAE,GAAG,2BAA2B,CAAC,CAAA;IACtE,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;OAGG;IACH,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAA;IACxB,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,UAAU;QACZ,OAAO,CAAC,SAAS,CAAC,CAAA;IACpB,CAAC;IAED,QAAQ;;QACN,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAEjC,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,MAAA,MAAC,IAAI,CAAC,IAAY,0CAAE,KAAK,0CAAG,UAAU,CAAC,CAAA;QAChD,CAAC;IACH,CAAC;IAED,IAAI,MAAM;;QACR,OAAO,MAAA,IAAI,CAAC,IAAI,0CAAE,MAAM,CAAA;IAC1B,CAAC;IAED,IAAI,SAAS;;QACX,OAAO,MAAA,IAAI,CAAC,IAAI,0CAAE,MAAM,CAAA;IAC1B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAA;IACpD,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED,iBAAiB;QACf,OAAO,WAAW,CAAA;IACpB,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,GAAQ;QAChB,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAA;QACjC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QACpB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACxD,GAAG,CAAC,WAAW,GAAG,SAAS,GAAG,GAAG,CAAA;IACnC,CAAC;IAED,QAAQ,CAAC,CAAC,EAAE,CAAC;QACX,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAC5D,IAAI,MAAM,GAAG,SAAS,GAAG,CAAC,CAAA;QAE1B,OAAO,CACL,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,MAAM;YACzC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,MAAM;YACzC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;YACxC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CACzC,CAAA;IACH,CAAC;IAED,IAAI,IAAI,CAAC,IAAI;QACX,IAAI,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QACtB,IAAI,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QAE1B,IAAI,CAAC,GAAG,CAAC;YACP,IAAI,EAAE,QAAQ,CAAC,CAAC;YAChB,GAAG,EAAE,QAAQ,CAAC,CAAC;YACf,KAAK,EAAE,YAAY,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;YAClC,MAAM,EAAE,YAAY,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;SACpC,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,IAAI;QACN,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAE7D,OAAO;YACL;gBACE,CAAC,EAAE,IAAI;gBACP,CAAC,EAAE,GAAG;aACP;YACD;gBACE,CAAC,EAAE,IAAI,GAAG,KAAK;gBACf,CAAC,EAAE,GAAG;aACP;YACD;gBACE,CAAC,EAAE,IAAI,GAAG,KAAK;gBACf,CAAC,EAAE,GAAG,GAAG,MAAM;aAChB;YACD;gBACE,CAAC,EAAE,IAAI;gBACP,CAAC,EAAE,GAAG,GAAG,MAAM;aAChB;SACF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAE/B,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAM;QACR,CAAC;QAED,OAAO,MAAM,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IACrC,CAAC;CACF","sourcesContent":["import { ContainerAbstract } from '@hatiolab/things-scene'\nimport { getPopupData } from '@fmsim/api'\n\nimport { MCSStatusMixin, MCSStatusMixinProperties } from './features/mcs-status-mixin'\nimport { LEGEND_UNIT, Legend } from './features/mcs-status-default'\nimport { ParentObjectMixin, ParentObjectMixinProperties } from './features/parent-object-mixin'\n\n/**\n * MCS용 Unit들의 공통 속성을 정의한 오브젝트\n */\nexport default class MCSUnit extends MCSStatusMixin(ParentObjectMixin(ContainerAbstract)) {\n static get properties(): any {\n return [...MCSStatusMixinProperties, ...ParentObjectMixinProperties]\n }\n\n get showMoveHandle() {\n return false\n }\n\n /**\n * id 후보를 조회할 때 백엔드에 보낼 타입명. 값이 있으면 검색(lookup) 대상.\n * 기본은 자기 타입. 특수 케이스는 override.\n */\n get idLookupType(): string | undefined {\n return this.state.type\n }\n\n get hasTextProperty() {\n return true\n }\n\n get decorators() {\n return ['decotag']\n }\n\n getTheme() {\n const { legendName } = this.state\n\n if (legendName) {\n return (this.root as any)?.style?.[legendName]\n }\n }\n\n get status() {\n return this.data?.STATUS\n }\n\n get auxStatus() {\n return this.data?.STATUS\n }\n\n get legend(): Legend {\n return this.getTheme() || this.getLegendFallback()\n }\n\n get auxLegend() {\n return this.legend\n }\n\n getLegendFallback() {\n return LEGEND_UNIT\n }\n\n /**\n * In 2D, ignore `state.alpha`; preserve inherited alpha + editMode-hidden dim.\n * See MCSMachine.prerender for the full rationale.\n */\n prerender(ctx: any) {\n const inherited = ctx.globalAlpha\n super.prerender(ctx)\n const dim = this.app.isEditMode && this.hidden ? 0.5 : 1\n ctx.globalAlpha = inherited * dim\n }\n\n contains(x, y) {\n var { left, top, width, height, lineWidth = 0 } = this.state\n var extend = lineWidth / 2\n\n return (\n x < Math.max(left + width, left) + extend &&\n x > Math.min(left + width, left) - extend &&\n y < Math.max(top + height, top) + extend &&\n y > Math.min(top + height, top) - extend\n )\n }\n\n set path(path) {\n var left_top = path[0]\n var right_bottom = path[2]\n\n this.set({\n left: left_top.x,\n top: left_top.y,\n width: right_bottom.x - left_top.x,\n height: right_bottom.y - left_top.y\n })\n }\n\n get path() {\n var { left = 0, top = 0, width = 0, height = 0 } = this.state\n\n return [\n {\n x: left,\n y: top\n },\n {\n x: left + width,\n y: top\n },\n {\n x: left + width,\n y: top + height\n },\n {\n x: left,\n y: top + height\n }\n ]\n }\n\n async detailInfo() {\n const { type, id } = this.state\n\n if (!id) {\n return\n }\n\n return await getPopupData(type, id)\n }\n}\n"]}
1
+ {"version":3,"file":"mcs-unit.js","sourceRoot":"","sources":["../src/mcs-unit.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContainerAbstract } from '@hatiolab/things-scene'\nimport { getPopupData } from '@fmsim/api'\n\nimport { MCSStatusMixin, MCSStatusMixinProperties } from './features/mcs-status-mixin'\nimport { LEGEND_UNIT, Legend } from './features/mcs-status-default'\nimport { ParentObjectMixin, ParentObjectMixinProperties } from './features/parent-object-mixin'\n\n/**\n * MCS용 Unit들의 공통 속성을 정의한 오브젝트\n */\nexport default class MCSUnit extends MCSStatusMixin(ParentObjectMixin(ContainerAbstract)) {\n static get properties(): any {\n return [...MCSStatusMixinProperties, ...ParentObjectMixinProperties]\n }\n\n get showMoveHandle() {\n return false\n }\n\n /**\n * id 후보를 조회할 때 백엔드에 보낼 타입명. 값이 있으면 검색(lookup) 대상.\n * 기본은 자기 타입. 특수 케이스는 override.\n */\n get idLookupType(): string | undefined {\n return this.state.type\n }\n\n get hasTextProperty() {\n return true\n }\n\n get decorators() {\n return ['decotag']\n }\n\n getTheme() {\n const { legendName } = this.state\n\n if (legendName) {\n return (this.root as any)?.style?.[legendName]\n }\n }\n\n get status() {\n return this.data?.STATUS\n }\n\n get auxStatus() {\n return this.data?.STATUS\n }\n\n get legend(): Legend {\n return this.getTheme() || this.getLegendFallback()\n }\n\n get auxLegend() {\n return this.legend\n }\n\n getLegendFallback() {\n return LEGEND_UNIT\n }\n\n /**\n * 2D 에서는 state.alpha 무시. hidden/모델링 dim 흐림은 코어(draw)가 일괄 처리 (MCSMachine 동일).\n */\n prerender(ctx: any) {\n super.prerender(ctx)\n ctx.globalAlpha = 1 - this._delta.fade\n }\n\n contains(x, y) {\n var { left, top, width, height, lineWidth = 0 } = this.state\n var extend = lineWidth / 2\n\n return (\n x < Math.max(left + width, left) + extend &&\n x > Math.min(left + width, left) - extend &&\n y < Math.max(top + height, top) + extend &&\n y > Math.min(top + height, top) - extend\n )\n }\n\n set path(path) {\n var left_top = path[0]\n var right_bottom = path[2]\n\n this.set({\n left: left_top.x,\n top: left_top.y,\n width: right_bottom.x - left_top.x,\n height: right_bottom.y - left_top.y\n })\n }\n\n get path() {\n var { left = 0, top = 0, width = 0, height = 0 } = this.state\n\n return [\n {\n x: left,\n y: top\n },\n {\n x: left + width,\n y: top\n },\n {\n x: left + width,\n y: top + height\n },\n {\n x: left,\n y: top + height\n }\n ]\n }\n\n async detailInfo() {\n const { type, id } = this.state\n\n if (!id) {\n return\n }\n\n return await getPopupData(type, id)\n }\n}\n"]}