@fmsim/machine 2.0.0-beta.62 → 2.0.0-beta.64
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.js +32 -1
- package/dist/carrier.js.map +1 -1
- package/dist/factories/machine-3d.js +24 -4
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/mcs-machine.js +28 -0
- package/dist/mcs-machine.js.map +1 -1
- package/dist/mcs-unit.js +10 -0
- package/dist/mcs-unit.js.map +1 -1
- package/dist/templates/backdrop.js +30 -0
- package/dist/templates/backdrop.js.map +1 -0
- package/dist/templates/decal.js +21 -0
- package/dist/templates/decal.js.map +1 -0
- package/dist/templates/index.js +5 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/icons/backdrop.png +0 -0
- package/icons/decal.png +0 -0
- package/package.json +2 -2
- package/src/carrier.ts +32 -1
- package/src/factories/machine-3d.ts +26 -3
- package/src/mcs-machine.ts +29 -0
- package/src/mcs-unit.ts +11 -0
- package/src/templates/backdrop.ts +30 -0
- package/src/templates/decal.ts +21 -0
- package/src/templates/index.ts +5 -1
- package/translations/en.json +15 -1
- package/translations/ja.json +15 -1
- package/translations/ko.json +15 -1
- package/translations/ms.json +15 -1
- package/translations/zh.json +15 -1
|
Binary file
|
package/icons/decal.png
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@fmsim/machine",
|
|
3
3
|
"description": "Layout View를 구성하는 설비(machine)들과 유닛을 구현한 모듈입니다.",
|
|
4
4
|
"author": "heartyoh",
|
|
5
|
-
"version": "2.0.0-beta.
|
|
5
|
+
"version": "2.0.0-beta.64",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"things-scene": true,
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"prettier --write"
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "5a69f5f54cea6183667429f6b7270d26a61ee893"
|
|
59
59
|
}
|
package/src/carrier.ts
CHANGED
|
@@ -13,6 +13,20 @@ const NATURE: ComponentNature = {
|
|
|
13
13
|
rotatable: false
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Feature flag — keep status text (F / E / X) screen-axis-aligned even
|
|
18
|
+
* when the carrier (or its parent holder, or an in-flight reparent
|
|
19
|
+
* tween) is rotated. Counter-rotates ONLY the text; the polygon path
|
|
20
|
+
* still rotates with the carrier as before.
|
|
21
|
+
*
|
|
22
|
+
* ROLLBACK procedure (if customer decides to revert):
|
|
23
|
+
* 1. Flip this flag to `false` — text rotates with carrier again.
|
|
24
|
+
* 2. Or delete this constant and the — fully strip the feature: keep
|
|
25
|
+
* `if (STATUS_TEXT_COUNTER_ROTATE)` only the `else` branch in
|
|
26
|
+
* branch in `render()`. `render()` (the original code).
|
|
27
|
+
*/
|
|
28
|
+
const STATUS_TEXT_COUNTER_ROTATE = true
|
|
29
|
+
|
|
16
30
|
@sceneComponent('Carrier')
|
|
17
31
|
export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
|
|
18
32
|
static get nature() {
|
|
@@ -284,7 +298,24 @@ export default class Carrier extends MCSStatusMixin(ParentObjectMixin(Shape)) {
|
|
|
284
298
|
ctx.textAlign = 'center'
|
|
285
299
|
ctx.textBaseline = 'middle'
|
|
286
300
|
|
|
287
|
-
|
|
301
|
+
if (STATUS_TEXT_COUNTER_ROTATE) {
|
|
302
|
+
// Counter-rotate so the status label stays screen-axis-aligned
|
|
303
|
+
// even when the carrier (or its parent holder, or an in-flight
|
|
304
|
+
// reparent transition) is rotated. Reading the current cumulative
|
|
305
|
+
// rotation from the canvas transform handles all sources of
|
|
306
|
+
// rotation uniformly (state.rotation, parent transforms,
|
|
307
|
+
// animation tweens) — no need to enumerate them.
|
|
308
|
+
const m = ctx.getTransform()
|
|
309
|
+
const cumulRotation = Math.atan2(m.b, m.a)
|
|
310
|
+
ctx.save()
|
|
311
|
+
ctx.translate(cx, cy)
|
|
312
|
+
ctx.rotate(-cumulRotation)
|
|
313
|
+
ctx.fillText(text, -width / 8, height / 8)
|
|
314
|
+
ctx.restore()
|
|
315
|
+
} else {
|
|
316
|
+
// Original — text rotates with the carrier.
|
|
317
|
+
ctx.fillText(text, cx - width / 8, cy + height / 8)
|
|
318
|
+
}
|
|
288
319
|
}
|
|
289
320
|
}
|
|
290
321
|
}
|
|
@@ -79,17 +79,26 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
79
79
|
// width, height 절대값 (음수 값은 양수로 취급).
|
|
80
80
|
// `resizable:false` 컴포넌트(예: Scanner)는 state.width/height 가 없고
|
|
81
81
|
// bounds 만 있을 수 있으므로 bounds 를 fallback 으로 사용.
|
|
82
|
+
//
|
|
83
|
+
// `??` only catches null/undefined — a property-panel input that produced
|
|
84
|
+
// NaN would slip through and corrupt every downstream Math.* call. Use a
|
|
85
|
+
// finite check so NaN falls through to bounds → 10 like missing values do.
|
|
82
86
|
protected get absSize(): { w: number; h: number } {
|
|
83
87
|
const state = (this.component.state || {}) as any
|
|
84
88
|
const bounds = (this.component.bounds || {}) as any
|
|
85
|
-
const width = state.width
|
|
86
|
-
const height = state.height
|
|
89
|
+
const width = pickFinite(state.width, bounds.width, 10)
|
|
90
|
+
const height = pickFinite(state.height, bounds.height, 10)
|
|
87
91
|
return { w: Math.abs(width), h: Math.abs(height) }
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
get effectiveDepth(): number {
|
|
91
95
|
const { depth } = this.component.state
|
|
92
|
-
if (depth)
|
|
96
|
+
// Truthy `if (depth)` already rejected NaN/0/undefined, but be explicit
|
|
97
|
+
// so the intent (and the resolution chain) reads cleanly: only an
|
|
98
|
+
// explicit positive finite number wins; everything else → defaults.
|
|
99
|
+
if (typeof depth === 'number' && Number.isFinite(depth) && depth > 0) {
|
|
100
|
+
return depth
|
|
101
|
+
}
|
|
93
102
|
if (this._config.defaultDepth) return this._config.defaultDepth
|
|
94
103
|
const { w, h } = this.absSize
|
|
95
104
|
return Math.min(w, h) * this._config.depthRatio
|
|
@@ -463,6 +472,20 @@ class MCSRealObject3D extends RealObjectGroup {
|
|
|
463
472
|
}
|
|
464
473
|
}
|
|
465
474
|
|
|
475
|
+
// ── 헬퍼 ────────────────────────────────────────────────
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Returns the first argument that is a finite number. NaN slips past `??`
|
|
479
|
+
* (which only catches null/undefined), so without this a property-panel
|
|
480
|
+
* input producing NaN would corrupt absSize and cascade through Math.*.
|
|
481
|
+
*/
|
|
482
|
+
function pickFinite(...candidates: unknown[]): number {
|
|
483
|
+
for (const c of candidates) {
|
|
484
|
+
if (typeof c === 'number' && Number.isFinite(c)) return c
|
|
485
|
+
}
|
|
486
|
+
return 0
|
|
487
|
+
}
|
|
488
|
+
|
|
466
489
|
// ── 팩토리 등록 헬퍼 ─────────────────────────────────────
|
|
467
490
|
|
|
468
491
|
function register(type: string, overrides: Partial<MCS3DConfig> = {}) {
|
package/src/mcs-machine.ts
CHANGED
|
@@ -70,6 +70,35 @@ export default class MCSMachine extends MCSStatusMixin(ContainerAbstract) {
|
|
|
70
70
|
return LEGEND_MACHINE
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* In 2D, ignore `state.alpha`; preserve everything else.
|
|
75
|
+
*
|
|
76
|
+
* things-scene's base prerender (component-drawing.ts:31, 44) does:
|
|
77
|
+
* const fade = (alpha || 1) - this._delta.fade
|
|
78
|
+
* if (fade) context.globalAlpha = fade
|
|
79
|
+
*
|
|
80
|
+
* It *overwrites* `globalAlpha` (not multiplies), so the inherited
|
|
81
|
+
* parent alpha is also lost — that's a base-layer quirk we match the
|
|
82
|
+
* recovery for. Then at line 48 it applies editMode+hidden dimming
|
|
83
|
+
* with a *= 0.5.
|
|
84
|
+
*
|
|
85
|
+
* Our recovery:
|
|
86
|
+
* 1. Capture the inherited `globalAlpha` *before* super runs.
|
|
87
|
+
* 2. Let super do its work (transforms / shadow / dim flags).
|
|
88
|
+
* 3. Restore globalAlpha to inherited × editMode-hidden dim factor —
|
|
89
|
+
* i.e., what super would have produced if our state.alpha were 1.
|
|
90
|
+
*
|
|
91
|
+
* 3D path is unaffected: 3D factories read `state.alpha` directly via
|
|
92
|
+
* their `updateAlpha()` implementations and continue to honor it (with
|
|
93
|
+
* per-config opacity fallback).
|
|
94
|
+
*/
|
|
95
|
+
prerender(ctx: any) {
|
|
96
|
+
const inherited = ctx.globalAlpha
|
|
97
|
+
super.prerender(ctx)
|
|
98
|
+
const dim = this.app.isEditMode && this.hidden ? 0.5 : 1
|
|
99
|
+
ctx.globalAlpha = inherited * dim
|
|
100
|
+
}
|
|
101
|
+
|
|
73
102
|
render(ctx) {
|
|
74
103
|
var { round = 0, lineWidth } = this.state
|
|
75
104
|
var { left, top, width, height } = this.bounds
|
package/src/mcs-unit.ts
CHANGED
|
@@ -49,6 +49,17 @@ export default class MCSUnit extends MCSStatusMixin(ParentObjectMixin(ContainerA
|
|
|
49
49
|
return LEGEND_UNIT
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* In 2D, ignore `state.alpha`; preserve inherited alpha + editMode-hidden dim.
|
|
54
|
+
* See MCSMachine.prerender for the full rationale.
|
|
55
|
+
*/
|
|
56
|
+
prerender(ctx: any) {
|
|
57
|
+
const inherited = ctx.globalAlpha
|
|
58
|
+
super.prerender(ctx)
|
|
59
|
+
const dim = this.app.isEditMode && this.hidden ? 0.5 : 1
|
|
60
|
+
ctx.globalAlpha = inherited * dim
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
contains(x, y) {
|
|
53
64
|
var { left, top, width, height, lineWidth = 0 } = this.state
|
|
54
65
|
var extend = lineWidth / 2
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const icon = new URL('../../icons/backdrop.png', import.meta.url).href
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
type: 'backdrop',
|
|
5
|
+
description: '3D scene backdrop — 6-sided envelope (4 walls + floor + ceiling)',
|
|
6
|
+
group: ['etc'],
|
|
7
|
+
icon,
|
|
8
|
+
model: {
|
|
9
|
+
type: 'backdrop',
|
|
10
|
+
top: 0,
|
|
11
|
+
left: 0,
|
|
12
|
+
width: 3000,
|
|
13
|
+
height: 2400,
|
|
14
|
+
// depth seeded on creation as min(width, height) by Backdrop's
|
|
15
|
+
// `_syncFromRoot(seedDepth=true)`. Editable via property panel
|
|
16
|
+
// afterward — board-resize syncs won't override it.
|
|
17
|
+
fitToBoard: true,
|
|
18
|
+
alpha: 1,
|
|
19
|
+
// 6면 합성 상태. 비어있으면 backdrop 컴포넌트의 default 가 적용됨
|
|
20
|
+
// (벽 4면 = #dde2e8/0.35, 바닥 = #9c9c9c/1.0, 천장 = #e8e8ec/1.0).
|
|
21
|
+
faces: {
|
|
22
|
+
east: { fillStyle: '#dde2e8', visible: true, alpha: 0.35 },
|
|
23
|
+
west: { fillStyle: '#dde2e8', visible: true, alpha: 0.35 },
|
|
24
|
+
north: { fillStyle: '#dde2e8', visible: true, alpha: 0.35 },
|
|
25
|
+
south: { fillStyle: '#dde2e8', visible: true, alpha: 0.35 },
|
|
26
|
+
floor: { fillStyle: '#9c9c9c', visible: true, alpha: 1.0 },
|
|
27
|
+
ceiling: { fillStyle: '#e8e8ec', visible: true, alpha: 1.0 }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const icon = new URL('../../icons/decal.png', import.meta.url).href
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
type: 'decal',
|
|
5
|
+
description: 'flat surface decal (3D-only) — floor zone paint, path lanes, signage',
|
|
6
|
+
group: ['etc'],
|
|
7
|
+
icon,
|
|
8
|
+
model: {
|
|
9
|
+
type: 'decal',
|
|
10
|
+
top: 100,
|
|
11
|
+
left: 100,
|
|
12
|
+
width: 200,
|
|
13
|
+
height: 200,
|
|
14
|
+
zPos: 0,
|
|
15
|
+
depth: 0,
|
|
16
|
+
fillStyle: '#cccccc',
|
|
17
|
+
strokeStyle: '#999999',
|
|
18
|
+
lineWidth: 1,
|
|
19
|
+
alpha: 1
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/templates/index.ts
CHANGED
|
@@ -21,6 +21,8 @@ import Scanner from './scanner'
|
|
|
21
21
|
import Node from './node'
|
|
22
22
|
import DataSubscription from './data-subscription'
|
|
23
23
|
import FloatingContainer from './floating-container'
|
|
24
|
+
import Decal from './decal'
|
|
25
|
+
import Backdrop from './backdrop'
|
|
24
26
|
|
|
25
27
|
export default [
|
|
26
28
|
StockerCapacityBar,
|
|
@@ -45,5 +47,7 @@ export default [
|
|
|
45
47
|
Scanner,
|
|
46
48
|
Node,
|
|
47
49
|
FloatingContainer,
|
|
48
|
-
DataSubscription
|
|
50
|
+
DataSubscription,
|
|
51
|
+
Decal,
|
|
52
|
+
Backdrop
|
|
49
53
|
]
|
package/translations/en.json
CHANGED
|
@@ -47,5 +47,19 @@
|
|
|
47
47
|
"label.nodes": "nodes",
|
|
48
48
|
"label.vehicle-animation": "vehicle animation",
|
|
49
49
|
"label.battery-rate-legend-name": "battery rate",
|
|
50
|
-
"label.conveyor-orientation": "Transfer Direction"
|
|
50
|
+
"label.conveyor-orientation": "Transfer Direction",
|
|
51
|
+
"component.backdrop": "backdrop",
|
|
52
|
+
"label.fit-to-board": "Fit to Board",
|
|
53
|
+
"label.east": "East",
|
|
54
|
+
"label.west": "West",
|
|
55
|
+
"label.north": "North",
|
|
56
|
+
"label.south": "South",
|
|
57
|
+
"label.floor": "Floor",
|
|
58
|
+
"label.ceiling": "Ceiling",
|
|
59
|
+
"label.visible": "Visible",
|
|
60
|
+
"label.alpha": "Alpha",
|
|
61
|
+
"label.rotation": "Rotation",
|
|
62
|
+
"label.mirror": "Mirror",
|
|
63
|
+
"label.copy-to-opposite": "Copy to opposite",
|
|
64
|
+
"label.copy-to-walls": "Copy to all walls"
|
|
51
65
|
}
|
package/translations/ja.json
CHANGED
|
@@ -49,5 +49,19 @@
|
|
|
49
49
|
"label.nodes": "nodes",
|
|
50
50
|
"label.vehicle-animation": "vehicle animation",
|
|
51
51
|
"label.battery-rate-legend-name": "battery rate",
|
|
52
|
-
"label.conveyor-orientation": "搬送方向"
|
|
52
|
+
"label.conveyor-orientation": "搬送方向",
|
|
53
|
+
"component.backdrop": "バックドロップ",
|
|
54
|
+
"label.fit-to-board": "ボードに合わせる",
|
|
55
|
+
"label.east": "東壁",
|
|
56
|
+
"label.west": "西壁",
|
|
57
|
+
"label.north": "北壁",
|
|
58
|
+
"label.south": "南壁",
|
|
59
|
+
"label.floor": "床",
|
|
60
|
+
"label.ceiling": "天井",
|
|
61
|
+
"label.visible": "表示",
|
|
62
|
+
"label.alpha": "透明度",
|
|
63
|
+
"label.rotation": "回転",
|
|
64
|
+
"label.mirror": "鏡像",
|
|
65
|
+
"label.copy-to-opposite": "反対側にコピー",
|
|
66
|
+
"label.copy-to-walls": "壁4面にコピー"
|
|
53
67
|
}
|
package/translations/ko.json
CHANGED
|
@@ -50,5 +50,19 @@
|
|
|
50
50
|
"label.nodes": "nodes",
|
|
51
51
|
"label.vehicle-animation": "vehicle animation",
|
|
52
52
|
"label.battery-rate-legend-name": "배터리 잔량",
|
|
53
|
-
"label.conveyor-orientation": "이송방향"
|
|
53
|
+
"label.conveyor-orientation": "이송방향",
|
|
54
|
+
"component.backdrop": "배경",
|
|
55
|
+
"label.fit-to-board": "보드에 맞춤",
|
|
56
|
+
"label.east": "동측 벽",
|
|
57
|
+
"label.west": "서측 벽",
|
|
58
|
+
"label.north": "북측 벽",
|
|
59
|
+
"label.south": "남측 벽",
|
|
60
|
+
"label.floor": "바닥",
|
|
61
|
+
"label.ceiling": "천장",
|
|
62
|
+
"label.visible": "표시",
|
|
63
|
+
"label.alpha": "투명도",
|
|
64
|
+
"label.rotation": "회전",
|
|
65
|
+
"label.mirror": "거울상",
|
|
66
|
+
"label.copy-to-opposite": "반대편에 복사",
|
|
67
|
+
"label.copy-to-walls": "벽 4면에 복사"
|
|
54
68
|
}
|
package/translations/ms.json
CHANGED
|
@@ -49,5 +49,19 @@
|
|
|
49
49
|
"label.nodes": "nodes",
|
|
50
50
|
"label.vehicle-animation": "vehicle animation",
|
|
51
51
|
"label.battery-rate-legend-name": "battery rate",
|
|
52
|
-
"label.conveyor-orientation": "Transfer Direction"
|
|
52
|
+
"label.conveyor-orientation": "Transfer Direction",
|
|
53
|
+
"component.backdrop": "Backdrop",
|
|
54
|
+
"label.fit-to-board": "Muat Papan",
|
|
55
|
+
"label.east": "Timur",
|
|
56
|
+
"label.west": "Barat",
|
|
57
|
+
"label.north": "Utara",
|
|
58
|
+
"label.south": "Selatan",
|
|
59
|
+
"label.floor": "Lantai",
|
|
60
|
+
"label.ceiling": "Siling",
|
|
61
|
+
"label.visible": "Kelihatan",
|
|
62
|
+
"label.alpha": "Alfa",
|
|
63
|
+
"label.rotation": "Putaran",
|
|
64
|
+
"label.mirror": "Cermin",
|
|
65
|
+
"label.copy-to-opposite": "Salin ke bertentangan",
|
|
66
|
+
"label.copy-to-walls": "Salin ke semua dinding"
|
|
53
67
|
}
|
package/translations/zh.json
CHANGED
|
@@ -49,5 +49,19 @@
|
|
|
49
49
|
"label.nodes": "nodes",
|
|
50
50
|
"label.vehicle-animation": "vehicle animation",
|
|
51
51
|
"label.battery-rate-legend-name": "battery rate",
|
|
52
|
-
"label.conveyor-orientation": "输送方向"
|
|
52
|
+
"label.conveyor-orientation": "输送方向",
|
|
53
|
+
"component.backdrop": "背景",
|
|
54
|
+
"label.fit-to-board": "适配画板",
|
|
55
|
+
"label.east": "东墙",
|
|
56
|
+
"label.west": "西墙",
|
|
57
|
+
"label.north": "北墙",
|
|
58
|
+
"label.south": "南墙",
|
|
59
|
+
"label.floor": "地板",
|
|
60
|
+
"label.ceiling": "天花板",
|
|
61
|
+
"label.visible": "显示",
|
|
62
|
+
"label.alpha": "透明度",
|
|
63
|
+
"label.rotation": "旋转",
|
|
64
|
+
"label.mirror": "镜像",
|
|
65
|
+
"label.copy-to-opposite": "复制到对面",
|
|
66
|
+
"label.copy-to-walls": "复制到4面墙"
|
|
53
67
|
}
|