@fmsim/machine 2.0.1 → 3.0.0-beta.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/agv.js +2 -3
- package/dist/agv.js.map +1 -1
- package/dist/carrier.js +53 -43
- package/dist/carrier.js.map +1 -1
- package/dist/crane.js +37 -2
- package/dist/crane.js.map +1 -1
- package/dist/editors/index.js +2 -0
- package/dist/editors/index.js.map +1 -1
- package/dist/editors/load-layout-host.js +96 -0
- package/dist/editors/load-layout-host.js.map +1 -0
- package/dist/editors/ox-load-layout-panel.js +124 -0
- package/dist/editors/ox-load-layout-panel.js.map +1 -0
- package/dist/factories/crane-3d.js +77 -18
- package/dist/factories/crane-3d.js.map +1 -1
- package/dist/factories/machine-3d.js +29 -52
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/features/capacity-gauge-mixin.js +2 -3
- package/dist/features/capacity-gauge-mixin.js.map +1 -1
- package/dist/features/load-layout-holder.js +77 -0
- package/dist/features/load-layout-holder.js.map +1 -0
- package/dist/features/mcs-status-default.js +30 -7
- package/dist/features/mcs-status-default.js.map +1 -1
- package/dist/features/mcs-status-mixin.js +3 -3
- package/dist/features/mcs-status-mixin.js.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/mcs-carrier-holder.js +287 -82
- package/dist/mcs-carrier-holder.js.map +1 -1
- package/dist/oht.js +4 -0
- package/dist/oht.js.map +1 -1
- package/dist/port.js +6 -1
- package/dist/port.js.map +1 -1
- package/dist/scene/root-container-override.js +5 -11
- package/dist/scene/root-container-override.js.map +1 -1
- package/dist/shelf.js +4 -0
- package/dist/shelf.js.map +1 -1
- package/dist/shuttle.js +4 -0
- package/dist/shuttle.js.map +1 -1
- package/dist/stocker-carrier-bar.js +4 -4
- package/dist/stocker-carrier-bar.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/carrier-type-code.js +10 -0
- package/dist/utils/carrier-type-code.js.map +1 -0
- package/dist/utils/crane-forks.js +5 -0
- package/dist/utils/crane-forks.js.map +1 -0
- package/dist/utils/layout-issue-text.js +16 -0
- package/dist/utils/layout-issue-text.js.map +1 -0
- package/dist/utils/layout-issue.js +16 -0
- package/dist/utils/layout-issue.js.map +1 -0
- package/dist/utils/load-address.js +217 -0
- package/dist/utils/load-address.js.map +1 -0
- package/dist/utils/stocker-gauge.js +3 -2
- package/dist/utils/stocker-gauge.js.map +1 -1
- package/jest.config.js +9 -1
- package/package.json +9 -6
- package/src/agv.ts +2 -3
- package/src/carrier.ts +50 -41
- package/src/crane.ts +39 -2
- package/src/editors/index.ts +2 -0
- package/src/editors/load-layout-host.ts +121 -0
- package/src/editors/ox-load-layout-panel.ts +129 -0
- package/src/factories/crane-3d.ts +74 -20
- package/src/factories/machine-3d.ts +26 -49
- package/src/features/capacity-gauge-mixin.ts +2 -3
- package/src/features/load-layout-holder.ts +83 -0
- package/src/features/mcs-status-default.ts +29 -10
- package/src/features/mcs-status-mixin.ts +3 -7
- package/src/index.ts +7 -0
- package/src/mcs-carrier-holder.ts +319 -78
- package/src/oht.ts +5 -0
- package/src/port.ts +7 -1
- package/src/scene/root-container-override.ts +4 -14
- package/src/shelf.ts +5 -0
- package/src/shuttle.ts +5 -0
- package/src/stocker-carrier-bar.ts +4 -4
- package/src/utils/carrier-type-code.ts +9 -0
- package/src/utils/crane-forks.ts +4 -0
- package/src/utils/layout-issue-text.ts +18 -0
- package/src/utils/layout-issue.ts +41 -0
- package/src/utils/load-address.ts +270 -0
- package/src/utils/stocker-gauge.ts +3 -3
- package/translations/en.json +42 -1
- package/translations/ja.json +42 -1
- package/translations/ko.json +59 -18
- package/translations/ms.json +42 -1
- package/translations/zh.json +42 -1
- package/dist/carrier-manager.js +0 -191
- package/dist/carrier-manager.js.map +0 -1
- package/dist/features/animation-default.js +0 -6
- package/dist/features/animation-default.js.map +0 -1
- package/dist/utils/get-value-on-ranges.js +0 -21
- package/dist/utils/get-value-on-ranges.js.map +0 -1
- package/src/carrier-manager.ts +0 -231
- package/src/features/animation-default.ts +0 -13
- package/src/utils/get-value-on-ranges.ts +0 -23
package/dist/carrier-manager.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { Model } from '@hatiolab/things-scene';
|
|
2
|
-
import { parseIfJsonString } from './utils/data-value';
|
|
3
|
-
// 사라진 carrier 의 dispose 지연. 같은 macrotask 안에서 holder A 의 사라짐
|
|
4
|
-
// 보고와 holder B 의 등장 보고가 순차로 들어오는 reparent 시나리오 (= 같은
|
|
5
|
-
// frame snapshot 의 여러 record 적용) 를 보호하기 위해 setTimeout(0) 의
|
|
6
|
-
// next-macrotask delay 만 둔다. 과거 1000ms grace 가 있었으나 seek 시 2단계
|
|
7
|
-
// 갱신 (1초 후 carrier 사라짐) 부작용이 명확하고 frame 누락 bouncing 깜빡임
|
|
8
|
-
// 보호 효용은 작다 판단되어 0 으로 단축 (2026-06-09).
|
|
9
|
-
const DYING_TIMEOUT = 0;
|
|
10
|
-
export default class CarrierManager {
|
|
11
|
-
constructor(root) {
|
|
12
|
-
this.carriers = new Map();
|
|
13
|
-
this.dyingTimers = new Map();
|
|
14
|
-
this.root = root;
|
|
15
|
-
}
|
|
16
|
-
get(carrierName) {
|
|
17
|
-
return this.carriers.get(carrierName);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Holder 의 data 변경에 대응해 carrier pool 을 동기화.
|
|
21
|
-
*
|
|
22
|
-
* 정책:
|
|
23
|
-
* - holder.data 가 *single source of truth*. 대표(CARRIERNAME) +
|
|
24
|
-
* ADDITIONS[] 를 하나의 *stack list* 로 정규화한 뒤 before/after 의
|
|
25
|
-
* 이름 set diff 로 추가/제거/유지 분기.
|
|
26
|
-
* - "대표" 와 "ADDITIONS" 는 *holder.data 안의 키 위치 차이일 뿐* 이고,
|
|
27
|
-
* carrier 인스턴스끼리는 **종속/그룹 관계 없음 — 모두 독립 동등**.
|
|
28
|
-
* stack 안의 위치 (stackIndex) 만 carrier.state 에 주입한다. 2D render
|
|
29
|
-
* skip 등의 판정은 *위치 (= parent.components 의 마지막 여부)* 로 한다.
|
|
30
|
-
* - 동일 CARRIERNAME 이 *다른 holder* 에 새로 등장하면 reparent (애니메이션).
|
|
31
|
-
* *해당 carrier 만 이동* — 같은 stack 의 다른 carrier 는 따라가지 않는다.
|
|
32
|
-
* - 사라진 carrier 는 next-macrotask 에 dispose. 같은 macrotask 안에서 다른
|
|
33
|
-
* holder 가 같은 이름을 보고하면 cancel 되어 reparent 로 전환.
|
|
34
|
-
*/
|
|
35
|
-
update(holder, after, before) {
|
|
36
|
-
const afterStack = this._buildStack(after);
|
|
37
|
-
const beforeStack = this._buildStack(before);
|
|
38
|
-
const afterNames = new Set(afterStack.map(i => i.CARRIERNAME));
|
|
39
|
-
const beforeNames = new Set(beforeStack.map(i => i.CARRIERNAME));
|
|
40
|
-
// 1) 사라진 것 → dying schedule (자기 holder 의 자식인 carrier 만)
|
|
41
|
-
for (const name of beforeNames) {
|
|
42
|
-
if (afterNames.has(name))
|
|
43
|
-
continue;
|
|
44
|
-
const carrier = this.carriers.get(name);
|
|
45
|
-
if (carrier && carrier.parent === holder) {
|
|
46
|
-
this.scheduleDying(name);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// showText 는 *현재 소유 홀더* 의 showCarrierText 를 항상 따른다 — 생성 시점에만
|
|
50
|
-
// 고정하면 동일 이름 carrier 가 다른 홀더(예: 분체 Crane)로 이동/보고될 때
|
|
51
|
-
// off 를 무시하고 표시(F/E/X)가 남는다. 매 update 마다 재동기화한다.
|
|
52
|
-
const showCarrierText = holder.getState('showCarrierText') || false;
|
|
53
|
-
// 2) afterStack 의 *순서대로* 처리 — bottom (ADDITIONS[0]) 부터 top (대표)
|
|
54
|
-
// 그래야 parent.components 가 자연스럽게 같은 순서로 정렬되어 stack index
|
|
55
|
-
// 가 일관된다.
|
|
56
|
-
afterStack.forEach((item, idx) => {
|
|
57
|
-
const name = item.CARRIERNAME;
|
|
58
|
-
this.cancelDying(name);
|
|
59
|
-
let carrier = this.carriers.get(name);
|
|
60
|
-
const { width, height } = holder.state;
|
|
61
|
-
if (!carrier) {
|
|
62
|
-
carrier = Model.compile({
|
|
63
|
-
id: name,
|
|
64
|
-
type: 'Carrier',
|
|
65
|
-
left: 0,
|
|
66
|
-
top: 0,
|
|
67
|
-
width,
|
|
68
|
-
height,
|
|
69
|
-
showText: showCarrierText,
|
|
70
|
-
stackIndex: idx
|
|
71
|
-
}, this.root.app);
|
|
72
|
-
this.carriers.set(name, carrier);
|
|
73
|
-
carrier.data = item;
|
|
74
|
-
holder.addComponent(carrier, { silent: true });
|
|
75
|
-
}
|
|
76
|
-
else if (carrier.parent !== holder && !beforeNames.has(name)) {
|
|
77
|
-
// 다른 holder 에 있던 carrier 가 이번 update 에서 *새로 등장* 한 경우만
|
|
78
|
-
// reparent. before 에도 같은 이름이 있었으면 ping-pong (옛 시점의
|
|
79
|
-
// 반복 보고) 이므로 trigger 하지 않는다. (F05 의 bouncing 방지)
|
|
80
|
-
;
|
|
81
|
-
holder.reparent(carrier, { animated: true, duration: 2000, ease: 'inout' });
|
|
82
|
-
}
|
|
83
|
-
if (carrier.parent === holder) {
|
|
84
|
-
carrier.setState('stackIndex', idx);
|
|
85
|
-
// showText 는 *값이 바뀔 때만* setState — 매 프레임 불필요한 onchange / 3D mesh
|
|
86
|
-
// 재빌드를 원천 차단(성능). 평시(변화 없음)엔 getState 비교 한 번으로 끝난다.
|
|
87
|
-
if (carrier.getState('showText') !== showCarrierText)
|
|
88
|
-
carrier.setState('showText', showCarrierText);
|
|
89
|
-
carrier.data = item;
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
// *_모든 carrier 추가 완료 후* — 각자의 transform 강제 갱신. 한 줄로 처리
|
|
93
|
-
// 안 되는 이유: cz override 의 offset 계산이 *형제 carrier 의 _realObject.
|
|
94
|
-
// effectiveDepth 누적*에 의존하는데, 첫 iteration 시점엔 자기 뒤 carrier 의
|
|
95
|
-
// _realObject 가 아직 없어 offset 이 부족하게 평가될 수 있다. 모두 빌드된
|
|
96
|
-
// 뒤 한 번 더 갱신해야 적층 정확.
|
|
97
|
-
afterStack.forEach(item => {
|
|
98
|
-
var _a, _b;
|
|
99
|
-
const carrier = this.carriers.get(item.CARRIERNAME);
|
|
100
|
-
if (carrier && carrier.parent === holder) {
|
|
101
|
-
;
|
|
102
|
-
(_b = (_a = carrier._realObject) === null || _a === void 0 ? void 0 : _a.updateTransform) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* data.CARRIERNAME (대표) + data.MULTICARRIERS[] 를 하나의 stack list 로 정규화.
|
|
108
|
-
* 적층 순서: multiCarriers[0..n-1] (bottom → up) + 대표 (top, 맨 마지막).
|
|
109
|
-
* (데이터 키는 모두 대문자 전제 — 케이스 정규화 없이 직접 읽는다)
|
|
110
|
-
*/
|
|
111
|
-
_buildStack(data) {
|
|
112
|
-
if (!data)
|
|
113
|
-
return [];
|
|
114
|
-
const stack = [];
|
|
115
|
-
// MULTICARRIERS 값이 JSON 문자열로 올 수 있어 파싱 후 처리.
|
|
116
|
-
const list = parseIfJsonString(data.MULTICARRIERS);
|
|
117
|
-
if (Array.isArray(list)) {
|
|
118
|
-
for (const a of list) {
|
|
119
|
-
if (a && typeof a === 'object' && a.CARRIERNAME) {
|
|
120
|
-
// 항목 *원본 그대로* — 외부 시스템이 임의 키를 추가로 보낼 수 있고,
|
|
121
|
-
// 누락된 4 키는 default 없이 그대로 비워둔다 (carrier.data 에 흘리는
|
|
122
|
-
// 가공 최소화 — F03 같은 기존 테스트의 raw shape 기대와도 정합).
|
|
123
|
-
stack.push(Object.assign({}, a));
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (data.CARRIERNAME) {
|
|
128
|
-
// 대표는 *holder.data 의 carrier 관련 키만 추출* — showText / carrierDepth
|
|
129
|
-
// 같은 holder 자체 속성은 carrier 로 흘리지 않는다. 명시되지 않은 키는
|
|
130
|
-
// 생략 (default 안 채움).
|
|
131
|
-
const repr = { CARRIERNAME: data.CARRIERNAME };
|
|
132
|
-
if ('CARRIERTYPE' in data)
|
|
133
|
-
repr.CARRIERTYPE = data.CARRIERTYPE;
|
|
134
|
-
if ('EMPTYTYPE' in data)
|
|
135
|
-
repr.EMPTYTYPE = data.EMPTYTYPE;
|
|
136
|
-
if ('CARRIERSTATUS' in data)
|
|
137
|
-
repr.CARRIERSTATUS = data.CARRIERSTATUS;
|
|
138
|
-
stack.push(repr);
|
|
139
|
-
}
|
|
140
|
-
return stack;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* carrier dispose 를 *next macrotask* 로 미룬다. 동기 즉시 dispose 가 아닌
|
|
144
|
-
* 이유: 같은 macrotask 안에서 holder A 의 사라짐 보고가 step 1 에 들어오고
|
|
145
|
-
* holder B 의 등장 보고가 (별도 carrier-manager.update 호출의) step 2 에
|
|
146
|
-
* 들어오는 reparent 시나리오를 보호하기 위함. 즉시 dispose 면 step 1 에서
|
|
147
|
-
* carrier 인스턴스가 파괴돼 step 2 의 reparent 분기가 *새 인스턴스 생성*
|
|
148
|
-
* 으로 떨어지고, 3D mesh 재구축 + 이동 애니메이션 손실이 발생.
|
|
149
|
-
*
|
|
150
|
-
* 같은 macrotask 안에서 cancelDying 호출이 도달하면 clearTimeout 으로
|
|
151
|
-
* dispose 회수 → reparent 정상.
|
|
152
|
-
*/
|
|
153
|
-
scheduleDying(carrierName) {
|
|
154
|
-
this.cancelDying(carrierName);
|
|
155
|
-
const timer = setTimeout(() => {
|
|
156
|
-
this.dyingTimers.delete(carrierName);
|
|
157
|
-
const carrier = this.carriers.get(carrierName);
|
|
158
|
-
if (carrier) {
|
|
159
|
-
this.carriers.delete(carrierName);
|
|
160
|
-
if (!carrier.disposed) {
|
|
161
|
-
carrier.removeSelf(true);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}, DYING_TIMEOUT);
|
|
165
|
-
this.dyingTimers.set(carrierName, timer);
|
|
166
|
-
}
|
|
167
|
-
cancelDying(carrierName) {
|
|
168
|
-
const timer = this.dyingTimers.get(carrierName);
|
|
169
|
-
if (timer) {
|
|
170
|
-
clearTimeout(timer);
|
|
171
|
-
this.dyingTimers.delete(carrierName);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
onCarrierDisposed(carrierName) {
|
|
175
|
-
this.cancelDying(carrierName);
|
|
176
|
-
this.carriers.delete(carrierName);
|
|
177
|
-
}
|
|
178
|
-
dispose() {
|
|
179
|
-
for (const timer of this.dyingTimers.values()) {
|
|
180
|
-
clearTimeout(timer);
|
|
181
|
-
}
|
|
182
|
-
this.dyingTimers.clear();
|
|
183
|
-
for (const carrier of this.carriers.values()) {
|
|
184
|
-
if (!carrier.disposed) {
|
|
185
|
-
carrier.removeSelf(true);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
this.carriers.clear();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
//# sourceMappingURL=carrier-manager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"carrier-manager.js","sourceRoot":"","sources":["../src/carrier-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { Model } from '@hatiolab/things-scene'\nimport type Carrier from './carrier'\nimport type MCSCarrierHolder from './mcs-carrier-holder'\nimport { parseIfJsonString } from './utils/data-value'\n\n// 사라진 carrier 의 dispose 지연. 같은 macrotask 안에서 holder A 의 사라짐\n// 보고와 holder B 의 등장 보고가 순차로 들어오는 reparent 시나리오 (= 같은\n// frame snapshot 의 여러 record 적용) 를 보호하기 위해 setTimeout(0) 의\n// next-macrotask delay 만 둔다. 과거 1000ms grace 가 있었으나 seek 시 2단계\n// 갱신 (1초 후 carrier 사라짐) 부작용이 명확하고 frame 누락 bouncing 깜빡임\n// 보호 효용은 작다 판단되어 0 으로 단축 (2026-06-09).\nconst DYING_TIMEOUT = 0\n\n/**\n * Carrier 가 들고 있는 4 키 묶음. data.CARRIERNAME 의 형제 키 (CARRIERTYPE,\n * EMPTYTYPE, CARRIERSTATUS) 를 한 item 으로 묶는다. holder.data 의 *대표*\n * 와 ADDITIONS 의 *각 항목* 모두 동일 shape 으로 정규화돼 stack diff 에\n * 들어간다.\n *\n * 주의 — 외부 시스템이 ADDITIONS 의 각 항목 키를 다른 이름으로 (예:\n * NAME / TYPE / EMPTY / STATUS) 줄 가능성에 대비해 이 build 함수가 단일\n * 정규화 지점이 된다. 키 매핑 변경은 여기 한 곳만 손대면 된다.\n */\nexport interface CarrierStackItem {\n CARRIERNAME: string\n CARRIERTYPE?: string\n EMPTYTYPE?: string\n CARRIERSTATUS?: string\n}\n\nexport default class CarrierManager {\n private carriers: Map<string, Carrier> = new Map()\n private dyingTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private root: any\n\n constructor(root: any) {\n this.root = root\n }\n\n get(carrierName: string): Carrier | undefined {\n return this.carriers.get(carrierName)\n }\n\n /**\n * Holder 의 data 변경에 대응해 carrier pool 을 동기화.\n *\n * 정책:\n * - holder.data 가 *single source of truth*. 대표(CARRIERNAME) +\n * ADDITIONS[] 를 하나의 *stack list* 로 정규화한 뒤 before/after 의\n * 이름 set diff 로 추가/제거/유지 분기.\n * - \"대표\" 와 \"ADDITIONS\" 는 *holder.data 안의 키 위치 차이일 뿐* 이고,\n * carrier 인스턴스끼리는 **종속/그룹 관계 없음 — 모두 독립 동등**.\n * stack 안의 위치 (stackIndex) 만 carrier.state 에 주입한다. 2D render\n * skip 등의 판정은 *위치 (= parent.components 의 마지막 여부)* 로 한다.\n * - 동일 CARRIERNAME 이 *다른 holder* 에 새로 등장하면 reparent (애니메이션).\n * *해당 carrier 만 이동* — 같은 stack 의 다른 carrier 는 따라가지 않는다.\n * - 사라진 carrier 는 next-macrotask 에 dispose. 같은 macrotask 안에서 다른\n * holder 가 같은 이름을 보고하면 cancel 되어 reparent 로 전환.\n */\n update(holder: MCSCarrierHolder, after: Record<string, any>, before: Record<string, any>): void {\n const afterStack = this._buildStack(after)\n const beforeStack = this._buildStack(before)\n\n const afterNames = new Set(afterStack.map(i => i.CARRIERNAME))\n const beforeNames = new Set(beforeStack.map(i => i.CARRIERNAME))\n\n // 1) 사라진 것 → dying schedule (자기 holder 의 자식인 carrier 만)\n for (const name of beforeNames) {\n if (afterNames.has(name)) continue\n const carrier = this.carriers.get(name)\n if (carrier && carrier.parent === holder) {\n this.scheduleDying(name)\n }\n }\n\n // showText 는 *현재 소유 홀더* 의 showCarrierText 를 항상 따른다 — 생성 시점에만\n // 고정하면 동일 이름 carrier 가 다른 홀더(예: 분체 Crane)로 이동/보고될 때\n // off 를 무시하고 표시(F/E/X)가 남는다. 매 update 마다 재동기화한다.\n const showCarrierText = holder.getState('showCarrierText') || false\n\n // 2) afterStack 의 *순서대로* 처리 — bottom (ADDITIONS[0]) 부터 top (대표)\n // 그래야 parent.components 가 자연스럽게 같은 순서로 정렬되어 stack index\n // 가 일관된다.\n afterStack.forEach((item, idx) => {\n const name = item.CARRIERNAME\n this.cancelDying(name)\n\n let carrier = this.carriers.get(name)\n const { width, height } = holder.state\n\n if (!carrier) {\n carrier = Model.compile(\n {\n id: name,\n type: 'Carrier',\n left: 0,\n top: 0,\n width,\n height,\n showText: showCarrierText,\n stackIndex: idx\n },\n this.root.app\n ) as unknown as Carrier\n\n this.carriers.set(name, carrier)\n carrier.data = item\n holder.addComponent(carrier, { silent: true })\n } else if (carrier.parent !== holder && !beforeNames.has(name)) {\n // 다른 holder 에 있던 carrier 가 이번 update 에서 *새로 등장* 한 경우만\n // reparent. before 에도 같은 이름이 있었으면 ping-pong (옛 시점의\n // 반복 보고) 이므로 trigger 하지 않는다. (F05 의 bouncing 방지)\n ;(holder as any).reparent(carrier, { animated: true, duration: 2000, ease: 'inout' })\n }\n\n if (carrier.parent === holder) {\n carrier.setState('stackIndex', idx)\n // showText 는 *값이 바뀔 때만* setState — 매 프레임 불필요한 onchange / 3D mesh\n // 재빌드를 원천 차단(성능). 평시(변화 없음)엔 getState 비교 한 번으로 끝난다.\n if (carrier.getState('showText') !== showCarrierText) carrier.setState('showText', showCarrierText)\n carrier.data = item\n }\n })\n\n // *_모든 carrier 추가 완료 후* — 각자의 transform 강제 갱신. 한 줄로 처리\n // 안 되는 이유: cz override 의 offset 계산이 *형제 carrier 의 _realObject.\n // effectiveDepth 누적*에 의존하는데, 첫 iteration 시점엔 자기 뒤 carrier 의\n // _realObject 가 아직 없어 offset 이 부족하게 평가될 수 있다. 모두 빌드된\n // 뒤 한 번 더 갱신해야 적층 정확.\n afterStack.forEach(item => {\n const carrier = this.carriers.get(item.CARRIERNAME)\n if (carrier && carrier.parent === holder) {\n ;(carrier as any)._realObject?.updateTransform?.()\n }\n })\n }\n\n /**\n * data.CARRIERNAME (대표) + data.MULTICARRIERS[] 를 하나의 stack list 로 정규화.\n * 적층 순서: multiCarriers[0..n-1] (bottom → up) + 대표 (top, 맨 마지막).\n * (데이터 키는 모두 대문자 전제 — 케이스 정규화 없이 직접 읽는다)\n */\n private _buildStack(data: any): CarrierStackItem[] {\n if (!data) return []\n\n const stack: CarrierStackItem[] = []\n\n // MULTICARRIERS 값이 JSON 문자열로 올 수 있어 파싱 후 처리.\n const list = parseIfJsonString(data.MULTICARRIERS)\n if (Array.isArray(list)) {\n for (const a of list) {\n if (a && typeof a === 'object' && a.CARRIERNAME) {\n // 항목 *원본 그대로* — 외부 시스템이 임의 키를 추가로 보낼 수 있고,\n // 누락된 4 키는 default 없이 그대로 비워둔다 (carrier.data 에 흘리는\n // 가공 최소화 — F03 같은 기존 테스트의 raw shape 기대와도 정합).\n stack.push({ ...a } as CarrierStackItem)\n }\n }\n }\n\n if (data.CARRIERNAME) {\n // 대표는 *holder.data 의 carrier 관련 키만 추출* — showText / carrierDepth\n // 같은 holder 자체 속성은 carrier 로 흘리지 않는다. 명시되지 않은 키는\n // 생략 (default 안 채움).\n const repr: any = { CARRIERNAME: data.CARRIERNAME }\n if ('CARRIERTYPE' in data) repr.CARRIERTYPE = data.CARRIERTYPE\n if ('EMPTYTYPE' in data) repr.EMPTYTYPE = data.EMPTYTYPE\n if ('CARRIERSTATUS' in data) repr.CARRIERSTATUS = data.CARRIERSTATUS\n stack.push(repr)\n }\n\n return stack\n }\n\n /**\n * carrier dispose 를 *next macrotask* 로 미룬다. 동기 즉시 dispose 가 아닌\n * 이유: 같은 macrotask 안에서 holder A 의 사라짐 보고가 step 1 에 들어오고\n * holder B 의 등장 보고가 (별도 carrier-manager.update 호출의) step 2 에\n * 들어오는 reparent 시나리오를 보호하기 위함. 즉시 dispose 면 step 1 에서\n * carrier 인스턴스가 파괴돼 step 2 의 reparent 분기가 *새 인스턴스 생성*\n * 으로 떨어지고, 3D mesh 재구축 + 이동 애니메이션 손실이 발생.\n *\n * 같은 macrotask 안에서 cancelDying 호출이 도달하면 clearTimeout 으로\n * dispose 회수 → reparent 정상.\n */\n private scheduleDying(carrierName: string): void {\n this.cancelDying(carrierName)\n\n const timer = setTimeout(() => {\n this.dyingTimers.delete(carrierName)\n\n const carrier = this.carriers.get(carrierName)\n if (carrier) {\n this.carriers.delete(carrierName)\n if (!carrier.disposed) {\n carrier.removeSelf(true)\n }\n }\n }, DYING_TIMEOUT)\n\n this.dyingTimers.set(carrierName, timer)\n }\n\n private cancelDying(carrierName: string): void {\n const timer = this.dyingTimers.get(carrierName)\n if (timer) {\n clearTimeout(timer)\n this.dyingTimers.delete(carrierName)\n }\n }\n\n onCarrierDisposed(carrierName: string): void {\n this.cancelDying(carrierName)\n this.carriers.delete(carrierName)\n }\n\n dispose(): void {\n for (const timer of this.dyingTimers.values()) {\n clearTimeout(timer)\n }\n this.dyingTimers.clear()\n\n for (const carrier of this.carriers.values()) {\n if (!carrier.disposed) {\n carrier.removeSelf(true)\n }\n }\n\n this.carriers.clear()\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"animation-default.js","sourceRoot":"","sources":["../../src/features/animation-default.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,iBAAiB,GAAoB;IAChD,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI;CACd,CAAA","sourcesContent":["export type AnimationConfig = {\n [key: string]: any\n}\n\nexport interface AnimationPreset {\n [state: string]: AnimationConfig | string | null\n}\n\nexport const ANIMATION_DEFAULT: AnimationPreset = {\n WARN: null,\n CRITICAL: null,\n default: null\n}\n"]}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export function getValueOnRanges(value, param) {
|
|
2
|
-
for (let range in param) {
|
|
3
|
-
let [from, to] = range.split('~');
|
|
4
|
-
let from_defined = !!from;
|
|
5
|
-
let to_defined = !!to;
|
|
6
|
-
from = parseFloat(from);
|
|
7
|
-
to = parseFloat(to);
|
|
8
|
-
value = Number(value) || 0;
|
|
9
|
-
if (from_defined && !to_defined && value >= from) {
|
|
10
|
-
return param[range];
|
|
11
|
-
}
|
|
12
|
-
else if (!from_defined && to_defined && value < to) {
|
|
13
|
-
return param[range];
|
|
14
|
-
}
|
|
15
|
-
if (value >= from && value < to) {
|
|
16
|
-
return param[range];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return param['default'];
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=get-value-on-ranges.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-value-on-ranges.js","sourceRoot":"","sources":["../../src/utils/get-value-on-ranges.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,KAAK,EAAE,KAAK;IAC3C,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,GAAwB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACtD,IAAI,YAAY,GAAG,CAAC,CAAC,IAAI,CAAA;QACzB,IAAI,UAAU,GAAG,CAAC,CAAC,EAAE,CAAA;QAErB,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QACvB,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC,CAAA;QACnB,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAE1B,IAAI,YAAY,IAAI,CAAC,UAAU,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;aAAM,IAAI,CAAC,YAAY,IAAI,UAAU,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;YACrD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;QAED,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,SAAS,CAAC,CAAA;AACzB,CAAC","sourcesContent":["export function getValueOnRanges(value, param): any {\n for (let range in param) {\n let [from, to]: (number | string)[] = range.split('~')\n let from_defined = !!from\n let to_defined = !!to\n\n from = parseFloat(from)\n to = parseFloat(to)\n value = Number(value) || 0\n\n if (from_defined && !to_defined && value >= from) {\n return param[range]\n } else if (!from_defined && to_defined && value < to) {\n return param[range]\n }\n\n if (value >= from && value < to) {\n return param[range]\n }\n }\n\n return param['default']\n}\n"]}
|
package/src/carrier-manager.ts
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { Model } from '@hatiolab/things-scene'
|
|
2
|
-
import type Carrier from './carrier'
|
|
3
|
-
import type MCSCarrierHolder from './mcs-carrier-holder'
|
|
4
|
-
import { parseIfJsonString } from './utils/data-value'
|
|
5
|
-
|
|
6
|
-
// 사라진 carrier 의 dispose 지연. 같은 macrotask 안에서 holder A 의 사라짐
|
|
7
|
-
// 보고와 holder B 의 등장 보고가 순차로 들어오는 reparent 시나리오 (= 같은
|
|
8
|
-
// frame snapshot 의 여러 record 적용) 를 보호하기 위해 setTimeout(0) 의
|
|
9
|
-
// next-macrotask delay 만 둔다. 과거 1000ms grace 가 있었으나 seek 시 2단계
|
|
10
|
-
// 갱신 (1초 후 carrier 사라짐) 부작용이 명확하고 frame 누락 bouncing 깜빡임
|
|
11
|
-
// 보호 효용은 작다 판단되어 0 으로 단축 (2026-06-09).
|
|
12
|
-
const DYING_TIMEOUT = 0
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Carrier 가 들고 있는 4 키 묶음. data.CARRIERNAME 의 형제 키 (CARRIERTYPE,
|
|
16
|
-
* EMPTYTYPE, CARRIERSTATUS) 를 한 item 으로 묶는다. holder.data 의 *대표*
|
|
17
|
-
* 와 ADDITIONS 의 *각 항목* 모두 동일 shape 으로 정규화돼 stack diff 에
|
|
18
|
-
* 들어간다.
|
|
19
|
-
*
|
|
20
|
-
* 주의 — 외부 시스템이 ADDITIONS 의 각 항목 키를 다른 이름으로 (예:
|
|
21
|
-
* NAME / TYPE / EMPTY / STATUS) 줄 가능성에 대비해 이 build 함수가 단일
|
|
22
|
-
* 정규화 지점이 된다. 키 매핑 변경은 여기 한 곳만 손대면 된다.
|
|
23
|
-
*/
|
|
24
|
-
export interface CarrierStackItem {
|
|
25
|
-
CARRIERNAME: string
|
|
26
|
-
CARRIERTYPE?: string
|
|
27
|
-
EMPTYTYPE?: string
|
|
28
|
-
CARRIERSTATUS?: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export default class CarrierManager {
|
|
32
|
-
private carriers: Map<string, Carrier> = new Map()
|
|
33
|
-
private dyingTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
|
|
34
|
-
private root: any
|
|
35
|
-
|
|
36
|
-
constructor(root: any) {
|
|
37
|
-
this.root = root
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get(carrierName: string): Carrier | undefined {
|
|
41
|
-
return this.carriers.get(carrierName)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Holder 의 data 변경에 대응해 carrier pool 을 동기화.
|
|
46
|
-
*
|
|
47
|
-
* 정책:
|
|
48
|
-
* - holder.data 가 *single source of truth*. 대표(CARRIERNAME) +
|
|
49
|
-
* ADDITIONS[] 를 하나의 *stack list* 로 정규화한 뒤 before/after 의
|
|
50
|
-
* 이름 set diff 로 추가/제거/유지 분기.
|
|
51
|
-
* - "대표" 와 "ADDITIONS" 는 *holder.data 안의 키 위치 차이일 뿐* 이고,
|
|
52
|
-
* carrier 인스턴스끼리는 **종속/그룹 관계 없음 — 모두 독립 동등**.
|
|
53
|
-
* stack 안의 위치 (stackIndex) 만 carrier.state 에 주입한다. 2D render
|
|
54
|
-
* skip 등의 판정은 *위치 (= parent.components 의 마지막 여부)* 로 한다.
|
|
55
|
-
* - 동일 CARRIERNAME 이 *다른 holder* 에 새로 등장하면 reparent (애니메이션).
|
|
56
|
-
* *해당 carrier 만 이동* — 같은 stack 의 다른 carrier 는 따라가지 않는다.
|
|
57
|
-
* - 사라진 carrier 는 next-macrotask 에 dispose. 같은 macrotask 안에서 다른
|
|
58
|
-
* holder 가 같은 이름을 보고하면 cancel 되어 reparent 로 전환.
|
|
59
|
-
*/
|
|
60
|
-
update(holder: MCSCarrierHolder, after: Record<string, any>, before: Record<string, any>): void {
|
|
61
|
-
const afterStack = this._buildStack(after)
|
|
62
|
-
const beforeStack = this._buildStack(before)
|
|
63
|
-
|
|
64
|
-
const afterNames = new Set(afterStack.map(i => i.CARRIERNAME))
|
|
65
|
-
const beforeNames = new Set(beforeStack.map(i => i.CARRIERNAME))
|
|
66
|
-
|
|
67
|
-
// 1) 사라진 것 → dying schedule (자기 holder 의 자식인 carrier 만)
|
|
68
|
-
for (const name of beforeNames) {
|
|
69
|
-
if (afterNames.has(name)) continue
|
|
70
|
-
const carrier = this.carriers.get(name)
|
|
71
|
-
if (carrier && carrier.parent === holder) {
|
|
72
|
-
this.scheduleDying(name)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// showText 는 *현재 소유 홀더* 의 showCarrierText 를 항상 따른다 — 생성 시점에만
|
|
77
|
-
// 고정하면 동일 이름 carrier 가 다른 홀더(예: 분체 Crane)로 이동/보고될 때
|
|
78
|
-
// off 를 무시하고 표시(F/E/X)가 남는다. 매 update 마다 재동기화한다.
|
|
79
|
-
const showCarrierText = holder.getState('showCarrierText') || false
|
|
80
|
-
|
|
81
|
-
// 2) afterStack 의 *순서대로* 처리 — bottom (ADDITIONS[0]) 부터 top (대표)
|
|
82
|
-
// 그래야 parent.components 가 자연스럽게 같은 순서로 정렬되어 stack index
|
|
83
|
-
// 가 일관된다.
|
|
84
|
-
afterStack.forEach((item, idx) => {
|
|
85
|
-
const name = item.CARRIERNAME
|
|
86
|
-
this.cancelDying(name)
|
|
87
|
-
|
|
88
|
-
let carrier = this.carriers.get(name)
|
|
89
|
-
const { width, height } = holder.state
|
|
90
|
-
|
|
91
|
-
if (!carrier) {
|
|
92
|
-
carrier = Model.compile(
|
|
93
|
-
{
|
|
94
|
-
id: name,
|
|
95
|
-
type: 'Carrier',
|
|
96
|
-
left: 0,
|
|
97
|
-
top: 0,
|
|
98
|
-
width,
|
|
99
|
-
height,
|
|
100
|
-
showText: showCarrierText,
|
|
101
|
-
stackIndex: idx
|
|
102
|
-
},
|
|
103
|
-
this.root.app
|
|
104
|
-
) as unknown as Carrier
|
|
105
|
-
|
|
106
|
-
this.carriers.set(name, carrier)
|
|
107
|
-
carrier.data = item
|
|
108
|
-
holder.addComponent(carrier, { silent: true })
|
|
109
|
-
} else if (carrier.parent !== holder && !beforeNames.has(name)) {
|
|
110
|
-
// 다른 holder 에 있던 carrier 가 이번 update 에서 *새로 등장* 한 경우만
|
|
111
|
-
// reparent. before 에도 같은 이름이 있었으면 ping-pong (옛 시점의
|
|
112
|
-
// 반복 보고) 이므로 trigger 하지 않는다. (F05 의 bouncing 방지)
|
|
113
|
-
;(holder as any).reparent(carrier, { animated: true, duration: 2000, ease: 'inout' })
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (carrier.parent === holder) {
|
|
117
|
-
carrier.setState('stackIndex', idx)
|
|
118
|
-
// showText 는 *값이 바뀔 때만* setState — 매 프레임 불필요한 onchange / 3D mesh
|
|
119
|
-
// 재빌드를 원천 차단(성능). 평시(변화 없음)엔 getState 비교 한 번으로 끝난다.
|
|
120
|
-
if (carrier.getState('showText') !== showCarrierText) carrier.setState('showText', showCarrierText)
|
|
121
|
-
carrier.data = item
|
|
122
|
-
}
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
// *_모든 carrier 추가 완료 후* — 각자의 transform 강제 갱신. 한 줄로 처리
|
|
126
|
-
// 안 되는 이유: cz override 의 offset 계산이 *형제 carrier 의 _realObject.
|
|
127
|
-
// effectiveDepth 누적*에 의존하는데, 첫 iteration 시점엔 자기 뒤 carrier 의
|
|
128
|
-
// _realObject 가 아직 없어 offset 이 부족하게 평가될 수 있다. 모두 빌드된
|
|
129
|
-
// 뒤 한 번 더 갱신해야 적층 정확.
|
|
130
|
-
afterStack.forEach(item => {
|
|
131
|
-
const carrier = this.carriers.get(item.CARRIERNAME)
|
|
132
|
-
if (carrier && carrier.parent === holder) {
|
|
133
|
-
;(carrier as any)._realObject?.updateTransform?.()
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* data.CARRIERNAME (대표) + data.MULTICARRIERS[] 를 하나의 stack list 로 정규화.
|
|
140
|
-
* 적층 순서: multiCarriers[0..n-1] (bottom → up) + 대표 (top, 맨 마지막).
|
|
141
|
-
* (데이터 키는 모두 대문자 전제 — 케이스 정규화 없이 직접 읽는다)
|
|
142
|
-
*/
|
|
143
|
-
private _buildStack(data: any): CarrierStackItem[] {
|
|
144
|
-
if (!data) return []
|
|
145
|
-
|
|
146
|
-
const stack: CarrierStackItem[] = []
|
|
147
|
-
|
|
148
|
-
// MULTICARRIERS 값이 JSON 문자열로 올 수 있어 파싱 후 처리.
|
|
149
|
-
const list = parseIfJsonString(data.MULTICARRIERS)
|
|
150
|
-
if (Array.isArray(list)) {
|
|
151
|
-
for (const a of list) {
|
|
152
|
-
if (a && typeof a === 'object' && a.CARRIERNAME) {
|
|
153
|
-
// 항목 *원본 그대로* — 외부 시스템이 임의 키를 추가로 보낼 수 있고,
|
|
154
|
-
// 누락된 4 키는 default 없이 그대로 비워둔다 (carrier.data 에 흘리는
|
|
155
|
-
// 가공 최소화 — F03 같은 기존 테스트의 raw shape 기대와도 정합).
|
|
156
|
-
stack.push({ ...a } as CarrierStackItem)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (data.CARRIERNAME) {
|
|
162
|
-
// 대표는 *holder.data 의 carrier 관련 키만 추출* — showText / carrierDepth
|
|
163
|
-
// 같은 holder 자체 속성은 carrier 로 흘리지 않는다. 명시되지 않은 키는
|
|
164
|
-
// 생략 (default 안 채움).
|
|
165
|
-
const repr: any = { CARRIERNAME: data.CARRIERNAME }
|
|
166
|
-
if ('CARRIERTYPE' in data) repr.CARRIERTYPE = data.CARRIERTYPE
|
|
167
|
-
if ('EMPTYTYPE' in data) repr.EMPTYTYPE = data.EMPTYTYPE
|
|
168
|
-
if ('CARRIERSTATUS' in data) repr.CARRIERSTATUS = data.CARRIERSTATUS
|
|
169
|
-
stack.push(repr)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return stack
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* carrier dispose 를 *next macrotask* 로 미룬다. 동기 즉시 dispose 가 아닌
|
|
177
|
-
* 이유: 같은 macrotask 안에서 holder A 의 사라짐 보고가 step 1 에 들어오고
|
|
178
|
-
* holder B 의 등장 보고가 (별도 carrier-manager.update 호출의) step 2 에
|
|
179
|
-
* 들어오는 reparent 시나리오를 보호하기 위함. 즉시 dispose 면 step 1 에서
|
|
180
|
-
* carrier 인스턴스가 파괴돼 step 2 의 reparent 분기가 *새 인스턴스 생성*
|
|
181
|
-
* 으로 떨어지고, 3D mesh 재구축 + 이동 애니메이션 손실이 발생.
|
|
182
|
-
*
|
|
183
|
-
* 같은 macrotask 안에서 cancelDying 호출이 도달하면 clearTimeout 으로
|
|
184
|
-
* dispose 회수 → reparent 정상.
|
|
185
|
-
*/
|
|
186
|
-
private scheduleDying(carrierName: string): void {
|
|
187
|
-
this.cancelDying(carrierName)
|
|
188
|
-
|
|
189
|
-
const timer = setTimeout(() => {
|
|
190
|
-
this.dyingTimers.delete(carrierName)
|
|
191
|
-
|
|
192
|
-
const carrier = this.carriers.get(carrierName)
|
|
193
|
-
if (carrier) {
|
|
194
|
-
this.carriers.delete(carrierName)
|
|
195
|
-
if (!carrier.disposed) {
|
|
196
|
-
carrier.removeSelf(true)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}, DYING_TIMEOUT)
|
|
200
|
-
|
|
201
|
-
this.dyingTimers.set(carrierName, timer)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private cancelDying(carrierName: string): void {
|
|
205
|
-
const timer = this.dyingTimers.get(carrierName)
|
|
206
|
-
if (timer) {
|
|
207
|
-
clearTimeout(timer)
|
|
208
|
-
this.dyingTimers.delete(carrierName)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
onCarrierDisposed(carrierName: string): void {
|
|
213
|
-
this.cancelDying(carrierName)
|
|
214
|
-
this.carriers.delete(carrierName)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
dispose(): void {
|
|
218
|
-
for (const timer of this.dyingTimers.values()) {
|
|
219
|
-
clearTimeout(timer)
|
|
220
|
-
}
|
|
221
|
-
this.dyingTimers.clear()
|
|
222
|
-
|
|
223
|
-
for (const carrier of this.carriers.values()) {
|
|
224
|
-
if (!carrier.disposed) {
|
|
225
|
-
carrier.removeSelf(true)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
this.carriers.clear()
|
|
230
|
-
}
|
|
231
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export type AnimationConfig = {
|
|
2
|
-
[key: string]: any
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export interface AnimationPreset {
|
|
6
|
-
[state: string]: AnimationConfig | string | null
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const ANIMATION_DEFAULT: AnimationPreset = {
|
|
10
|
-
WARN: null,
|
|
11
|
-
CRITICAL: null,
|
|
12
|
-
default: null
|
|
13
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export function getValueOnRanges(value, param): any {
|
|
2
|
-
for (let range in param) {
|
|
3
|
-
let [from, to]: (number | string)[] = range.split('~')
|
|
4
|
-
let from_defined = !!from
|
|
5
|
-
let to_defined = !!to
|
|
6
|
-
|
|
7
|
-
from = parseFloat(from)
|
|
8
|
-
to = parseFloat(to)
|
|
9
|
-
value = Number(value) || 0
|
|
10
|
-
|
|
11
|
-
if (from_defined && !to_defined && value >= from) {
|
|
12
|
-
return param[range]
|
|
13
|
-
} else if (!from_defined && to_defined && value < to) {
|
|
14
|
-
return param[range]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (value >= from && value < to) {
|
|
18
|
-
return param[range]
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return param['default']
|
|
23
|
-
}
|