@syhr/dga-charts 1.0.0
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/README.md +83 -0
- package/dist/index.cjs +31 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +2796 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +31 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +39 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2796 @@
|
|
|
1
|
+
var Dt = Object.defineProperty;
|
|
2
|
+
var Et = (v, t, e) => t in v ? Dt(v, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : v[t] = e;
|
|
3
|
+
var K = (v, t, e) => Et(v, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
class At {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._h = /* @__PURE__ */ Object.create(null);
|
|
7
|
+
}
|
|
8
|
+
on(t, e) {
|
|
9
|
+
return (this._h[t] || (this._h[t] = [])).push(e), this;
|
|
10
|
+
}
|
|
11
|
+
off(t, e) {
|
|
12
|
+
return e ? (this._h[t] && (this._h[t] = this._h[t].filter((o) => o !== e)), this) : (delete this._h[t], this);
|
|
13
|
+
}
|
|
14
|
+
emit(t, e) {
|
|
15
|
+
return (this._h[t] || []).slice().forEach((o) => o(e)), this;
|
|
16
|
+
}
|
|
17
|
+
dispose() {
|
|
18
|
+
this._h = /* @__PURE__ */ Object.create(null);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class ut {
|
|
22
|
+
constructor(t = {}) {
|
|
23
|
+
this.zoom = 1, this.panX = 0, this.panY = 0, this._min = t.min ?? 0.3, this._max = t.max ?? 6, this._step = t.step ?? 0.12, this._dragging = !1, this._dragStart = null, this._panStart = null;
|
|
24
|
+
}
|
|
25
|
+
onWheel(t, e, o) {
|
|
26
|
+
const s = t < 0 ? 1 : -1, i = Math.min(this._max, Math.max(this._min, this.zoom * (1 + s * this._step))), n = i / this.zoom;
|
|
27
|
+
return this.panX = e - n * (e - this.panX), this.panY = o - n * (o - this.panY), this.zoom = i, this;
|
|
28
|
+
}
|
|
29
|
+
startDrag(t, e) {
|
|
30
|
+
this._dragging = !0, this._dragStart = { x: t, y: e }, this._panStart = { x: this.panX, y: this.panY };
|
|
31
|
+
}
|
|
32
|
+
moveDrag(t, e, o) {
|
|
33
|
+
return this._dragging ? (this.panX = this._panStart.x + (t - this._dragStart.x) * o, this.panY = this._panStart.y + (e - this._dragStart.y) * o, !0) : !1;
|
|
34
|
+
}
|
|
35
|
+
endDrag() {
|
|
36
|
+
const t = this._dragging;
|
|
37
|
+
return this._dragging = !1, t;
|
|
38
|
+
}
|
|
39
|
+
reset() {
|
|
40
|
+
return this.zoom = 1, this.panX = 0, this.panY = 0, this;
|
|
41
|
+
}
|
|
42
|
+
get isDragging() {
|
|
43
|
+
return this._dragging;
|
|
44
|
+
}
|
|
45
|
+
get state() {
|
|
46
|
+
return { zoom: this.zoom, panX: this.panX, panY: this.panY };
|
|
47
|
+
}
|
|
48
|
+
applyTransform(t) {
|
|
49
|
+
t.translate(this.panX, this.panY), t.scale(this.zoom, this.zoom);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function Lt(v, t) {
|
|
53
|
+
const e = v.replace("#", ""), o = parseInt(e.slice(0, 2), 16), s = parseInt(e.slice(2, 4), 16), i = parseInt(e.slice(4, 6), 16);
|
|
54
|
+
return `rgba(${o},${s},${i},${t})`;
|
|
55
|
+
}
|
|
56
|
+
function E(v, t) {
|
|
57
|
+
for (const e of Object.keys(t)) {
|
|
58
|
+
const o = t[e];
|
|
59
|
+
o !== null && typeof o == "object" && !Array.isArray(o) ? (v[e] = v[e] && typeof v[e] == "object" ? v[e] : {}, E(v[e], o)) : v[e] = o;
|
|
60
|
+
}
|
|
61
|
+
return v;
|
|
62
|
+
}
|
|
63
|
+
function gt(v, t, e) {
|
|
64
|
+
let o = !1;
|
|
65
|
+
for (let s = 0, i = e.length - 1; s < e.length; i = s++) {
|
|
66
|
+
const [n, l] = e[s], [h, a] = e[i];
|
|
67
|
+
l > t != a > t && v < (h - n) * (t - l) / (a - l) + n && (o = !o);
|
|
68
|
+
}
|
|
69
|
+
return o;
|
|
70
|
+
}
|
|
71
|
+
class yt {
|
|
72
|
+
constructor(t, e) {
|
|
73
|
+
this.container = t, this.el = document.createElement("div"), this.el.style.position = "absolute", this.el.style.pointerEvents = "none", this.el.style.display = "none", this.el.style.zIndex = "9999", this.el.style.transition = "left 0.1s, top 0.1s", window.getComputedStyle(t).position === "static" && (t.style.position = "relative"), t.appendChild(this.el), this.updateTheme(e);
|
|
74
|
+
}
|
|
75
|
+
updateTheme(t) {
|
|
76
|
+
if (!t) return;
|
|
77
|
+
const e = t.tooltip || {}, o = e.textStyle || {};
|
|
78
|
+
this.el.style.backgroundColor = e.backgroundColor || t.tooltipBg || "rgba(6,13,31,0.93)", this.el.style.color = o.color || t.tooltipTextColor || "#c8ddf0";
|
|
79
|
+
let s = e.padding !== void 0 ? e.padding : t.tooltipPadding || 10;
|
|
80
|
+
typeof s == "number" ? this.el.style.padding = `${s}px` : Array.isArray(s) ? this.el.style.padding = s.map((i) => `${i}px`).join(" ") : this.el.style.padding = s + "px", this.el.style.borderRadius = "5px", this.el.style.border = `1px solid ${t.zoneBorderColor || "rgba(255,255,255,0.25)"}`, this.el.style.boxShadow = "0 0 10px rgba(0,0,0,0.45)", this.el.style.fontFamily = t.fontFamily || "Rajdhani, sans-serif", this.el.style.fontSize = o.fontSize !== void 0 ? typeof o.fontSize == "number" ? `${o.fontSize}px` : o.fontSize : "12px";
|
|
81
|
+
}
|
|
82
|
+
show(t, e, o, s = {}, i = {}, n = null) {
|
|
83
|
+
this.el.innerHTML = t, this.el.style.display = "block", this.el.style.backgroundColor = s.backgroundColor || i.tooltipBg || "rgba(6,13,31,0.93)";
|
|
84
|
+
const l = s.borderWidth !== void 0 ? typeof s.borderWidth == "number" ? `${s.borderWidth}px` : s.borderWidth : "1px", h = s.borderColor || (n ? n.borderColor || i.zoneBorderColor || n.color : "rgba(255,255,255,0.25)");
|
|
85
|
+
this.el.style.border = `${l} solid ${h}`, s.padding !== void 0 ? typeof s.padding == "number" ? this.el.style.padding = `${s.padding}px` : Array.isArray(s.padding) ? this.el.style.padding = s.padding.map((p) => `${p}px`).join(" ") : this.el.style.padding = s.padding : this.el.style.padding = (i.tooltipPadding || 10) + "px";
|
|
86
|
+
const a = s.textStyle || {};
|
|
87
|
+
this.el.style.color = a.color || i.tooltipTextColor || "#c8ddf0", this.el.style.fontStyle = a.fontStyle || "", this.el.style.fontWeight = a.fontWeight || a.fontStyle || "", this.el.style.fontSize = a.fontSize !== void 0 ? typeof a.fontSize == "number" ? `${a.fontSize}px` : a.fontSize : "12px", this.el.style.fontFamily = i.fontFamily || "Rajdhani, sans-serif";
|
|
88
|
+
const r = this.container.getBoundingClientRect(), c = this.el.offsetWidth, d = this.el.offsetHeight;
|
|
89
|
+
let f = e + 15, g = o - d / 2;
|
|
90
|
+
f + c > r.width && (f = e - c - 15), g < 0 && (g = 0), g + d > r.height && (g = r.height - d), this.el.style.left = f + "px", this.el.style.top = g + "px";
|
|
91
|
+
}
|
|
92
|
+
hide() {
|
|
93
|
+
this.el.style.display = "none";
|
|
94
|
+
}
|
|
95
|
+
dispose() {
|
|
96
|
+
this.el && this.el.parentNode && this.el.parentNode.removeChild(this.el);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function ct(v, t, e = {}, o = 45) {
|
|
100
|
+
const s = (f, g, p) => f == null ? p : typeof f == "string" && f.endsWith("%") ? parseFloat(f) / 100 * g : parseFloat(f) || 0, i = s(e.left, v, o), n = s(e.right, v, o), l = s(e.top, t, o), h = s(e.bottom, t, o), a = Math.max(0, v - i - n), r = Math.max(0, t - l - h), c = i + a / 2, d = l + r / 2;
|
|
101
|
+
return { left: i, right: n, top: l, bottom: h, availW: a, availH: r, cx: c, cy: d };
|
|
102
|
+
}
|
|
103
|
+
function nt(v, t, e, o, s, i = 1, n = 0) {
|
|
104
|
+
const l = (s + n) * i;
|
|
105
|
+
if (o === "circle" || o === "ring")
|
|
106
|
+
v.arc(t, e, l, 0, Math.PI * 2);
|
|
107
|
+
else if (o === "star") {
|
|
108
|
+
const h = l * 0.42;
|
|
109
|
+
for (let a = 0; a < 10; a++) {
|
|
110
|
+
const r = a * Math.PI / 5 - Math.PI / 2, c = a % 2 === 0 ? l : h;
|
|
111
|
+
a === 0 ? v.moveTo(t + c * Math.cos(r), e + c * Math.sin(r)) : v.lineTo(t + c * Math.cos(r), e + c * Math.sin(r));
|
|
112
|
+
}
|
|
113
|
+
v.closePath();
|
|
114
|
+
} else if (o === "triangle") {
|
|
115
|
+
for (let h = 0; h < 3; h++) {
|
|
116
|
+
const a = -Math.PI / 2 + h * 2 * Math.PI / 3, r = t + l * Math.cos(a), c = e + l * Math.sin(a);
|
|
117
|
+
h === 0 ? v.moveTo(r, c) : v.lineTo(r, c);
|
|
118
|
+
}
|
|
119
|
+
v.closePath();
|
|
120
|
+
} else if (o === "diamond")
|
|
121
|
+
v.moveTo(t, e - l), v.lineTo(t + l, e), v.lineTo(t, e + l), v.lineTo(t - l, e), v.closePath();
|
|
122
|
+
else if (o === "square" || o === "rect") {
|
|
123
|
+
const h = (s + n) * 2 * i, a = t - h / 2, r = e - h / 2;
|
|
124
|
+
v.rect(a, r, h, h);
|
|
125
|
+
} else
|
|
126
|
+
v.arc(t, e, l, 0, Math.PI * 2);
|
|
127
|
+
}
|
|
128
|
+
const Pt = [
|
|
129
|
+
{
|
|
130
|
+
id: "PD",
|
|
131
|
+
name: "PD",
|
|
132
|
+
desc: "局部放电",
|
|
133
|
+
color: "#A0DC99",
|
|
134
|
+
labelColor: "#1a4a1a",
|
|
135
|
+
points: [[1, 0, 0], [0.95, 0.05, 0], [0.95, 0, 0.05]]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "D1",
|
|
139
|
+
name: "D1",
|
|
140
|
+
desc: "低能放电",
|
|
141
|
+
color: "#77A3FC",
|
|
142
|
+
labelColor: "#ffffff",
|
|
143
|
+
points: [[0.85, 0.15, 0], [0, 1, 0], [0, 0.75, 0.25], [0.6, 0.15, 0.25]]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "D2",
|
|
147
|
+
name: "D2",
|
|
148
|
+
desc: "高能放电",
|
|
149
|
+
color: "#E3F2FF",
|
|
150
|
+
labelColor: "#1a3a6c",
|
|
151
|
+
points: [[0, 0.75, 0.25], [0, 0.28, 0.72], [0.32, 0.28, 0.4], [0.45, 0.15, 0.4], [0.6, 0.15, 0.25]]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "T1",
|
|
155
|
+
name: "T1",
|
|
156
|
+
desc: "热故障 < 300°C",
|
|
157
|
+
color: "#CBE4FD",
|
|
158
|
+
labelColor: "#1a2a6c",
|
|
159
|
+
points: [[0.95, 0.05, 0], [0.85, 0.05, 0.1], [0.9, 0, 0.1], [0.95, 0, 0.05]]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "T2",
|
|
163
|
+
name: "T2",
|
|
164
|
+
desc: "热故障 300 ~ 700°C",
|
|
165
|
+
color: "#56AD4A",
|
|
166
|
+
labelColor: "#e8ffe0",
|
|
167
|
+
points: [[0.85, 0.05, 0.1], [0.45, 0.05, 0.5], [0.5, 0, 0.5], [0.9, 0, 0.1]]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "T3",
|
|
171
|
+
name: "T3",
|
|
172
|
+
desc: "热故障 > 700°C",
|
|
173
|
+
color: "#5490FF",
|
|
174
|
+
labelColor: "#e0eeff",
|
|
175
|
+
points: [[0.5, 0, 0.5], [0.35, 0.15, 0.5], [0, 0.15, 0.85], [0, 0, 1]]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "DT",
|
|
179
|
+
name: "DT",
|
|
180
|
+
desc: "混合故障(放电+热)",
|
|
181
|
+
color: "#313CFF",
|
|
182
|
+
labelColor: "#c8d8ff",
|
|
183
|
+
points: [[0.95, 0.05, 0], [0.85, 0.15, 0], [0.45, 0.15, 0.4], [0.32, 0.28, 0.4], [0, 0.28, 0.72], [0, 0.15, 0.85], [0.35, 0.15, 0.5], [0.45, 0.05, 0.5]]
|
|
184
|
+
}
|
|
185
|
+
], Ot = [
|
|
186
|
+
{
|
|
187
|
+
id: "PD",
|
|
188
|
+
name: "PD",
|
|
189
|
+
desc: "电晕型局部放电",
|
|
190
|
+
color: "#A0DC99",
|
|
191
|
+
labelColor: "#1a4a1a",
|
|
192
|
+
points: [[0.975, 0, 0.025], [0.955, 0.02, 0.025], [0.83, 0.02, 0.15], [0.85, 0, 0.15]]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "S",
|
|
196
|
+
name: "S",
|
|
197
|
+
desc: "温度<200℃时的杂散气体",
|
|
198
|
+
color: "#313CFF",
|
|
199
|
+
labelColor: "#c8d8ff",
|
|
200
|
+
points: [[1, 0, 0], [0.45, 0.55, 0], [0.08, 0.52, 0.4], [0.08, 0.3, 0.62], [0.15, 0.3, 0.55], [0.15, 0.25, 0.6], [0.4, 0.25, 0.35], [0.65, 0, 0.35], [0.85, 0, 0.15], [0.83, 0.02, 0.15], [0.955, 0.02, 0.025], [0.975, 0, 0.025]]
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: "C",
|
|
204
|
+
name: "C",
|
|
205
|
+
desc: "绝缘纸可能碳化",
|
|
206
|
+
color: "#3587FF",
|
|
207
|
+
labelColor: "#ffffff",
|
|
208
|
+
points: [[0.65, 0, 0.35], [0.4, 0.25, 0.35], [0.15, 0.25, 0.6], [0.15, 0.3, 0.55], [0, 0.3, 0.7], [0, 0, 1]]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: "O",
|
|
212
|
+
name: "O",
|
|
213
|
+
desc: "过热温度<250℃,绝缘纸不碳化",
|
|
214
|
+
color: "#6AA4FF",
|
|
215
|
+
labelColor: "#c8d8ff",
|
|
216
|
+
points: [[0.08, 0.92, 0], [0, 1, 0], [0, 0.3, 0.7], [0.08, 0.3, 0.62]]
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "ND",
|
|
220
|
+
name: "ND",
|
|
221
|
+
desc: "未确定",
|
|
222
|
+
color: "#BEE1FD",
|
|
223
|
+
labelColor: "#1a2a6c",
|
|
224
|
+
points: [[0.45, 0.55, 0], [0.08, 0.92, 0], [0.08, 0.52, 0.4]]
|
|
225
|
+
}
|
|
226
|
+
], Nt = [
|
|
227
|
+
{
|
|
228
|
+
id: "PD",
|
|
229
|
+
name: "PD",
|
|
230
|
+
desc: "电晕型局部放电",
|
|
231
|
+
color: "#A0DC99",
|
|
232
|
+
labelColor: "#1a4a1a",
|
|
233
|
+
points: [[0.85, 0.15, 0], [0.83, 0.15, 0.02], [0.96, 0.02, 0.02], [0.98, 0.02, 0]]
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: "T2",
|
|
237
|
+
name: "T2",
|
|
238
|
+
desc: "中温过热 (300~700℃)",
|
|
239
|
+
color: "#56AD4A",
|
|
240
|
+
labelColor: "#1a4a1a",
|
|
241
|
+
points: [[0.9, 0, 0.1], [0.65, 0, 0.35], [0.525, 0.125, 0.35], [0.775, 0.125, 0.1]]
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: "S",
|
|
245
|
+
name: "S",
|
|
246
|
+
desc: "温度<200℃时的杂散气体",
|
|
247
|
+
color: "#313CFF",
|
|
248
|
+
labelColor: "#c8d8ff",
|
|
249
|
+
points: [[0.85, 0.15, 0], [0.35, 0.65, 0], [0.25, 0.65, 0.1], [0.75, 0.15, 0.1]]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
id: "C",
|
|
253
|
+
name: "C",
|
|
254
|
+
desc: "绝缘纸可能碳化",
|
|
255
|
+
color: "#3587FF",
|
|
256
|
+
labelColor: "#ffffff",
|
|
257
|
+
points: [[0.6, 0.3, 0.1], [0, 0.3, 0.7], [0.15, 0.15, 0.7], [0.35, 0.15, 0.5], [0.375, 0.125, 0.5], [0.775, 0.125, 0.1]]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: "O",
|
|
261
|
+
name: "O",
|
|
262
|
+
desc: "过热",
|
|
263
|
+
color: "#6AA4FF",
|
|
264
|
+
labelColor: "#1a3a6c",
|
|
265
|
+
points: [[1, 0, 0], [0.98, 0.02, 0], [0.96, 0.02, 0.02], [0.83, 0.15, 0.02], [0.85, 0.15, 0], [0, 1, 0], [0, 0.9, 0.1], [0.35, 0.55, 0.1], [0.45, 0.55, 0], [0.85, 0.15, 0], [0.75, 0.15, 0.1], [0.9, 0, 0.1]]
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
id: "T3-H",
|
|
269
|
+
name: "T3-H",
|
|
270
|
+
desc: "仅矿物油过热",
|
|
271
|
+
color: "#77A3FC",
|
|
272
|
+
labelColor: "#ffffff",
|
|
273
|
+
points: [[0.35, 0.3, 0.35], [0, 0.65, 0.35], [0, 0, 1], [0.65, 0, 0.35], [0.525, 0.125, 0.35], [0.375, 0.125, 0.5], [0.35, 0.15, 0.5], [0.15, 0.15, 0.7], [0, 0.3, 0.7]],
|
|
274
|
+
labelAt: [0.1, 0.42, 0.48]
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
id: "ND",
|
|
278
|
+
name: "ND",
|
|
279
|
+
desc: "未确定",
|
|
280
|
+
color: "#E3F2FF",
|
|
281
|
+
labelColor: "#1a3a6c",
|
|
282
|
+
points: [[0.6, 0.3, 0.1], [0, 0.9, 0.1], [0, 0.65, 0.35], [0.35, 0.3, 0.35]]
|
|
283
|
+
}
|
|
284
|
+
], it = [
|
|
285
|
+
{
|
|
286
|
+
name: "PD",
|
|
287
|
+
title: "电晕型局部放电",
|
|
288
|
+
color: "#A0DC99",
|
|
289
|
+
desc: "电晕型局部放电",
|
|
290
|
+
poly: [
|
|
291
|
+
[0, 33],
|
|
292
|
+
[-1, 33],
|
|
293
|
+
[-1, 24.5],
|
|
294
|
+
[0, 24.5]
|
|
295
|
+
]
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "D1",
|
|
299
|
+
title: "低能量放电或火花型局部放电",
|
|
300
|
+
color: "#81ADFF",
|
|
301
|
+
desc: "低能量放电或火花型局部放电",
|
|
302
|
+
poly: [
|
|
303
|
+
[0, 40],
|
|
304
|
+
[38, 12],
|
|
305
|
+
[32, -6.1],
|
|
306
|
+
[4, 16],
|
|
307
|
+
[0, 1.5]
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: "D2",
|
|
312
|
+
title: "高能量放电",
|
|
313
|
+
color: "#E3F2FF",
|
|
314
|
+
desc: "高能量放电",
|
|
315
|
+
poly: [
|
|
316
|
+
[4, 16],
|
|
317
|
+
[32, -6.1],
|
|
318
|
+
[24.3, -30],
|
|
319
|
+
[0, -3],
|
|
320
|
+
[0, 1.5]
|
|
321
|
+
]
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "T3",
|
|
325
|
+
title: "过热故障,t>700℃",
|
|
326
|
+
color: "#5490FF",
|
|
327
|
+
desc: "过热故障,t>700℃",
|
|
328
|
+
poly: [
|
|
329
|
+
[0, -3],
|
|
330
|
+
[24.3, -30],
|
|
331
|
+
[23.5, -32.4],
|
|
332
|
+
[1, -32.4],
|
|
333
|
+
[-6, -4]
|
|
334
|
+
]
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "T2",
|
|
338
|
+
title: "过热故障,300℃<t<700℃",
|
|
339
|
+
color: "#56AD4A",
|
|
340
|
+
desc: "中温热故障,铁心或铜导体局部过热,C₂H₄+CH₄ 均较高",
|
|
341
|
+
poly: [
|
|
342
|
+
[-6, -4],
|
|
343
|
+
[1, -32.4],
|
|
344
|
+
[-22.5, -32.4]
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: "T1",
|
|
349
|
+
title: "过热故障,t<300℃",
|
|
350
|
+
color: "#CBE4FD",
|
|
351
|
+
desc: "低温热故障(<300°C),CH₄+C₂H₆ 为主要特征气体",
|
|
352
|
+
poly: [
|
|
353
|
+
[-6, -4],
|
|
354
|
+
[-22.5, -32.4],
|
|
355
|
+
[-23.5, -32.4],
|
|
356
|
+
[-35, 3],
|
|
357
|
+
[0, 1.5],
|
|
358
|
+
[0, -3]
|
|
359
|
+
]
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: "S",
|
|
363
|
+
title: "温度<200℃时的杂散气体",
|
|
364
|
+
color: "#2735FF",
|
|
365
|
+
desc: "温度<200℃时的杂散气体",
|
|
366
|
+
poly: [
|
|
367
|
+
[0, 1.5],
|
|
368
|
+
[-35, 3.1],
|
|
369
|
+
[-38, 12.4],
|
|
370
|
+
[0, 40],
|
|
371
|
+
[0, 33],
|
|
372
|
+
[-1, 33],
|
|
373
|
+
[-1, 24.5],
|
|
374
|
+
[0, 24.5]
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
], lt = [
|
|
378
|
+
{
|
|
379
|
+
name: "PD",
|
|
380
|
+
title: "电晕型局部放电",
|
|
381
|
+
color: "#A0DC99",
|
|
382
|
+
desc: "内部局部放电,H₂ 占比 >93%",
|
|
383
|
+
poly: [
|
|
384
|
+
[0, 33],
|
|
385
|
+
[-1, 33],
|
|
386
|
+
[-1, 24.5],
|
|
387
|
+
[0, 24.5]
|
|
388
|
+
]
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: "D1",
|
|
392
|
+
title: "低能量放电或火花型局部放电",
|
|
393
|
+
color: "#81ADFF",
|
|
394
|
+
desc: "低能量火花放电,绕组匝间短路早期,H₂+C₂H₂ 较高",
|
|
395
|
+
poly: [
|
|
396
|
+
[0, 40],
|
|
397
|
+
[38, 12],
|
|
398
|
+
[32, -6.1],
|
|
399
|
+
[4, 16],
|
|
400
|
+
[0, 1.5]
|
|
401
|
+
]
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: "D2",
|
|
405
|
+
title: "高能量放电",
|
|
406
|
+
color: "#E3F2FF",
|
|
407
|
+
desc: "高能量电弧放电,严重绝缘故障,C₂H₂ 占比高",
|
|
408
|
+
poly: [
|
|
409
|
+
[4, 16],
|
|
410
|
+
[32, -6.1],
|
|
411
|
+
[24.3, -30],
|
|
412
|
+
[0, -3],
|
|
413
|
+
[0, 1.5]
|
|
414
|
+
]
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "S",
|
|
418
|
+
title: "温度<200℃时的杂散气体",
|
|
419
|
+
color: "#2735FF",
|
|
420
|
+
desc: "温度<200℃时的杂散气体",
|
|
421
|
+
poly: [
|
|
422
|
+
[0, 1.5],
|
|
423
|
+
[-35, 3.1],
|
|
424
|
+
[-38, 12.4],
|
|
425
|
+
[0, 40],
|
|
426
|
+
[0, 33],
|
|
427
|
+
[-1, 33],
|
|
428
|
+
[-1, 24.5],
|
|
429
|
+
[0, 24.5]
|
|
430
|
+
]
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
name: "T3-H",
|
|
434
|
+
title: "——仅矿物油过热",
|
|
435
|
+
color: "#25A53D",
|
|
436
|
+
desc: "700℃以上高温过热,C₂H₄ 占比高,总烃快速增长",
|
|
437
|
+
poly: [
|
|
438
|
+
[0, -3],
|
|
439
|
+
[24.3, -30],
|
|
440
|
+
[23.5, -32.4],
|
|
441
|
+
[2.5, -32.4],
|
|
442
|
+
[-3.5, -3]
|
|
443
|
+
]
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
name: "C",
|
|
447
|
+
title: "绝缘纸可能碳化",
|
|
448
|
+
color: "#3587FF",
|
|
449
|
+
desc: "绝缘纸可能碳化",
|
|
450
|
+
poly: [
|
|
451
|
+
[-3.5, -3],
|
|
452
|
+
[2.5, -32.4],
|
|
453
|
+
[-21.5, -32.4],
|
|
454
|
+
[-11, -8]
|
|
455
|
+
]
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: "O",
|
|
459
|
+
title: "过热温度<250℃,绝缘纸不碳化",
|
|
460
|
+
color: "#6AA4FF",
|
|
461
|
+
desc: "过热温度<250℃,绝缘纸不碳化",
|
|
462
|
+
poly: [
|
|
463
|
+
[-3.5, -3],
|
|
464
|
+
[-11, -8],
|
|
465
|
+
[-21.5, -32.4],
|
|
466
|
+
[-23.5, -32.4],
|
|
467
|
+
[-35, 3.1],
|
|
468
|
+
[0, 1.5],
|
|
469
|
+
[0, -3]
|
|
470
|
+
]
|
|
471
|
+
}
|
|
472
|
+
], Wt = [
|
|
473
|
+
{
|
|
474
|
+
name: "D1/PD",
|
|
475
|
+
title: "局部放电",
|
|
476
|
+
color: "rgba(56,139,253,0.22)",
|
|
477
|
+
labelColor: "rgba(56,139,253,0.9)",
|
|
478
|
+
poly: [[-2, 3], [3, 3], [3, 0], [-2, 0]]
|
|
479
|
+
// Y >= 1 (logY >= 0), full X
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: "T3/DT",
|
|
483
|
+
title: "高温热故障/放电+热",
|
|
484
|
+
color: "rgba(63,185,80,0.18)",
|
|
485
|
+
labelColor: "rgba(63,185,80,0.9)",
|
|
486
|
+
poly: [[-2, 0], [3, 0], [3, -2], [-2, -2]]
|
|
487
|
+
// -2 <= logY <= 0, full X
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
name: "T1",
|
|
491
|
+
title: "过热故障,t<300℃",
|
|
492
|
+
color: "rgba(250,199,75,0.22)",
|
|
493
|
+
labelColor: "rgba(250,199,75,0.9)",
|
|
494
|
+
poly: [[-2, -2], [0, -2], [0, -3], [-2, -3]]
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: "T2",
|
|
498
|
+
title: "过热故障,300℃<t<700℃",
|
|
499
|
+
color: "rgba(240,120,60,0.25)",
|
|
500
|
+
labelColor: "rgba(240,120,60,0.9)",
|
|
501
|
+
poly: [[0, -2], [0.4, -2], [0.4, -3], [0, -3]]
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: "T3",
|
|
505
|
+
title: "过热故障,t>700℃",
|
|
506
|
+
color: "rgba(46,160,67,0.22)",
|
|
507
|
+
labelColor: "rgba(46,160,67,0.9)",
|
|
508
|
+
poly: [[0.4, -2], [3, -2], [3, -3], [0.4, -3]]
|
|
509
|
+
}
|
|
510
|
+
], Zt = [
|
|
511
|
+
{
|
|
512
|
+
name: "D2",
|
|
513
|
+
title: "高能放电",
|
|
514
|
+
color: "rgba(56,139,253,0.28)",
|
|
515
|
+
labelColor: "rgba(100,180,255,0.95)",
|
|
516
|
+
// Y >= 10 AND X >= 10 → logY>=1, logX>=1
|
|
517
|
+
poly: [[-2, 3], [3, 3], [3, 0], [1, 0], [1, 1], [-2, 1]]
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
name: "D1",
|
|
521
|
+
title: "低能放电",
|
|
522
|
+
color: "rgba(56,139,253,0.15)",
|
|
523
|
+
labelColor: "rgba(130,190,255,0.85)",
|
|
524
|
+
// L-shape: Y>=1 AND X<10
|
|
525
|
+
// = (logY in [0,3], logX in [-2,1])
|
|
526
|
+
poly: [[-2, 1], [1, 1], [1, 0], [-2, 0]]
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
name: "PD",
|
|
530
|
+
title: "局部放电",
|
|
531
|
+
color: "rgba(63,185,80,0.18)",
|
|
532
|
+
labelColor: "rgba(63,185,80,0.9)",
|
|
533
|
+
poly: [[-2, 0], [0, 0], [0, -2], [-2, -2]]
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: "T3/DT",
|
|
537
|
+
title: "高温热/放电+热",
|
|
538
|
+
color: "rgba(250,199,75,0.18)",
|
|
539
|
+
labelColor: "rgba(250,199,75,0.9)",
|
|
540
|
+
poly: [[0, 0], [3, 0], [3, -2], [0, -2]]
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
name: "T1",
|
|
544
|
+
title: "过热故障,t<300℃",
|
|
545
|
+
color: "rgba(250,199,75,0.22)",
|
|
546
|
+
labelColor: "rgba(250,199,75,0.9)",
|
|
547
|
+
poly: [[-2, -2], [0, -2], [0, -3], [-2, -3]]
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
name: "T2",
|
|
551
|
+
title: "过热故障,300℃<t<700℃",
|
|
552
|
+
color: "rgba(240,120,60,0.25)",
|
|
553
|
+
labelColor: "rgba(240,120,60,0.9)",
|
|
554
|
+
poly: [[0, -2], [0.4, -2], [0.4, -3], [0, -3]]
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: "T3",
|
|
558
|
+
title: "过热故障,t>700℃",
|
|
559
|
+
color: "rgba(46,160,67,0.22)",
|
|
560
|
+
labelColor: "rgba(46,160,67,0.9)",
|
|
561
|
+
poly: [[0.4, -2], [3, -2], [3, -3], [0.4, -3]]
|
|
562
|
+
}
|
|
563
|
+
], kt = [
|
|
564
|
+
{
|
|
565
|
+
name: "PD",
|
|
566
|
+
title: "电晕型局部放电",
|
|
567
|
+
fill: "#09FAFF",
|
|
568
|
+
edgeColor: "#09FAFF",
|
|
569
|
+
p1: [0, 0, 0],
|
|
570
|
+
// 开始坐标点
|
|
571
|
+
p2: [0.2, 0.1, 0.01]
|
|
572
|
+
// 结束坐标点
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "T1",
|
|
576
|
+
title: "过热故障,t<300℃",
|
|
577
|
+
fill: "#EC9B9A",
|
|
578
|
+
edgeColor: "#EC9B9A",
|
|
579
|
+
p1: [0, 1, 0],
|
|
580
|
+
p2: [1, 10, 0.01]
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
name: "T2",
|
|
584
|
+
title: "过热故障,300℃<t<700℃",
|
|
585
|
+
fill: "#A0C9F5",
|
|
586
|
+
edgeColor: "#A0C9F5",
|
|
587
|
+
p1: [1, 1, 0],
|
|
588
|
+
p2: [4, 10, 0.1]
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
name: "T3",
|
|
592
|
+
title: "过热故障,t>700℃",
|
|
593
|
+
fill: "#CA677B",
|
|
594
|
+
edgeColor: "#CA677B",
|
|
595
|
+
p1: [4, 1, 0],
|
|
596
|
+
p2: [10, 10, 0.2]
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "D1",
|
|
600
|
+
title: "低能量放电火花型局部放电",
|
|
601
|
+
fill: "#FF7B16",
|
|
602
|
+
edgeColor: "#FF7B16",
|
|
603
|
+
p1: [1, 0.1, 1],
|
|
604
|
+
p2: [10, 0.5, 10]
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
name: "D2",
|
|
608
|
+
title: "高能量放电",
|
|
609
|
+
fill: "#48FF16",
|
|
610
|
+
edgeColor: "#48FF16",
|
|
611
|
+
p1: [2, 0.1, 0.6],
|
|
612
|
+
p2: [10, 1, 2.5]
|
|
613
|
+
}
|
|
614
|
+
];
|
|
615
|
+
function ft(v) {
|
|
616
|
+
if (!Array.isArray(v)) return [];
|
|
617
|
+
const t = v.map((o) => Math.max(Number(o) || 0, 0)), e = t.reduce((o, s) => o + s, 0);
|
|
618
|
+
return e === 0 ? t.map(() => 0) : t.map((o) => {
|
|
619
|
+
const s = o / e * 100;
|
|
620
|
+
return Math.round(s * 100) / 100;
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
function mt(v, t, e, o, s) {
|
|
624
|
+
const i = [v, t, e, o, s].map((d) => Math.max(0, Number(d) || 0)), n = i.reduce((d, f) => d + f, 0);
|
|
625
|
+
if (n <= 0) return null;
|
|
626
|
+
const l = i.map((d) => d / n), h = 40, a = [90, 18, -54, -126, 162];
|
|
627
|
+
let r = 0, c = 0;
|
|
628
|
+
for (let d = 0; d < 5; d++) {
|
|
629
|
+
const f = a[d] * Math.PI / 180;
|
|
630
|
+
r += l[d] * h * Math.cos(f), c += l[d] * h * Math.sin(f);
|
|
631
|
+
}
|
|
632
|
+
return { x: r, y: c, ratio: l };
|
|
633
|
+
}
|
|
634
|
+
function bt(v, t, e) {
|
|
635
|
+
let o = !1;
|
|
636
|
+
for (let s = 0, i = e.length - 1; s < e.length; i = s++) {
|
|
637
|
+
const [n, l] = e[s], [h, a] = e[i];
|
|
638
|
+
l > t != a > t && v < (h - n) * (t - l) / (a - l) + n && (o = !o);
|
|
639
|
+
}
|
|
640
|
+
return o;
|
|
641
|
+
}
|
|
642
|
+
const Ut = {
|
|
643
|
+
duvalTriangle1: (v, t, e) => {
|
|
644
|
+
if (v + t + e <= 0) return { code: "INVALID", label: "无效输入" };
|
|
645
|
+
const [s, i, n] = ft([v, t, e]);
|
|
646
|
+
return s >= 98 ? { code: "PD", label: "局部放电" } : i >= 23 && n <= 13 ? { code: "D1", label: "低能放电" } : i >= 23 && i <= 40 && n >= 13 && n <= 29 ? { code: "D2", label: "高能放电" } : n <= 4 && i <= 20 ? { code: "T1", label: "热故障(t < 300℃)" } : n <= 4 && i > 20 && i <= 50 ? { code: "T2", label: "热故障(300℃ < t < 700℃)" } : n <= 15 && i >= 50 ? { code: "T3", label: "热故障(t > 700℃)" } : { code: "ND", label: "区域未定义" };
|
|
647
|
+
},
|
|
648
|
+
duvalTriangle4: (v, t, e) => {
|
|
649
|
+
if (v + t + e <= 0) return { code: "INVALID", label: "无效输入" };
|
|
650
|
+
const [s, i, n] = ft([v, t, e]);
|
|
651
|
+
return i >= 1 && n >= 2 && n <= 15 ? { code: "PD", label: "电晕型局部放电" } : s <= 9 && i >= 24 && i <= 46 && n <= 36 ? { code: "S", label: "温度<200℃时的杂散气体" } : s <= 9 && i <= 30 ? { code: "O", label: "过热温度<250℃,绝缘纸不碳化" } : i >= 24 && i <= 30 && n >= 36 ? { code: "C", label: "绝缘纸可能碳化" } : { code: "ND", label: "区域未定义" };
|
|
652
|
+
},
|
|
653
|
+
duvalTriangle5: (v, t, e) => {
|
|
654
|
+
if (v + t + e <= 0) return { code: "INVALID", label: "无效输入" };
|
|
655
|
+
const [s, i, n] = ft([v, t, e]);
|
|
656
|
+
return s >= 85 && s <= 100 && n >= 0 && n <= 15 ? { code: "PD", label: "电晕型局部放电" } : i >= 35 && i <= 100 && n >= 0 && n <= 14 || i >= 35 && i <= 70 && n >= 30 && n <= 75 ? { code: "T3", label: "高温过热故障(t > 700℃)" } : i >= 10 && i <= 35 && n >= 0 && n <= 12 ? { code: "T2", label: "中温过热故障(t > 300℃)" } : i >= 10 && i <= 70 && n >= 12 && n <= 30 ? { code: "C", label: "绝缘纸碳化故障" } : i <= 10 && n >= 15 && n <= 54 ? { code: "S", label: "矿物油杂散气体(低温过热 90℃~200℃)" } : i <= 10 && (n <= 15 || n >= 54) ? { code: "O", label: "过热故障" } : { code: "ND", label: "区域未定义" };
|
|
657
|
+
},
|
|
658
|
+
duvalPentagon1: (v, t, e, o, s) => {
|
|
659
|
+
const i = mt(v, e, o, t, s);
|
|
660
|
+
if (!i) return { code: "INVALID", label: "无效输入", desc: "", color: "" };
|
|
661
|
+
const { x: n, y: l } = i;
|
|
662
|
+
for (let a = 0; a < it.length - 1; a++) {
|
|
663
|
+
const r = it[a];
|
|
664
|
+
if (bt(n, l, r.poly))
|
|
665
|
+
return { code: r.name, label: r.title, desc: r.desc, color: r.color };
|
|
666
|
+
}
|
|
667
|
+
const h = it[it.length - 1];
|
|
668
|
+
return { code: h.name, label: h.title, desc: h.desc, color: h.color };
|
|
669
|
+
},
|
|
670
|
+
duvalPentagon2: (v, t, e, o, s) => {
|
|
671
|
+
const i = mt(v, e, o, t, s);
|
|
672
|
+
if (!i) return { code: "INVALID", label: "无效输入", desc: "", color: "" };
|
|
673
|
+
const { x: n, y: l } = i;
|
|
674
|
+
for (let a = 0; a < lt.length - 1; a++) {
|
|
675
|
+
const r = lt[a];
|
|
676
|
+
if (bt(n, l, r.poly))
|
|
677
|
+
return { code: r.name, label: r.title, desc: r.desc, color: r.color };
|
|
678
|
+
}
|
|
679
|
+
const h = lt[lt.length - 1];
|
|
680
|
+
return { code: h.name, label: h.title, desc: h.desc, color: h.color };
|
|
681
|
+
}
|
|
682
|
+
}, $ = class $ {
|
|
683
|
+
// 度
|
|
684
|
+
/**
|
|
685
|
+
* 将 doc 坐标(R=40,Y向上)转换为归一化 ratio 向量
|
|
686
|
+
* @param {number} x - doc 坐标系 x
|
|
687
|
+
* @param {number} y - doc 坐标系 y
|
|
688
|
+
* @returns {number[]|null} - 长度为5的 ratio 向量,或 null(如果在五边形外)
|
|
689
|
+
*/
|
|
690
|
+
static docToRatio(t, e) {
|
|
691
|
+
const o = $.DOC_R, i = $.DOC_ANGLES.map((n) => {
|
|
692
|
+
const l = n * Math.PI / 180;
|
|
693
|
+
return [o * Math.cos(l), o * Math.sin(l)];
|
|
694
|
+
});
|
|
695
|
+
for (let n = 0; n < 5; n++)
|
|
696
|
+
if (Math.hypot(t - i[n][0], e - i[n][1]) < 0.5) {
|
|
697
|
+
const l = [0, 0, 0, 0, 0];
|
|
698
|
+
return l[n] = 1, l;
|
|
699
|
+
}
|
|
700
|
+
for (let n = 0; n < 5; n++) {
|
|
701
|
+
const l = (n + 1) % 5, [h, a] = i[n], [r, c] = i[l], d = r - h, f = c - a, g = d * d + f * f;
|
|
702
|
+
if (g < 1e-10) continue;
|
|
703
|
+
const p = ((t - h) * d + (e - a) * f) / g;
|
|
704
|
+
if (p >= -1e-6 && p <= 1 + 1e-6 && Math.abs((e - a) * d - (t - h) * f) / Math.sqrt(g) < 0.15) {
|
|
705
|
+
const y = Math.max(0, Math.min(1, p)), u = [0, 0, 0, 0, 0];
|
|
706
|
+
return u[n] = 1 - y, u[l] = y, u;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
for (let n = 0; n < 5; n++) {
|
|
710
|
+
const l = [0, 0], h = i[n], a = i[(n + 1) % 5], r = (h[1] - a[1]) * (l[0] - a[0]) + (a[0] - h[0]) * (l[1] - a[1]);
|
|
711
|
+
if (Math.abs(r) < 1e-10) continue;
|
|
712
|
+
const c = ((h[1] - a[1]) * (t - a[0]) + (a[0] - h[0]) * (e - a[1])) / r, d = ((a[1] - l[1]) * (t - a[0]) + (l[0] - a[0]) * (e - a[1])) / r, f = 1 - c - d;
|
|
713
|
+
if (c >= -0.01 && d >= -0.01 && f >= -0.01) {
|
|
714
|
+
const g = new Array(5).fill(c / 5);
|
|
715
|
+
g[n] += d, g[(n + 1) % 5] += f;
|
|
716
|
+
const p = g.reduce((m, y) => m + Math.max(0, y), 0);
|
|
717
|
+
return g.map((m) => Math.max(0, m) / p);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* 将归一化 ratio 向量转换为 doc 坐标
|
|
724
|
+
* @param {number[]} ratio - 长度为5的 ratio 向量
|
|
725
|
+
* @returns {number[]} - [x, y] doc 坐标
|
|
726
|
+
*/
|
|
727
|
+
static ratioToDoc(t) {
|
|
728
|
+
const e = $.DOC_R, o = $.DOC_ANGLES;
|
|
729
|
+
let s = 0, i = 0;
|
|
730
|
+
for (let n = 0; n < 5; n++) {
|
|
731
|
+
const l = o[n] * Math.PI / 180;
|
|
732
|
+
s += t[n] * e * Math.cos(l), i += t[n] * e * Math.sin(l);
|
|
733
|
+
}
|
|
734
|
+
return [s, i];
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* 将 canvas 像素坐标转换为归一化 ratio 向量
|
|
738
|
+
* @param {number} px - canvas 像素坐标 x
|
|
739
|
+
* @param {number} py - canvas 像素坐标 y
|
|
740
|
+
* @param {number} cx - 五边形中心 x
|
|
741
|
+
* @param {number} cy - 五边形中心 y
|
|
742
|
+
* @param {number} R - 五边形半径
|
|
743
|
+
* @returns {number[]|null} - ratio 向量或 null
|
|
744
|
+
*/
|
|
745
|
+
static canvasToRatio(t, e, o, s, i) {
|
|
746
|
+
const n = Array.from({ length: 5 }, (l, h) => $.canvasVertex(h, o, s, i));
|
|
747
|
+
if (!gt(t, e, n)) return null;
|
|
748
|
+
for (let l = 0; l < 5; l++) {
|
|
749
|
+
const h = [o, s], a = n[l], r = n[(l + 1) % 5], c = (a[1] - r[1]) * (h[0] - r[0]) + (r[0] - a[0]) * (h[1] - r[1]);
|
|
750
|
+
if (Math.abs(c) < 1e-10) continue;
|
|
751
|
+
const d = ((a[1] - r[1]) * (t - r[0]) + (r[0] - a[0]) * (e - r[1])) / c, f = ((r[1] - h[1]) * (t - r[0]) + (h[0] - r[0]) * (e - r[1])) / c, g = 1 - d - f;
|
|
752
|
+
if (d >= -1e-9 && f >= -1e-9 && g >= -1e-9) {
|
|
753
|
+
const p = new Array(5).fill(d / 5);
|
|
754
|
+
p[l] += f, p[(l + 1) % 5] += g;
|
|
755
|
+
const m = p.reduce((y, u) => y + Math.max(0, u), 0);
|
|
756
|
+
return p.map((y) => Math.max(0, y) / m);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
// canvas 顶点坐标(Y 向下,从顶部 12 点顺时针)
|
|
762
|
+
static canvasVertex(t, e, o, s) {
|
|
763
|
+
const i = -Math.PI / 2 + t * 2 * Math.PI / 5;
|
|
764
|
+
return [e + s * Math.cos(i), o + s * Math.sin(i)];
|
|
765
|
+
}
|
|
766
|
+
// doc 坐标 → canvas 坐标(直接线性映射)
|
|
767
|
+
static docToCanvas(t, e, o, s, i) {
|
|
768
|
+
return [
|
|
769
|
+
o + t / $.DOC_R * i,
|
|
770
|
+
s - e / $.DOC_R * i
|
|
771
|
+
// Y 轴反转
|
|
772
|
+
];
|
|
773
|
+
}
|
|
774
|
+
// poly 统一解析 → canvas 点数组
|
|
775
|
+
static polyToCanvas(t, e, o, s) {
|
|
776
|
+
if (t.length === 2)
|
|
777
|
+
return $.docToCanvas(t[0], t[1], e, o, s);
|
|
778
|
+
let i = 0, n = 0;
|
|
779
|
+
for (let l = 0; l < 5; l++) {
|
|
780
|
+
const [h, a] = $.canvasVertex(l, e, o, s);
|
|
781
|
+
i += t[l] * h, n += t[l] * a;
|
|
782
|
+
}
|
|
783
|
+
return [i, n];
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* 将任意格式的 series.data 解析为归一化 ratio 向量
|
|
787
|
+
* @param {object|number[]} data - 待解析的数据
|
|
788
|
+
* @param {string[]} gasOrder - 气体顺序
|
|
789
|
+
* @returns {number[]|null} - ratio 向量或 null
|
|
790
|
+
*/
|
|
791
|
+
static resolveData(t, e) {
|
|
792
|
+
if (!t) return null;
|
|
793
|
+
if (Array.isArray(t)) {
|
|
794
|
+
const o = t.length > 0 && t[0] && typeof t[0] == "object";
|
|
795
|
+
let s;
|
|
796
|
+
o && e ? s = e.map((n) => {
|
|
797
|
+
const l = t.find((h) => h.name === n);
|
|
798
|
+
return Math.max(0, (l ? l.value : 0) || 0);
|
|
799
|
+
}) : s = t.map((n) => Math.max(0, n || 0));
|
|
800
|
+
const i = s.reduce((n, l) => n + l, 0);
|
|
801
|
+
return i <= 1e-9 ? null : s.map((n) => n / i);
|
|
802
|
+
}
|
|
803
|
+
return "x" in t && "y" in t ? $.docToRatio(t.x, t.y) : null;
|
|
804
|
+
}
|
|
805
|
+
// ratio → doc 坐标字符串(调试/显示用)
|
|
806
|
+
static ratioToDocStr(t, e = 1) {
|
|
807
|
+
const [o, s] = $.ratioToDoc(t);
|
|
808
|
+
return `(${o.toFixed(e)}, ${s.toFixed(e)})`;
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
// doc 坐标系参数(固定)
|
|
812
|
+
K($, "DOC_R", 40), // 顶点角度(数学坐标,顺时针,H2 在顶部=90°)
|
|
813
|
+
K($, "DOC_ANGLES", [90, 18, -54, -126, 162]);
|
|
814
|
+
let W = $;
|
|
815
|
+
const J = class J {
|
|
816
|
+
static resolve(t) {
|
|
817
|
+
return !t || t === "dark" ? { ...J.DARK } : t === "light" ? { ...J.LIGHT } : E({ ...J.DARK }, t);
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
K(J, "DARK", {
|
|
821
|
+
backgroundColor: "transparent",
|
|
822
|
+
// 画布背景色
|
|
823
|
+
pointStyle: {
|
|
824
|
+
itemStyle: {
|
|
825
|
+
shape: "circle",
|
|
826
|
+
radius: 3,
|
|
827
|
+
color: "#00e5ff",
|
|
828
|
+
borderColor: "#ffffff",
|
|
829
|
+
borderWidth: 0.5
|
|
830
|
+
},
|
|
831
|
+
textStyle: {
|
|
832
|
+
show: !1,
|
|
833
|
+
fontSize: 11,
|
|
834
|
+
color: "#00e5ff",
|
|
835
|
+
position: "top"
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
vertex: {
|
|
839
|
+
show: !0,
|
|
840
|
+
labelStyle: {
|
|
841
|
+
color: "#01FFE1",
|
|
842
|
+
fontStyle: "normal",
|
|
843
|
+
fontWeight: "bold",
|
|
844
|
+
fontSize: 14
|
|
845
|
+
}
|
|
846
|
+
},
|
|
847
|
+
grid: {
|
|
848
|
+
show: !1,
|
|
849
|
+
lineStyle: {
|
|
850
|
+
lineType: "solid",
|
|
851
|
+
lineWidth: 1,
|
|
852
|
+
lineColor: "rgba(0, 229, 255, 0.15)"
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
zone: {
|
|
856
|
+
labelStyle: {
|
|
857
|
+
show: !0,
|
|
858
|
+
color: "#ffffff",
|
|
859
|
+
fontStyle: "bold",
|
|
860
|
+
fontSize: 12,
|
|
861
|
+
letterSpacing: 0,
|
|
862
|
+
textShadow: "rgba(0,0,0,0.85)",
|
|
863
|
+
padding: [4, 8],
|
|
864
|
+
borderRadius: 4,
|
|
865
|
+
backgroundColor: "transparent"
|
|
866
|
+
},
|
|
867
|
+
borderStyle: {
|
|
868
|
+
show: !0,
|
|
869
|
+
stroke: 1,
|
|
870
|
+
type: "solid",
|
|
871
|
+
color: "rgba(255, 255, 255, 0.25)"
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
tooltip: {
|
|
875
|
+
show: !0,
|
|
876
|
+
showDiagnostic: !1,
|
|
877
|
+
showPoint: !0,
|
|
878
|
+
backgroundColor: "rgba(6, 13, 31, 0.93)",
|
|
879
|
+
textStyle: {
|
|
880
|
+
color: "#c8ddf0",
|
|
881
|
+
fontStyle: "",
|
|
882
|
+
fontWeight: "",
|
|
883
|
+
fontSize: 12
|
|
884
|
+
},
|
|
885
|
+
padding: 10
|
|
886
|
+
},
|
|
887
|
+
fontFamily: "Rajdhani, sans-serif"
|
|
888
|
+
// 全局字体
|
|
889
|
+
}), K(J, "LIGHT", {
|
|
890
|
+
backgroundColor: "transparent",
|
|
891
|
+
// 画布背景色
|
|
892
|
+
pointStyle: {
|
|
893
|
+
itemStyle: {
|
|
894
|
+
shape: "circle",
|
|
895
|
+
radius: 3,
|
|
896
|
+
color: "#1d4ed8",
|
|
897
|
+
borderColor: "#ffffff",
|
|
898
|
+
borderWidth: 0.5
|
|
899
|
+
},
|
|
900
|
+
textStyle: {
|
|
901
|
+
show: !1,
|
|
902
|
+
fontSize: 11,
|
|
903
|
+
color: "#1d4ed8",
|
|
904
|
+
position: "top"
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
vertex: {
|
|
908
|
+
show: !0,
|
|
909
|
+
labelStyle: {
|
|
910
|
+
color: "#1d4ed8",
|
|
911
|
+
fontStyle: "normal",
|
|
912
|
+
fontWeight: "bold",
|
|
913
|
+
fontSize: 14
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
grid: {
|
|
917
|
+
show: !1,
|
|
918
|
+
lineStyle: {
|
|
919
|
+
lineType: "solid",
|
|
920
|
+
lineWidth: 1,
|
|
921
|
+
lineColor: "rgba(29, 78, 216, 0.15)"
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
zone: {
|
|
925
|
+
labelStyle: {
|
|
926
|
+
show: !0,
|
|
927
|
+
color: "#1e293b",
|
|
928
|
+
fontStyle: "bold",
|
|
929
|
+
fontSize: 11,
|
|
930
|
+
letterSpacing: 0,
|
|
931
|
+
textShadow: "rgba(255,255,255,0.8)",
|
|
932
|
+
padding: [4, 8],
|
|
933
|
+
borderRadius: 4,
|
|
934
|
+
backgroundColor: "transparent"
|
|
935
|
+
},
|
|
936
|
+
borderStyle: {
|
|
937
|
+
show: !0,
|
|
938
|
+
stroke: 1,
|
|
939
|
+
type: "solid",
|
|
940
|
+
color: "rgba(0, 0, 0, 0.25)"
|
|
941
|
+
}
|
|
942
|
+
},
|
|
943
|
+
tooltip: {
|
|
944
|
+
show: !0,
|
|
945
|
+
showDiagnostic: !1,
|
|
946
|
+
showPoint: !0,
|
|
947
|
+
backgroundColor: "rgba(240, 244, 248, 0.96)",
|
|
948
|
+
textStyle: {
|
|
949
|
+
color: "#334155",
|
|
950
|
+
fontStyle: "",
|
|
951
|
+
fontWeight: "",
|
|
952
|
+
fontSize: 12
|
|
953
|
+
},
|
|
954
|
+
padding: 10
|
|
955
|
+
},
|
|
956
|
+
fontFamily: "Rajdhani, sans-serif"
|
|
957
|
+
// 全局字体
|
|
958
|
+
});
|
|
959
|
+
let rt = J;
|
|
960
|
+
class pt {
|
|
961
|
+
/**
|
|
962
|
+
* 根据 ratio 向量诊断所在故障区域
|
|
963
|
+
* @param {number[]} ratio - 待诊断的 ratio 向量
|
|
964
|
+
* @param {object} zoneOpt - 区域配置对象
|
|
965
|
+
* @param {number} cx - 中心点 x
|
|
966
|
+
* @param {number} cy - 中心点 y
|
|
967
|
+
* @param {number} R - 半径
|
|
968
|
+
* @returns {object} - 匹配的区域对象
|
|
969
|
+
*/
|
|
970
|
+
static diagnose(t, e, o, s, i) {
|
|
971
|
+
const n = e && Array.isArray(e.data) ? e.data : Array.isArray(e) ? e : [];
|
|
972
|
+
if (!n.length) return null;
|
|
973
|
+
const [l, h] = W.polyToCanvas(t, o, s, i);
|
|
974
|
+
for (let a = 0; a < n.length - 1; a++) {
|
|
975
|
+
const r = n[a].poly.map((c) => W.polyToCanvas(c, o, s, i));
|
|
976
|
+
if (gt(l, h, r)) return n[a];
|
|
977
|
+
}
|
|
978
|
+
return n[n.length - 1];
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* 绘制所有故障区域
|
|
982
|
+
* @param {CanvasRenderingContext2D} ctx - Canvas 2D 上下文
|
|
983
|
+
* @param {object} zoneOpt - 区域配置对象
|
|
984
|
+
* @param {object} theme - 主题配置
|
|
985
|
+
* @param {number} cx - 中心点 x
|
|
986
|
+
* @param {number} cy - 中心点 y
|
|
987
|
+
* @param {number} R - 半径
|
|
988
|
+
* @param {number} S - 容器尺寸
|
|
989
|
+
*/
|
|
990
|
+
static draw(t, e, o, s, i, n, l) {
|
|
991
|
+
const h = e && Array.isArray(e.data) ? e.data : Array.isArray(e) ? e : [], a = e.labelStyle || {}, r = e.borderStyle || {};
|
|
992
|
+
for (const d of h) {
|
|
993
|
+
if (!d.poly || !d.poly.length) continue;
|
|
994
|
+
const f = d.poly.map((g) => W.polyToCanvas(g, s, i, n));
|
|
995
|
+
t.beginPath(), f.forEach(([g, p], m) => m === 0 ? t.moveTo(g, p) : t.lineTo(g, p)), t.closePath(), t.fillStyle = d.color || "transparent", t.fill();
|
|
996
|
+
}
|
|
997
|
+
for (const d of h) {
|
|
998
|
+
const f = { ...r, ...d.borderStyle || {} };
|
|
999
|
+
if (f.show === !1 || !d.poly || !d.poly.length) continue;
|
|
1000
|
+
const g = d.poly.map((p) => W.polyToCanvas(p, s, i, n));
|
|
1001
|
+
t.beginPath(), g.forEach(([p, m], y) => y === 0 ? t.moveTo(p, m) : t.lineTo(p, m)), t.closePath(), t.strokeStyle = f.color || d.borderColor || o.zoneBorderColor || "rgba(255,255,255,0.5)", t.lineWidth = (f.stroke ?? d.borderWidth ?? o.zoneBorderWidth ?? 1) * 1, f.type === "dashed" ? t.setLineDash([4, 4]) : f.type === "dotted" ? t.setLineDash([2, 4]) : t.setLineDash([]), t.stroke();
|
|
1002
|
+
}
|
|
1003
|
+
t.setLineDash([]);
|
|
1004
|
+
const c = o.fontFamily || "Rajdhani, sans-serif";
|
|
1005
|
+
for (const d of h) {
|
|
1006
|
+
const f = { ...a, ...d.labelStyle || {} };
|
|
1007
|
+
if (f.show === !1 || !d.name) continue;
|
|
1008
|
+
const g = d.poly ? d.poly.map((x) => W.polyToCanvas(x, s, i, n)) : [];
|
|
1009
|
+
let p, m;
|
|
1010
|
+
if (d.labelAt && Array.isArray(d.labelAt))
|
|
1011
|
+
[p, m] = W.polyToCanvas(d.labelAt, s, i, n);
|
|
1012
|
+
else if (g.length > 0)
|
|
1013
|
+
p = g.reduce((x, C) => x + C[0], 0) / g.length, m = g.reduce((x, C) => x + C[1], 0) / g.length;
|
|
1014
|
+
else
|
|
1015
|
+
continue;
|
|
1016
|
+
const y = Math.round((f.fontSize || o.zoneLabelSize || 12) * 1), u = f.fontStyle || "bold";
|
|
1017
|
+
t.font = `${u} ${y}px ${c}`, f.letterSpacing !== void 0 && "letterSpacing" in t && (t.letterSpacing = `${f.letterSpacing}px`), t.textAlign = "center", t.textBaseline = "middle";
|
|
1018
|
+
const b = d.name, w = t.measureText(b).width, S = y;
|
|
1019
|
+
if (f.backgroundColor && f.backgroundColor !== "transparent") {
|
|
1020
|
+
let x = 4, C = 4;
|
|
1021
|
+
Array.isArray(f.padding) ? (C = f.padding[0], x = f.padding[1] !== void 0 ? f.padding[1] : C) : typeof lstyle.padding == "number" && (x = C = lstyle.padding), x *= 1, C *= 1;
|
|
1022
|
+
const z = (f.borderRadius || 0) * 1, _ = w + x * 2, A = S + C * 2, Y = p - _ / 2, P = m - A / 2;
|
|
1023
|
+
t.fillStyle = f.backgroundColor, t.beginPath(), t.roundRect ? t.roundRect(Y, P, _, A, z) : t.rect(Y, P, _, A), t.fill();
|
|
1024
|
+
}
|
|
1025
|
+
f.textShadow && f.textShadow !== "none" ? (t.shadowColor = f.textShadow, t.shadowBlur = 4) : (t.shadowColor = "transparent", t.shadowBlur = 0), t.fillStyle = f.color || o.zoneLabelColor || "#fff", t.fillText(b, p, m), t.shadowBlur = 0, t.shadowColor = "transparent", "letterSpacing" in t && (t.letterSpacing = "0px");
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
class Xt {
|
|
1030
|
+
static draw(t, e, o, s, i, n, l, h) {
|
|
1031
|
+
var d;
|
|
1032
|
+
const r = o.fontFamily || "Rajdhani, sans-serif";
|
|
1033
|
+
if (h && h.show !== !1) {
|
|
1034
|
+
const f = h.lineStyle || {}, g = (f.lineWidth ?? 1) * 1, p = f.lineColor || "#e5e7eb", m = f.lineType || "solid";
|
|
1035
|
+
t.strokeStyle = p, t.lineWidth = g, m === "dashed" ? t.setLineDash([4, 4]) : m === "dotted" ? t.setLineDash([2, 4]) : t.setLineDash([]);
|
|
1036
|
+
for (const y of [0.2, 0.4, 0.6, 0.8, 1]) {
|
|
1037
|
+
t.beginPath();
|
|
1038
|
+
for (let u = 0; u < 5; u++) {
|
|
1039
|
+
const [b, w] = W.canvasVertex(u, s, i, n * y);
|
|
1040
|
+
u === 0 ? t.moveTo(b, w) : t.lineTo(b, w);
|
|
1041
|
+
}
|
|
1042
|
+
t.closePath(), t.stroke();
|
|
1043
|
+
}
|
|
1044
|
+
for (let y = 0; y < 5; y++) {
|
|
1045
|
+
const [u, b] = W.canvasVertex(y, s, i, n);
|
|
1046
|
+
t.beginPath(), t.moveTo(s, i), t.lineTo(u, b), t.stroke();
|
|
1047
|
+
}
|
|
1048
|
+
t.setLineDash([]);
|
|
1049
|
+
}
|
|
1050
|
+
if (!e || e.show === !1) return;
|
|
1051
|
+
const c = ((d = e.labelStyle) == null ? void 0 : d.distance) !== void 0 ? e.labelStyle.distance : 27.2;
|
|
1052
|
+
e.data.forEach((f, g) => {
|
|
1053
|
+
const p = f.labelStyle || {}, m = p.distance !== void 0 ? p.distance : c, y = typeof m == "string" && m.endsWith("%") ? parseFloat(m) / 100 * n : parseFloat(m) * 1, [u, b] = W.canvasVertex(g, s, i, n + y), w = p.fontStyle || "normal", S = p.fontWeight || "bold", x = Math.round((p.fontSize || o.gasLabelSize || 14) * 1);
|
|
1054
|
+
t.font = `${w} ${S} ${x}px ${r}`, t.fillStyle = p.color || o.gasLabelColor || "#fff", t.textAlign = "center", t.textBaseline = "middle", t.shadowColor = o.gasLabelShadow, t.shadowBlur = 7, t.fillText(f.name, u, b), t.shadowBlur = 0;
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
function wt(v) {
|
|
1059
|
+
const t = v.trim().toLowerCase();
|
|
1060
|
+
if (t.startsWith("#")) {
|
|
1061
|
+
let o = t.slice(1);
|
|
1062
|
+
o.length === 3 && (o = o[0] + o[0] + o[1] + o[1] + o[2] + o[2]);
|
|
1063
|
+
const s = parseInt(o.substring(0, 2), 16), i = parseInt(o.substring(2, 4), 16), n = parseInt(o.substring(4, 6), 16), l = o.length === 8 ? parseInt(o.substring(6, 8), 16) / 255 : 1;
|
|
1064
|
+
return [s, i, n, l];
|
|
1065
|
+
}
|
|
1066
|
+
const e = t.match(/rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\)/);
|
|
1067
|
+
return e ? [
|
|
1068
|
+
parseInt(e[1], 10),
|
|
1069
|
+
parseInt(e[2], 10),
|
|
1070
|
+
parseInt(e[3], 10),
|
|
1071
|
+
e[4] !== void 0 ? parseFloat(e[4]) : 1
|
|
1072
|
+
] : t === "transparent" ? [0, 0, 0, 0] : [255, 255, 255, 1];
|
|
1073
|
+
}
|
|
1074
|
+
function Ft(v, t, e) {
|
|
1075
|
+
const [o, s, i, n] = wt(v), [l, h, a, r] = wt(t), c = Math.round(o + (l - o) * e), d = Math.round(s + (h - s) * e), f = Math.round(i + (a - i) * e), g = n + (r - n) * e;
|
|
1076
|
+
return `rgba(${c},${d},${f},${g})`;
|
|
1077
|
+
}
|
|
1078
|
+
class Yt {
|
|
1079
|
+
/**
|
|
1080
|
+
* 绘制数据点及其辉光、标签
|
|
1081
|
+
*/
|
|
1082
|
+
static draw(t, e, o, s, i, n, l, h, a, r) {
|
|
1083
|
+
if (!e || !e.data || !Array.isArray(e.data)) return [];
|
|
1084
|
+
const c = 1, d = o.fontFamily || "Rajdhani, sans-serif", f = [], g = e.data;
|
|
1085
|
+
if (!g || g.length === 0) return [];
|
|
1086
|
+
const p = W.resolveData(g, h);
|
|
1087
|
+
if (!p) return [];
|
|
1088
|
+
const m = e.name, y = e.shape || "circle", u = e.color || "#409eff";
|
|
1089
|
+
let b = e.size !== void 0 ? e.size : 5;
|
|
1090
|
+
const [w, S] = W.polyToCanvas(p, s, i, n), x = pt.diagnose(p, a, s, i, n), [C, z] = W.ratioToDoc(p);
|
|
1091
|
+
r && r.series === e && (b *= 1.5);
|
|
1092
|
+
const A = e.itemStyle || {}, Y = A.borderColor || "#fff", P = A.borderWidth !== void 0 ? A.borderWidth : 1, k = A.opacity !== void 0 ? A.opacity : 1, M = e.textStyle || {}, L = M.show !== void 0 ? M.show : !0, Q = M.color || u, N = M.fontSize !== void 0 ? M.fontSize : 12, st = M.fontWeight || "normal", Z = M.position || "top", tt = M.offset || [0, 0], H = e.glowStyle || {}, dt = H.show !== void 0 ? H.show : !1, X = H.colors, I = H.blur !== void 0 ? H.blur : 15, R = H.startColor || u, U = H.endColor || "transparent", V = H.count !== void 0 ? H.count : 3, Mt = H.glowDistance !== void 0 ? H.glowDistance : 2;
|
|
1093
|
+
if (t.save(), t.globalAlpha = k, dt) {
|
|
1094
|
+
t.shadowColor = X ? X[0] : R, t.shadowBlur = I;
|
|
1095
|
+
let O = [], j = V;
|
|
1096
|
+
if (Array.isArray(X) && X.length > 0)
|
|
1097
|
+
O = X, j = O.length;
|
|
1098
|
+
else
|
|
1099
|
+
for (let B = 0; B < j; B++) {
|
|
1100
|
+
const q = j > 1 ? B / (j - 1) : 0;
|
|
1101
|
+
O.push(Ft(R, U, q));
|
|
1102
|
+
}
|
|
1103
|
+
for (let B = j - 1; B >= 0; B--) {
|
|
1104
|
+
const q = O[B];
|
|
1105
|
+
t.save(), t.fillStyle = q, t.beginPath(), nt(t, w, S, y, b, c, (B + 1) * Mt), t.fill(), t.restore();
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (t.beginPath(), nt(t, w, S, y, b, c, 0), y === "ring" ? (t.strokeStyle = u, t.lineWidth = P * c, t.stroke()) : (t.fillStyle = u, t.fill(), P > 0 && (t.strokeStyle = Y, t.lineWidth = P * c, t.stroke())), t.restore(), L && m) {
|
|
1109
|
+
t.save(), t.globalAlpha = k, t.font = `${st} ${Math.round(N * c)}px ${d}`, t.fillStyle = Q;
|
|
1110
|
+
let O = "center", j = "middle", B = w + tt[0] * c, q = S + tt[1] * c, et = 6 * c;
|
|
1111
|
+
y === "circle" || y === "ring" || y === "star" || y === "triangle" ? et += b * c : y === "square" && (et += b * c / 2), Z === "top" ? (O = "center", j = "bottom", q -= et) : Z === "bottom" ? (O = "center", j = "top", q += et) : Z === "left" ? (O = "right", j = "middle", B -= et) : Z === "right" ? (O = "left", j = "middle", B += et) : Z === "center" && (O = "center", j = "middle"), t.textAlign = O, t.textBaseline = j, t.fillText(m, B, q), t.restore();
|
|
1112
|
+
}
|
|
1113
|
+
return f.push({
|
|
1114
|
+
ratio: p,
|
|
1115
|
+
zone: x,
|
|
1116
|
+
docX: C,
|
|
1117
|
+
docY: z,
|
|
1118
|
+
rawData: g,
|
|
1119
|
+
name: m,
|
|
1120
|
+
series: e
|
|
1121
|
+
}), f;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
const G = class G {
|
|
1125
|
+
constructor(t, e = {}) {
|
|
1126
|
+
if (this._el = typeof t == "string" ? document.getElementById(t) : t, !this._el) throw new Error(`DuvalPentagon: container not found — "${t}"`);
|
|
1127
|
+
this._theme = rt.resolve(e.theme || G.DEFAULTS.theme), this._opt = E(E(E({}, G.DEFAULTS), this._theme), e), this._normalizeOptions(), this._bus = new At(), this._zoom = new ut(this._opt.zoom), this._canvas = document.createElement("canvas"), this._ctx = this._canvas.getContext("2d"), Object.assign(this._canvas.style, { display: "block", width: "100%", height: "100%" }), this._el.appendChild(this._canvas), this._tooltipRenderer = new yt(this._el, this._theme), this._hoverRatio = null, this._hoverZone = null, this._hoverPoint = null, this._bindEvents(), this._observeResize(), this._syncCanvas(), this._renderFull();
|
|
1128
|
+
}
|
|
1129
|
+
_normalizeOptions() {
|
|
1130
|
+
if (this._opt.showTooltip !== void 0 && (this._opt.tooltip || (this._opt.tooltip = {}), this._opt.tooltip.show = this._opt.showTooltip), this._opt.tooltipTrigger !== void 0 && (this._opt.tooltip || (this._opt.tooltip = {}), this._opt.tooltipTrigger === "point" ? (this._opt.tooltip.showDiagnostic = !1, this._opt.tooltip.showPoint = !0) : this._opt.tooltipTrigger === "zone" && (this._opt.tooltip.showDiagnostic = !0, this._opt.tooltip.showPoint = !0)), this._opt.vertex.data || (this._opt.vertex.data = ["H2", "C2H2", "C2H4", "CH4", "C2H6"]), !Array.isArray(this._opt.vertex.data) || this._opt.vertex.data.length !== 5)
|
|
1131
|
+
throw new Error("DuvalPentagon: vertex.data must be an array of exactly 5 elements.");
|
|
1132
|
+
const t = this._opt.vertex.labelStyle || {};
|
|
1133
|
+
this._opt.vertex.data = this._opt.vertex.data.map((e) => {
|
|
1134
|
+
if (typeof e == "string")
|
|
1135
|
+
return {
|
|
1136
|
+
name: e,
|
|
1137
|
+
labelStyle: { ...t }
|
|
1138
|
+
};
|
|
1139
|
+
if (e && typeof e == "object") {
|
|
1140
|
+
if (!e.name)
|
|
1141
|
+
throw new Error("DuvalPentagon: each vertex object in vertex.data must have a 'name' property.");
|
|
1142
|
+
return {
|
|
1143
|
+
name: e.name,
|
|
1144
|
+
labelStyle: E({ ...t }, e.labelStyle || {})
|
|
1145
|
+
};
|
|
1146
|
+
} else
|
|
1147
|
+
throw new Error("DuvalPentagon: vertex.data elements must be either strings or objects with a 'name' property.");
|
|
1148
|
+
}), this._opt.gasOrder = this._opt.vertex.data.map((e) => e.name), this._opt.zone ? Array.isArray(this._opt.zone) ? this._opt.zone = { data: this._opt.zone } : typeof this._opt.zone == "object" && !this._opt.zone.data && (this._opt.zone.data = G.DEFAULT_ZONES) : this._opt.zone = { data: G.DEFAULT_ZONES };
|
|
1149
|
+
}
|
|
1150
|
+
/* 公开 API ─ */
|
|
1151
|
+
/**
|
|
1152
|
+
* 深度合并配置并重绘图表
|
|
1153
|
+
* @param {object} opt - 需要合并的配置项
|
|
1154
|
+
* @returns {DuvalPentagon} - 当前实例,支持链式调用
|
|
1155
|
+
*/
|
|
1156
|
+
setOption(t) {
|
|
1157
|
+
return t.theme !== void 0 && (this._theme = rt.resolve(t.theme), this._tooltipRenderer.updateTheme(this._theme), this._opt = E(E(E({}, G.DEFAULTS), this._theme), this._opt)), E(this._opt, t), this._normalizeOptions(), this._syncCanvas(), this._render(), this;
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* 替换全部数据点并重绘
|
|
1161
|
+
* @param {object|object[]} series - 单个或多个数据点系列
|
|
1162
|
+
* @returns {DuvalPentagon} - 当前实例,支持链式调用
|
|
1163
|
+
*/
|
|
1164
|
+
setSeries(t) {
|
|
1165
|
+
return this._opt.series = Array.isArray(t) ? t : [t], this._renderFull(), this;
|
|
1166
|
+
}
|
|
1167
|
+
zoomReset() {
|
|
1168
|
+
return this._zoom.reset(), this._renderFull(), this;
|
|
1169
|
+
}
|
|
1170
|
+
on(t, e) {
|
|
1171
|
+
return this._bus.on(t, e), this;
|
|
1172
|
+
}
|
|
1173
|
+
off(t, e) {
|
|
1174
|
+
return this._bus.off(t, e), this;
|
|
1175
|
+
}
|
|
1176
|
+
dispose() {
|
|
1177
|
+
var e, o;
|
|
1178
|
+
(e = this._ro) == null || e.disconnect(), this._tooltipRenderer.dispose();
|
|
1179
|
+
const t = this._canvas;
|
|
1180
|
+
t && (t.removeEventListener("wheel", this._onWheel), t.removeEventListener("mousedown", this._onDown), t.removeEventListener("mousemove", this._onMove), t.removeEventListener("mouseleave", this._onLeave), t.removeEventListener("dblclick", this._onDbl), (o = t.parentNode) == null || o.removeChild(t)), window.removeEventListener("mouseup", this._onUp), this._bus.dispose();
|
|
1181
|
+
}
|
|
1182
|
+
/* 内部:事件绑定 */
|
|
1183
|
+
_bindEvents() {
|
|
1184
|
+
const t = this._canvas;
|
|
1185
|
+
this._onWheel = (e) => {
|
|
1186
|
+
e.preventDefault();
|
|
1187
|
+
const o = t.getBoundingClientRect(), s = (e.clientX - o.left) * (this._cssW / o.width), i = (e.clientY - o.top) * (this._cssH / o.height);
|
|
1188
|
+
this._zoom.onWheel(e.deltaY, s, i), this._bus.emit("zoom", this._zoom.state), this._renderFull();
|
|
1189
|
+
}, this._onDown = (e) => {
|
|
1190
|
+
e.button === 0 && (this._zoom.startDrag(e.clientX, e.clientY), t.style.cursor = "grabbing");
|
|
1191
|
+
}, this._onMove = (e) => {
|
|
1192
|
+
const o = t.getBoundingClientRect(), s = this._cssW / o.width;
|
|
1193
|
+
if (this._zoom.moveDrag(e.clientX, e.clientY, s)) {
|
|
1194
|
+
this._renderFull();
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
const i = ((e.clientX - o.left) * s - this._zoom.panX) / this._zoom.zoom, n = ((e.clientY - o.top) * s - this._zoom.panY) / this._zoom.zoom, { cx: l, cy: h, R: a, S: r } = this._dims();
|
|
1198
|
+
let c = null, d = 1 / 0;
|
|
1199
|
+
if (this._drawnResults)
|
|
1200
|
+
for (const p of this._drawnResults) {
|
|
1201
|
+
const [m, y] = W.polyToCanvas(p.ratio, l, h, a), u = p.series.size || Math.max(5, r * 0.013), b = Math.hypot(m - i, y - n);
|
|
1202
|
+
b < Math.max(u * 1.5, 12) && b < d && (d = b, c = p);
|
|
1203
|
+
}
|
|
1204
|
+
const f = this._opt.tooltip || {}, g = f.show !== !1;
|
|
1205
|
+
if (c && g && f.showPoint !== !1)
|
|
1206
|
+
t.style.cursor = "pointer", this._hoverPoint = c, this._hoverRatio = c.ratio, this._hoverZone = c.zone, this._bus.emit("hover", {
|
|
1207
|
+
ratio: c.ratio,
|
|
1208
|
+
zone: c.zone,
|
|
1209
|
+
docX: c.docX,
|
|
1210
|
+
docY: c.docY,
|
|
1211
|
+
point: c.series
|
|
1212
|
+
});
|
|
1213
|
+
else {
|
|
1214
|
+
t.style.cursor = "default", this._hoverPoint = null;
|
|
1215
|
+
const p = W.canvasToRatio(i, n, l, h, a);
|
|
1216
|
+
if (p && g && f.showDiagnostic !== !1) {
|
|
1217
|
+
this._hoverRatio = p, this._hoverZone = pt.diagnose(p, this._opt.zone, l, h, a);
|
|
1218
|
+
const [m, y] = W.ratioToDoc(p);
|
|
1219
|
+
this._bus.emit("hover", { ratio: p, zone: this._hoverZone, docX: m, docY: y });
|
|
1220
|
+
} else
|
|
1221
|
+
this._hoverRatio = null, this._hoverZone = null, this._bus.emit("hover", null);
|
|
1222
|
+
}
|
|
1223
|
+
this._renderFull();
|
|
1224
|
+
}, this._onUp = () => {
|
|
1225
|
+
this._zoom.endDrag() && (t.style.cursor = this._hoverPoint ? "pointer" : "default", this._bus.emit("zoom", this._zoom.state));
|
|
1226
|
+
}, this._onLeave = () => {
|
|
1227
|
+
this._hoverRatio = null, this._hoverZone = null, this._hoverPoint = null, this._bus.emit("hover", null), this._renderFull();
|
|
1228
|
+
}, this._onDbl = () => {
|
|
1229
|
+
this.zoomReset(), this._bus.emit("zoom", this._zoom.state);
|
|
1230
|
+
}, t.addEventListener("wheel", this._onWheel, { passive: !1 }), t.addEventListener("mousedown", this._onDown), t.addEventListener("mousemove", this._onMove), t.addEventListener("mouseleave", this._onLeave), t.addEventListener("dblclick", this._onDbl), window.addEventListener("mouseup", this._onUp), t.style.cursor = "default";
|
|
1231
|
+
}
|
|
1232
|
+
_observeResize() {
|
|
1233
|
+
typeof ResizeObserver > "u" || (this._ro = new ResizeObserver(() => {
|
|
1234
|
+
this._syncCanvas(), this._renderFull();
|
|
1235
|
+
}), this._ro.observe(this._el));
|
|
1236
|
+
}
|
|
1237
|
+
_syncCanvas() {
|
|
1238
|
+
const t = window.devicePixelRatio || 1, e = this._el.clientWidth || 300, o = this._el.clientHeight || 300;
|
|
1239
|
+
this._cssW = e, this._cssH = o, this._canvas.width = Math.round(e * t), this._canvas.height = Math.round(o * t);
|
|
1240
|
+
}
|
|
1241
|
+
_dims() {
|
|
1242
|
+
var a;
|
|
1243
|
+
const t = this._cssW, e = this._cssH, o = ((a = this._opt) == null ? void 0 : a.grid) || {};
|
|
1244
|
+
if (!(o.left !== void 0 || o.right !== void 0 || o.top !== void 0 || o.bottom !== void 0)) {
|
|
1245
|
+
const r = Math.min(t, e);
|
|
1246
|
+
return { W: t, H: e, S: r, cx: t / 2, cy: e / 2, R: r * 0.385 };
|
|
1247
|
+
}
|
|
1248
|
+
const i = ct(t, e, o, 0), l = Math.min(i.availW, i.availH) / 2, h = l / 0.385;
|
|
1249
|
+
return { W: t, H: e, S: h, cx: i.cx, cy: i.cy, R: l };
|
|
1250
|
+
}
|
|
1251
|
+
_render() {
|
|
1252
|
+
this._hoverRatio = null, this._hoverZone = null, this._hoverPoint = null, this._renderFull();
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* 核心渲染方法,绘制所有可见元素
|
|
1256
|
+
* @private
|
|
1257
|
+
*/
|
|
1258
|
+
_renderFull() {
|
|
1259
|
+
var y;
|
|
1260
|
+
const t = this._ctx;
|
|
1261
|
+
if (!t) return;
|
|
1262
|
+
const e = window.devicePixelRatio || 1, { W: o, H: s, S: i, cx: n, cy: l, R: h } = this._dims(), a = this._theme, r = this._opt.gasOrder, c = this._opt.zone, d = this._opt.vertex;
|
|
1263
|
+
t.setTransform(e, 0, 0, e, 0, 0), t.clearRect(0, 0, o, s);
|
|
1264
|
+
const f = this._opt.backgroundColor || a.backgroundColor || "transparent";
|
|
1265
|
+
f !== "transparent" && (t.fillStyle = f, t.fillRect(0, 0, o, s)), t.save(), this._zoom.applyTransform(t), pt.draw(t, c, a, n, l, h, i), Xt.draw(t, d, a, n, l, h, i, this._opt.grid);
|
|
1266
|
+
const g = [];
|
|
1267
|
+
for (const u of this._opt.series || []) {
|
|
1268
|
+
const b = Yt.draw(t, u, a, n, l, h, i, r, c, this._hoverPoint);
|
|
1269
|
+
Array.isArray(b) && b.forEach((w) => {
|
|
1270
|
+
g.push({ series: u, ...w });
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
this._drawnResults = g;
|
|
1274
|
+
const p = this._opt.tooltip || {};
|
|
1275
|
+
if (p.show !== !1 && this._hoverRatio) {
|
|
1276
|
+
let u = 0, b = 0;
|
|
1277
|
+
(this._hoverPoint || p.showDiagnostic !== !1) && ([u, b] = W.polyToCanvas(this._hoverRatio, n, l, h));
|
|
1278
|
+
const w = u * this._zoom.zoom + this._zoom.panX, S = b * this._zoom.zoom + this._zoom.panY;
|
|
1279
|
+
let x = "";
|
|
1280
|
+
typeof p.formatter == "function" ? x = p.formatter({
|
|
1281
|
+
ratio: this._hoverRatio,
|
|
1282
|
+
zone: this._hoverZone,
|
|
1283
|
+
point: (y = this._hoverPoint) == null ? void 0 : y.series,
|
|
1284
|
+
gasOrder: r
|
|
1285
|
+
}) : x = this._defaultTooltipHTML(this._hoverRatio, this._hoverZone, a, r, this._hoverPoint), this._tooltipRenderer.show(x, w, S, p, a, this._hoverZone);
|
|
1286
|
+
} else
|
|
1287
|
+
this._tooltipRenderer.hide();
|
|
1288
|
+
t.restore(), g.length > 0 && this._bus.emit("diagnose", g);
|
|
1289
|
+
}
|
|
1290
|
+
_defaultTooltipHTML(t, e, o, s, i) {
|
|
1291
|
+
var d;
|
|
1292
|
+
const n = ((d = this._opt.vertex) == null ? void 0 : d.data) || [], l = i == null ? void 0 : i.rawData;
|
|
1293
|
+
let h = "";
|
|
1294
|
+
i && i.name && (h = `<div style="font-size:14px; font-weight:bold; margin-bottom:4px; color:#fff;">${i.name}</div>`);
|
|
1295
|
+
const a = e ? `${e.name} ${e.title}` : "Outside", r = e ? e.color : "#fff";
|
|
1296
|
+
let c = `
|
|
1297
|
+
${h}
|
|
1298
|
+
<div style="color:${r}; font-size:13px; font-weight:bold; padding-bottom:8px; margin-bottom:8px; border-bottom:1px solid rgba(255,255,255,0.07);">
|
|
1299
|
+
${a}
|
|
1300
|
+
</div>
|
|
1301
|
+
`;
|
|
1302
|
+
return s.forEach((f, g) => {
|
|
1303
|
+
var y, u, b, w;
|
|
1304
|
+
const p = ((u = (y = n[g]) == null ? void 0 : y.labelStyle) == null ? void 0 : u.color) || ((w = (b = o.vertex) == null ? void 0 : b.labelStyle) == null ? void 0 : w.color) || "#fff";
|
|
1305
|
+
let m = "-";
|
|
1306
|
+
if (l)
|
|
1307
|
+
if (Array.isArray(l)) {
|
|
1308
|
+
const S = l[g];
|
|
1309
|
+
if (S && typeof S == "object") {
|
|
1310
|
+
const x = l.find((C) => C.name === f);
|
|
1311
|
+
m = x && x.value !== void 0 ? x.value : "-";
|
|
1312
|
+
} else
|
|
1313
|
+
m = l[g] !== void 0 ? l[g] : "-";
|
|
1314
|
+
} else typeof l == "object" && (m = l[f] !== void 0 ? l[f] : "-");
|
|
1315
|
+
c += `
|
|
1316
|
+
<div style="display:flex; align-items:center; margin-bottom:6px; font-size:13px;">
|
|
1317
|
+
<span style="display:inline-block; width:8px; height:8px; border-radius:50%; background-color:${p}; margin-right:8px;"></span>
|
|
1318
|
+
<span style="color:#cbd5e1; flex-grow:1;">${f}</span>
|
|
1319
|
+
<span style="color:#fff; font-weight:bold; margin-left:20px;">${m}</span>
|
|
1320
|
+
</div>
|
|
1321
|
+
`;
|
|
1322
|
+
}), c;
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
K(G, "DEFAULT_ZONES", it), K(G, "DEFAULTS", {
|
|
1326
|
+
theme: "dark",
|
|
1327
|
+
vertex: {
|
|
1328
|
+
show: !0,
|
|
1329
|
+
data: ["H2", "C2H2", "C2H4", "CH4", "C2H6"]
|
|
1330
|
+
},
|
|
1331
|
+
zone: null,
|
|
1332
|
+
series: [],
|
|
1333
|
+
grid: {
|
|
1334
|
+
show: !1
|
|
1335
|
+
},
|
|
1336
|
+
zoom: { min: 0.3, max: 6, step: 0.12 },
|
|
1337
|
+
tooltip: {
|
|
1338
|
+
show: !0,
|
|
1339
|
+
showDiagnostic: !1,
|
|
1340
|
+
showPoint: !0
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
let vt = G;
|
|
1344
|
+
const at = Object.freeze({
|
|
1345
|
+
zone: { data: Pt },
|
|
1346
|
+
backgroundColor: "#050d1a",
|
|
1347
|
+
triFill: "rgba(8,20,50,0)",
|
|
1348
|
+
grid: {
|
|
1349
|
+
show: !0,
|
|
1350
|
+
steps: 10,
|
|
1351
|
+
majorEvery: 2,
|
|
1352
|
+
lineStyle: {
|
|
1353
|
+
color: "rgba(255,255,255,0)",
|
|
1354
|
+
width: 1,
|
|
1355
|
+
type: "solid"
|
|
1356
|
+
},
|
|
1357
|
+
majorLineStyle: {
|
|
1358
|
+
color: "rgba(255,255,255,0)",
|
|
1359
|
+
width: 1,
|
|
1360
|
+
type: "solid"
|
|
1361
|
+
},
|
|
1362
|
+
labelStyle: {
|
|
1363
|
+
color: "rgba(200,220,255,0.8)",
|
|
1364
|
+
fontSize: 12,
|
|
1365
|
+
fontWeight: "normal"
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1368
|
+
side: {
|
|
1369
|
+
show: !0,
|
|
1370
|
+
data: ["CH4", "C2H2", "C2H4"],
|
|
1371
|
+
labelStyle: {
|
|
1372
|
+
color: "#00e5ff",
|
|
1373
|
+
fontSize: 14,
|
|
1374
|
+
fontWeight: "bold",
|
|
1375
|
+
offset: 40
|
|
1376
|
+
},
|
|
1377
|
+
tickStyle: {
|
|
1378
|
+
show: !0,
|
|
1379
|
+
color: "rgba(200,220,255)",
|
|
1380
|
+
fontSize: 14,
|
|
1381
|
+
offset: 15,
|
|
1382
|
+
length: 6,
|
|
1383
|
+
lineColor: "rgba(0,0,0,0.2)"
|
|
1384
|
+
},
|
|
1385
|
+
lineStyle: {
|
|
1386
|
+
color: "rgba(180,210,255,0.1)",
|
|
1387
|
+
width: 1.5,
|
|
1388
|
+
type: "solid"
|
|
1389
|
+
}
|
|
1390
|
+
},
|
|
1391
|
+
tooltip: {
|
|
1392
|
+
show: !0,
|
|
1393
|
+
backgroundColor: "rgba(5,13,26,0.95)",
|
|
1394
|
+
borderColor: "#00e5ff",
|
|
1395
|
+
padding: 10,
|
|
1396
|
+
textStyle: {
|
|
1397
|
+
color: "#c8d8e8",
|
|
1398
|
+
fontSize: 12
|
|
1399
|
+
}
|
|
1400
|
+
},
|
|
1401
|
+
pointStyle: {
|
|
1402
|
+
itemStyle: {
|
|
1403
|
+
shape: "circle",
|
|
1404
|
+
radius: 6,
|
|
1405
|
+
color: "#ff4081",
|
|
1406
|
+
borderWidth: 1.5,
|
|
1407
|
+
borderColor: "rgba(255,255,255,0.7)"
|
|
1408
|
+
},
|
|
1409
|
+
textStyle: {
|
|
1410
|
+
show: !0,
|
|
1411
|
+
fontSize: 12,
|
|
1412
|
+
color: "#00e5ff",
|
|
1413
|
+
position: "top"
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
class D {
|
|
1418
|
+
/** 计算等边三角形在给定视口下的顶点 */
|
|
1419
|
+
static computeTri(t) {
|
|
1420
|
+
const { availW: e, availH: o, cx: s, cy: i } = t, n = Math.max(0, Math.min(e, o / Math.sin(Math.PI / 3))), l = n * Math.sin(Math.PI / 3), h = s - n / 2, a = i - l / 2;
|
|
1421
|
+
return {
|
|
1422
|
+
top: { x: s, y: a },
|
|
1423
|
+
left: { x: h, y: a + l },
|
|
1424
|
+
right: { x: h + n, y: a + l }
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
/** 三元坐标(a,b,c)转笛卡尔坐标(x,y) */
|
|
1428
|
+
static t2c(t, e, o, s) {
|
|
1429
|
+
const { top: i, left: n, right: l } = s;
|
|
1430
|
+
return {
|
|
1431
|
+
x: t * i.x + e * n.x + o * l.x,
|
|
1432
|
+
y: t * i.y + e * n.y + o * l.y
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
/** 笛卡尔坐标(x,y)转三元坐标(a,b,c) */
|
|
1436
|
+
static c2t(t, e, o) {
|
|
1437
|
+
const { top: s, left: i, right: n } = o, l = (i.y - n.y) * (s.x - n.x) + (n.x - i.x) * (s.y - n.y), h = ((i.y - n.y) * (t - n.x) + (n.x - i.x) * (e - n.y)) / l, a = ((n.y - s.y) * (t - n.x) + (s.x - n.x) * (e - n.y)) / l;
|
|
1438
|
+
return { a: h, b: a, c: 1 - h - a };
|
|
1439
|
+
}
|
|
1440
|
+
/** 计算多边形像素面积 */
|
|
1441
|
+
static polyArea(t) {
|
|
1442
|
+
let e = 0;
|
|
1443
|
+
for (let o = 0, s = t.length; o < s; o++) {
|
|
1444
|
+
const i = (o + 1) % s;
|
|
1445
|
+
e += t[o].x * t[i].y - t[i].x * t[o].y;
|
|
1446
|
+
}
|
|
1447
|
+
return Math.abs(e) / 2;
|
|
1448
|
+
}
|
|
1449
|
+
/** 计算多边形质心 */
|
|
1450
|
+
static polyCentroid(t) {
|
|
1451
|
+
const e = t.length;
|
|
1452
|
+
let o = 0, s = 0, i = 0;
|
|
1453
|
+
for (let n = 0; n < e; n++) {
|
|
1454
|
+
const l = (n + 1) % e, h = t[n].x * t[l].y - t[l].x * t[n].y;
|
|
1455
|
+
o += h, s += (t[n].x + t[l].x) * h, i += (t[n].y + t[l].y) * h;
|
|
1456
|
+
}
|
|
1457
|
+
return o /= 2, { x: s / (6 * o), y: i / (6 * o) };
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* 解析并归一化数据点 (支持对象数组或原始数值数组)
|
|
1461
|
+
* 返回 [a, b, c] 比例数组,保证 a+b+c = 1
|
|
1462
|
+
*/
|
|
1463
|
+
static resolveData(t, e) {
|
|
1464
|
+
let o = null;
|
|
1465
|
+
if (Array.isArray(t) ? t.length >= 3 && (typeof t[0] == "object" && t[0] !== null ? e ? o = e.map((h) => {
|
|
1466
|
+
const a = t.find((r) => r && r.name === h);
|
|
1467
|
+
return a ? a.value : 0;
|
|
1468
|
+
}) : o = [t[0].value, t[1].value, t[2].value] : o = [t[0], t[1], t[2]]) : t && t.value && Array.isArray(t.value) && t.value.length >= 3 && (o = [t.value[0], t.value[1], t.value[2]]), !o || o.some((h) => typeof h != "number" || isNaN(h))) return null;
|
|
1469
|
+
const [s, i, n] = o.map((h) => Math.max(0, h || 0)), l = s + i + n;
|
|
1470
|
+
return l <= 1e-9 ? [0.3333, 0.3333, 0.3333] : [s / l, i / l, n / l];
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
class Rt {
|
|
1474
|
+
constructor(t) {
|
|
1475
|
+
this.ctx = t;
|
|
1476
|
+
}
|
|
1477
|
+
/** 全量渲染入口 */
|
|
1478
|
+
render(t) {
|
|
1479
|
+
const e = this.ctx, { W: o, H: s, dpr: i, scale: n, tx: l, ty: h, option: a, tri: r, zones: c, hoveredPoint: d } = t;
|
|
1480
|
+
e.setTransform(1, 0, 0, 1, 0, 0), e.clearRect(0, 0, o * i, s * i), e.fillStyle = a.backgroundColor, e.fillRect(0, 0, o * i, s * i), e.setTransform(n * i, 0, 0, n * i, l * i, h * i), this.drawTriFill(r), this.drawZones(c, r, a), this.drawGrid(a, r), this.drawBorder(a, r), this.drawSideLabels(a, r), this.drawSeries(a, r, d), e.setTransform(1, 0, 0, 1, 0, 0);
|
|
1481
|
+
}
|
|
1482
|
+
drawTriFill(t) {
|
|
1483
|
+
const e = this.ctx, { top: o, left: s, right: i } = t;
|
|
1484
|
+
e.beginPath(), e.moveTo(o.x, o.y), e.lineTo(s.x, s.y), e.lineTo(i.x, i.y), e.closePath(), e.fillStyle = at.triFill, e.fill();
|
|
1485
|
+
}
|
|
1486
|
+
drawZones(t, e, o) {
|
|
1487
|
+
var r, c, d, f;
|
|
1488
|
+
const s = this.ctx, i = this.ctx.getTransform().a / (window.devicePixelRatio || 1);
|
|
1489
|
+
t.forEach((g) => {
|
|
1490
|
+
!g.points || !g.points.length || (s.beginPath(), g.points.forEach(([p, m, y], u) => {
|
|
1491
|
+
const b = D.t2c(p, m, y, e);
|
|
1492
|
+
u === 0 ? s.moveTo(b.x, b.y) : s.lineTo(b.x, b.y);
|
|
1493
|
+
}), s.closePath(), s.fillStyle = g.color || "transparent", s.fill());
|
|
1494
|
+
});
|
|
1495
|
+
const n = ((r = o.zone) == null ? void 0 : r.borderStyle) || {};
|
|
1496
|
+
if (t.forEach((g) => {
|
|
1497
|
+
const p = E(E({}, n), g.borderStyle || {});
|
|
1498
|
+
p.show === !1 || !g.points || !g.points.length || (s.beginPath(), g.points.forEach(([m, y, u], b) => {
|
|
1499
|
+
const w = D.t2c(m, y, u, e);
|
|
1500
|
+
b === 0 ? s.moveTo(w.x, w.y) : s.lineTo(w.x, w.y);
|
|
1501
|
+
}), s.closePath(), s.strokeStyle = p.color || g.borderColor || "rgba(255,255,255,0.2)", s.lineWidth = (p.width ?? p.stroke ?? g.borderWidth ?? 1) * i, p.type === "dashed" ? s.setLineDash([4, 4]) : p.type === "dotted" ? s.setLineDash([2, 4]) : s.setLineDash([]), s.stroke());
|
|
1502
|
+
}), s.setLineDash([]), !(o.showZoneLabel !== !1)) return;
|
|
1503
|
+
const h = ((d = (c = o.grid) == null ? void 0 : c.labelStyle) == null ? void 0 : d.fontFamily) || "Microsoft YaHei UI, monospace", a = ((f = o.zone) == null ? void 0 : f.labelStyle) || {};
|
|
1504
|
+
t.forEach((g) => {
|
|
1505
|
+
const p = E(E({}, a), g.labelStyle || {});
|
|
1506
|
+
if (p.show === !1 || !g.id) return;
|
|
1507
|
+
const m = g.labelPos;
|
|
1508
|
+
if (!m) return;
|
|
1509
|
+
const y = D.t2c(m.a, m.b, m.c, e), u = Math.round((p.fontSize || 11) * i), b = p.fontStyle || "bold";
|
|
1510
|
+
s.font = `${b} ${u}px ${h}`, s.textAlign = "center", s.textBaseline = "middle";
|
|
1511
|
+
const w = g.id, S = s.measureText(w).width, x = u;
|
|
1512
|
+
let C = y.x, z = y.y;
|
|
1513
|
+
if (p.backgroundColor && p.backgroundColor !== "transparent") {
|
|
1514
|
+
let _ = 4, A = 4;
|
|
1515
|
+
Array.isArray(p.padding) ? (A = p.padding[0], _ = p.padding[1] !== void 0 ? p.padding[1] : A) : typeof p.padding == "number" && (_ = A = p.padding), _ *= i, A *= i;
|
|
1516
|
+
const Y = (p.borderRadius || 3) * i, P = S + _ * 2, k = x + A * 2, M = C - P / 2, L = z - k / 2;
|
|
1517
|
+
s.fillStyle = p.backgroundColor, s.beginPath(), s.roundRect ? s.roundRect(M, L, P, k, Y) : s.rect(M, L, P, k), s.fill();
|
|
1518
|
+
}
|
|
1519
|
+
p.textShadow && p.textShadow !== "none" ? (s.shadowColor = p.textShadow, s.shadowBlur = 4) : (s.shadowColor = "transparent", s.shadowBlur = 0), s.fillStyle = p.color || g.labelColor || "#fff", s.fillText(w, C, z), s.shadowBlur = 0, s.shadowColor = "transparent";
|
|
1520
|
+
}), s.textBaseline = "alphabetic";
|
|
1521
|
+
}
|
|
1522
|
+
drawBadge(t, e, o, s, i) {
|
|
1523
|
+
const n = this.ctx, { badgePadding: l, badgeHeight: h, badgeRadius: a } = at.ZONE_VIS, r = o + l * 2, c = t - r / 2, d = e - h / 2;
|
|
1524
|
+
n.beginPath(), n.roundRect ? n.roundRect(c, d, r, h, a) : n.rect(c, d, r, h), n.fillStyle = i, n.fill();
|
|
1525
|
+
}
|
|
1526
|
+
drawArrow(t, e, o, s, i) {
|
|
1527
|
+
const n = this.ctx, l = o - t, h = s - e, a = Math.hypot(l, h);
|
|
1528
|
+
if (a < 1) return;
|
|
1529
|
+
const r = l / a, c = h / a, d = 7;
|
|
1530
|
+
n.beginPath(), n.moveTo(o, s), n.lineTo(o - r * d + c * d * 0.5, s - c * d - r * d * 0.5), n.lineTo(o - r * d - c * d * 0.5, s - c * d + r * d * 0.5), n.closePath(), n.fillStyle = i, n.fill();
|
|
1531
|
+
}
|
|
1532
|
+
drawGrid(t, e) {
|
|
1533
|
+
if (!t.grid || !t.grid.show) return;
|
|
1534
|
+
const o = this.ctx, s = t.grid, i = s.steps || 10, n = s.majorEvery || 2, l = s.lineStyle || { color: "rgba(255,255,255,0.1)", width: 1, type: "solid" }, h = s.majorLineStyle || { color: "rgba(255,255,255,0.2)", width: 1, type: "solid" }, a = this.ctx.getTransform().a / (window.devicePixelRatio || 1), r = (p, m, y) => {
|
|
1535
|
+
let u = m.x - p.x, b = m.y - p.y;
|
|
1536
|
+
const w = Math.hypot(u, b);
|
|
1537
|
+
u /= w, b /= w;
|
|
1538
|
+
const S = { x: -b, y: u }, x = { x: b, y: -u };
|
|
1539
|
+
return (y.x - p.x) * S.x + (y.y - p.y) * S.y > 0 ? S : x;
|
|
1540
|
+
}, { top: c, left: d, right: f } = e, g = {
|
|
1541
|
+
bottom: r(d, f, c),
|
|
1542
|
+
left: r(d, c, f),
|
|
1543
|
+
right: r(f, c, d)
|
|
1544
|
+
};
|
|
1545
|
+
for (let p = 1; p < i; p++) {
|
|
1546
|
+
const m = p / i, y = p % n === 0, u = y ? h : l;
|
|
1547
|
+
o.strokeStyle = u.color || "transparent", o.lineWidth = (u.width ?? 1) * a, u.type === "dashed" ? o.setLineDash([4, 4]) : u.type === "dotted" ? o.setLineDash([2, 4]) : o.setLineDash([]);
|
|
1548
|
+
const b = (w, S) => {
|
|
1549
|
+
o.beginPath(), o.moveTo(w.x, w.y), o.lineTo(S.x, S.y), o.stroke();
|
|
1550
|
+
};
|
|
1551
|
+
b(D.t2c(m, 1 - m, 0, e), D.t2c(m, 0, 1 - m, e)), b(D.t2c(1 - m, m, 0, e), D.t2c(0, m, 1 - m, e)), b(D.t2c(1 - m, 0, m, e), D.t2c(0, 1 - m, m, e)), this.drawSideTick(p, i, m, t, e, y, g);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
drawSideTick(t, e, o, s, i, n, l) {
|
|
1555
|
+
const a = (s.side || {}).tickStyle || {};
|
|
1556
|
+
if (a.show === !1) return;
|
|
1557
|
+
const r = this.ctx, c = Math.round(t * 100 / e);
|
|
1558
|
+
r.textAlign = "center", r.textBaseline = "middle";
|
|
1559
|
+
const d = a.length || 6, f = a.lineColor || "rgba(0,0,0,0.2)", g = a.color || "rgba(200,220,255)", p = a.offset !== void 0 ? a.offset : 15, m = a.fontFamily || "Microsoft YaHei UI, sans-serif", y = a.fontSize || 14, b = `${a.fontWeight || "normal"} ${y}px ${m}`, w = (z, _, A) => {
|
|
1560
|
+
r.beginPath(), r.moveTo(z.x, z.y), r.lineTo(z.x + _ * d, z.y + A * d), r.strokeStyle = f, r.lineWidth = 1.5, r.stroke();
|
|
1561
|
+
}, S = D.t2c(o, 1 - o, 0, i);
|
|
1562
|
+
w(S, l.left.x, l.left.y), n && (r.fillStyle = g, r.font = b, r.save(), r.translate(S.x - p, S.y - 8), r.rotate(-Math.PI / 3), r.fillText(c + "", 0, 0), r.restore());
|
|
1563
|
+
const x = D.t2c(1 - o, 0, o, i);
|
|
1564
|
+
w(x, l.right.x, l.right.y), n && (r.fillStyle = g, r.font = b, r.save(), r.translate(x.x + p, x.y - 8), r.rotate(Math.PI / 3), r.fillText(c + "", 0, 0), r.restore());
|
|
1565
|
+
const C = D.t2c(0, o, 1 - o, i);
|
|
1566
|
+
w(C, l.bottom.x, l.bottom.y), n && (r.fillStyle = g, r.font = b, r.fillText(c + "", C.x, C.y + p));
|
|
1567
|
+
}
|
|
1568
|
+
drawBorder(t, e) {
|
|
1569
|
+
const o = this.ctx, { top: s, left: i, right: n } = e, h = (t.side || {}).lineStyle || { color: "rgba(180,210,255,0.1)", width: 1.5, type: "solid" };
|
|
1570
|
+
[
|
|
1571
|
+
{ f: s, t: i },
|
|
1572
|
+
{ f: s, t: n },
|
|
1573
|
+
{ f: i, t: n }
|
|
1574
|
+
].forEach(({ f: r, t: c }) => {
|
|
1575
|
+
o.beginPath(), o.moveTo(r.x, r.y), o.lineTo(c.x, c.y), o.strokeStyle = h.color || "transparent", o.lineWidth = h.width || 1, h.type === "dashed" ? o.setLineDash([4, 4]) : h.type === "dotted" ? o.setLineDash([2, 4]) : o.setLineDash([]), o.stroke();
|
|
1576
|
+
}), o.setLineDash([]);
|
|
1577
|
+
}
|
|
1578
|
+
drawSideLabels(t, e) {
|
|
1579
|
+
var C;
|
|
1580
|
+
if (((C = t.side) == null ? void 0 : C.show) === !1) return;
|
|
1581
|
+
const o = this.ctx, { top: s, left: i, right: n } = e, l = t.side || {}, h = l.data || ["A", "B", "C"], a = l.labelStyle || {}, r = a.fontFamily || "Microsoft YaHei UI, sans-serif", c = a.fontSize || 14, d = a.fontWeight || "bold", f = a.color || "#00e5ff", g = a.offset !== void 0 ? a.offset : 40;
|
|
1582
|
+
o.textBaseline = "middle", o.textAlign = "center", o.fillStyle = f, o.font = `${d} ${c}px ${r}`;
|
|
1583
|
+
const p = { x: (s.x + i.x) / 2, y: (s.y + i.y) / 2 }, m = i.x - s.x, y = i.y - s.y, u = Math.hypot(m, y);
|
|
1584
|
+
o.save(), o.translate(p.x + -y / u * g, p.y + m / u * g), o.rotate(Math.atan2(y, m) + Math.PI), o.fillText((h[1] || "B") + " (%)", 0, 0), o.restore();
|
|
1585
|
+
const b = { x: (s.x + n.x) / 2, y: (s.y + n.y) / 2 }, w = n.x - s.x, S = n.y - s.y, x = Math.hypot(w, S);
|
|
1586
|
+
o.save(), o.translate(b.x + S / x * g, b.y + -w / x * g), o.rotate(Math.atan2(S, w)), o.fillText((h[2] || "C") + " (%)", 0, 0), o.restore(), o.save(), o.translate((i.x + n.x) / 2, (i.y + n.y) / 2 + g), o.fillText((h[0] || "A") + " (%)", 0, 0), o.restore();
|
|
1587
|
+
}
|
|
1588
|
+
drawSeries(t, e, o) {
|
|
1589
|
+
var h;
|
|
1590
|
+
const s = this.ctx, i = this.ctx.getTransform().a / (window.devicePixelRatio || 1), n = "Microsoft YaHei UI, sans-serif", l = (h = t.side) == null ? void 0 : h.data;
|
|
1591
|
+
(t.series || []).forEach((a) => {
|
|
1592
|
+
const r = a.data;
|
|
1593
|
+
if (!r || r.length === 0) return;
|
|
1594
|
+
const c = a.name, d = D.resolveData(r, l);
|
|
1595
|
+
if (!d) return;
|
|
1596
|
+
const f = D.t2c(d[0], d[1], d[2], e), g = f.x, p = f.y, m = o && o.series === a, y = a.shape || "circle", u = a.color || "#409eff";
|
|
1597
|
+
let b = a.size !== void 0 ? a.size : 5;
|
|
1598
|
+
m && (b *= 1.5);
|
|
1599
|
+
const w = a.itemStyle || {}, S = w.borderColor || "#fff", x = w.borderWidth !== void 0 ? w.borderWidth : 1, C = w.opacity !== void 0 ? w.opacity : 1, z = a.textStyle || {}, _ = z.show !== void 0 ? z.show : !0, A = z.color || u, Y = z.fontSize !== void 0 ? z.fontSize : 12, P = z.fontWeight || "normal", k = z.position || "top", M = z.offset || [0, 0], L = a.glowStyle || {}, Q = L.show !== void 0 ? L.show : !1, N = L.colors, st = L.blur !== void 0 ? L.blur : 15, Z = L.startColor || u, tt = L.endColor || "transparent", H = L.count !== void 0 ? L.count : 3, dt = L.glowDistance !== void 0 ? L.glowDistance : 2;
|
|
1600
|
+
if (s.save(), s.globalAlpha = C, Q) {
|
|
1601
|
+
s.shadowColor = N ? N[0] : Z, s.shadowBlur = st;
|
|
1602
|
+
let X = [], I = H;
|
|
1603
|
+
if (Array.isArray(N) && N.length > 0)
|
|
1604
|
+
X = N, I = X.length;
|
|
1605
|
+
else
|
|
1606
|
+
for (let R = 0; R < I; R++) {
|
|
1607
|
+
const U = I > 1 ? R / (I - 1) : 0;
|
|
1608
|
+
X.push($t(Z, tt, U));
|
|
1609
|
+
}
|
|
1610
|
+
for (let R = I - 1; R >= 0; R--) {
|
|
1611
|
+
const U = X[R];
|
|
1612
|
+
s.save(), s.fillStyle = U, s.beginPath(), nt(s, g, p, y, b, i, (R + 1) * dt), s.fill(), s.restore();
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
if (s.beginPath(), nt(s, g, p, y, b, i, 0), y === "ring" ? (s.strokeStyle = u, s.lineWidth = x * i, s.stroke()) : (s.fillStyle = u, s.fill(), x > 0 && (s.strokeStyle = S, s.lineWidth = x * i, s.stroke())), s.restore(), _ && c) {
|
|
1616
|
+
s.save(), s.globalAlpha = C, s.font = `${P} ${Math.round(Y * i)}px ${n}`, s.fillStyle = A;
|
|
1617
|
+
let X = "center", I = "middle", R = g + M[0] * i, U = p + M[1] * i, V = 6 * i;
|
|
1618
|
+
y === "circle" || y === "ring" || y === "star" || y === "triangle" ? V += b * i : y === "square" && (V += b * i / 2), k === "top" ? (X = "center", I = "bottom", U -= V) : k === "bottom" ? (X = "center", I = "top", U += V) : k === "left" ? (X = "right", I = "middle", R -= V) : k === "right" ? (X = "left", I = "middle", R += V) : k === "center" && (X = "center", I = "middle"), s.textAlign = X, s.textBaseline = I, s.fillText(c, R, U), s.restore();
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
class Gt {
|
|
1624
|
+
constructor(t, e = {}) {
|
|
1625
|
+
this.initDOM(t), this.renderer = new Rt(this.ctx), this.state = {
|
|
1626
|
+
W: 0,
|
|
1627
|
+
H: 0,
|
|
1628
|
+
dpr: 1,
|
|
1629
|
+
scale: 1,
|
|
1630
|
+
tx: 0,
|
|
1631
|
+
ty: 0,
|
|
1632
|
+
option: {},
|
|
1633
|
+
tri: {},
|
|
1634
|
+
zones: [],
|
|
1635
|
+
hoveredPoint: null
|
|
1636
|
+
}, this._zoom = new ut(e.zoom), this.events = { click: null, viewChange: null, hover: null }, this._initTooltip(), this.bindEvents(), this.ro = new ResizeObserver(() => this.fitCanvas()), this.ro.observe(this.container), this.fitCanvas(), Object.keys(e).length && this.setOption(e);
|
|
1637
|
+
}
|
|
1638
|
+
initDOM(t) {
|
|
1639
|
+
let e = typeof t == "string" ? document.getElementById(t) : t;
|
|
1640
|
+
if (!e) throw new Error("DuvalTriangle: container not found");
|
|
1641
|
+
this.container = e.tagName.toLowerCase() === "canvas" && e.parentElement || e, this.canvas = e.tagName.toLowerCase() === "canvas" ? e : document.createElement("canvas"), e.tagName.toLowerCase() !== "canvas" && (this.container.style.position = this.container.style.position || "relative", this.canvas.style.cssText = "display:block;width:100%;height:100%;cursor:default;", this.container.appendChild(this.canvas)), this.ctx = this.canvas.getContext("2d");
|
|
1642
|
+
}
|
|
1643
|
+
_initTooltip() {
|
|
1644
|
+
var e;
|
|
1645
|
+
const t = this.state.option.tooltip || at.tooltip;
|
|
1646
|
+
this._tooltip = new yt(this.container, {
|
|
1647
|
+
tooltipBg: t.backgroundColor || "rgba(5,13,26,0.95)",
|
|
1648
|
+
tooltipTextColor: ((e = t.textStyle) == null ? void 0 : e.color) || "#c8d8e8",
|
|
1649
|
+
tooltipPadding: t.padding || 10,
|
|
1650
|
+
zoneBorderColor: t.borderColor || "#00e5ff",
|
|
1651
|
+
fontFamily: "monospace"
|
|
1652
|
+
}), this._tooltip.el.style.whiteSpace = "nowrap", this._tooltip.el.style.lineHeight = "1.6";
|
|
1653
|
+
}
|
|
1654
|
+
setOption(t) {
|
|
1655
|
+
!this.state.option || !Object.keys(this.state.option).length ? this.state.option = E(E({}, at), t) : E(this.state.option, t), this._normalizeOptions(), this.computeGeometry(), this.buildZonePaths(), this.render();
|
|
1656
|
+
}
|
|
1657
|
+
_normalizeOptions() {
|
|
1658
|
+
const t = this.state.option;
|
|
1659
|
+
t.vertex && !t.side && (t.side = t.vertex, delete t.vertex), t.axes && Array.isArray(t.axes) && (t.side || (t.side = {}), t.side.data = t.axes.map((e) => typeof e == "object" ? e.name : e), delete t.axes), t.axisStyle && (t.side || (t.side = {}), E(t.side, t.axisStyle), delete t.axisStyle), t.side || (t.side = {}), (!t.side.data || t.side.data.length < 3) && (t.side.data = ["A", "B", "C"]), t.zones && Array.isArray(t.zones) && (t.zone || (t.zone = {}), t.zone.data = t.zones, delete t.zones), t.showTooltip !== void 0 && (t.tooltip || (t.tooltip = {}), t.tooltip.show = t.showTooltip);
|
|
1660
|
+
}
|
|
1661
|
+
computeGeometry() {
|
|
1662
|
+
const t = window.devicePixelRatio || 1;
|
|
1663
|
+
this.state.W = this.canvas.width / t, this.state.H = this.canvas.height / t, this.state.dpr = t;
|
|
1664
|
+
const e = this.state.option.padding ?? 45, o = this.state.option.grid || {}, s = ct(this.state.W, this.state.H, o, e);
|
|
1665
|
+
this.state.tri = D.computeTri(s);
|
|
1666
|
+
}
|
|
1667
|
+
buildZonePaths() {
|
|
1668
|
+
var s;
|
|
1669
|
+
const { option: t, tri: e } = this.state, o = ((s = t.zone) == null ? void 0 : s.data) || [];
|
|
1670
|
+
this.state.zones = o.map((i) => {
|
|
1671
|
+
const n = { ...i }, l = new Path2D();
|
|
1672
|
+
if (i.points.forEach(([h, a, r], c) => {
|
|
1673
|
+
const d = D.t2c(h, a, r, e);
|
|
1674
|
+
c === 0 ? l.moveTo(d.x, d.y) : l.lineTo(d.x, d.y);
|
|
1675
|
+
}), l.closePath(), n.path = l, i.labelAt)
|
|
1676
|
+
n.labelPos = { a: i.labelAt[0], b: i.labelAt[1], c: i.labelAt[2] };
|
|
1677
|
+
else {
|
|
1678
|
+
const h = i.points.map((c) => D.t2c(c[0], c[1], c[2], e)), a = D.polyCentroid(h), r = D.c2t(a.x, a.y, e);
|
|
1679
|
+
n.labelPos = { a: r.a, b: r.b, c: r.c };
|
|
1680
|
+
}
|
|
1681
|
+
return n;
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
fitCanvas() {
|
|
1685
|
+
const t = window.devicePixelRatio || 1, e = this.container.clientWidth || 400, o = this.container.clientHeight || 400;
|
|
1686
|
+
this.canvas.width = Math.round(e * t), this.canvas.height = Math.round(o * t), this.canvas.style.width = e + "px", this.canvas.style.height = o + "px", this.state.scale = 1, this.state.tx = 0, this.state.ty = 0, this._zoom && this._zoom.reset(), Object.keys(this.state.option).length && (this.computeGeometry(), this.buildZonePaths(), this.render());
|
|
1687
|
+
}
|
|
1688
|
+
render() {
|
|
1689
|
+
this.state.scale = this._zoom.zoom, this.state.tx = this._zoom.panX, this.state.ty = this._zoom.panY, this.renderer.render(this.state);
|
|
1690
|
+
}
|
|
1691
|
+
bindEvents() {
|
|
1692
|
+
const t = this.canvas;
|
|
1693
|
+
this._onWheel = (e) => {
|
|
1694
|
+
e.preventDefault();
|
|
1695
|
+
const o = t.getBoundingClientRect(), s = (e.clientX - o.left) * (this.state.W / o.width), i = (e.clientY - o.top) * (this.state.H / o.height);
|
|
1696
|
+
this._zoom.onWheel(e.deltaY, s, i), typeof this.events.viewChange == "function" && this.events.viewChange(this._zoom.state), this.render();
|
|
1697
|
+
}, this._onDown = (e) => {
|
|
1698
|
+
if (e.button !== 0) return;
|
|
1699
|
+
const o = t.getBoundingClientRect(), s = this.state.W / o.width, i = this.state.H / o.height;
|
|
1700
|
+
this._zoom.startDrag(e.clientX * s, e.clientY * i), t.style.cursor = "grabbing";
|
|
1701
|
+
}, this._onMove = (e) => {
|
|
1702
|
+
const o = t.getBoundingClientRect(), s = this.state.W / o.width, i = this.state.H / o.height;
|
|
1703
|
+
if (this._zoom.moveDrag(e.clientX * s, e.clientY * i, 1)) {
|
|
1704
|
+
this.render();
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
this.handleMouseMove(e);
|
|
1708
|
+
}, this._onUp = () => {
|
|
1709
|
+
this._zoom.endDrag() && (t.style.cursor = this.state.hoveredPoint ? "pointer" : "default", typeof this.events.viewChange == "function" && this.events.viewChange(this._zoom.state));
|
|
1710
|
+
}, this._onLeave = () => {
|
|
1711
|
+
this.hideTooltip(), this.state.hoveredPoint = null, typeof this.events.hover == "function" && this.events.hover(null), this.render();
|
|
1712
|
+
}, this._onDbl = () => {
|
|
1713
|
+
this._zoom.reset(), typeof this.events.viewChange == "function" && this.events.viewChange(this._zoom.state), this.render();
|
|
1714
|
+
}, t.addEventListener("wheel", this._onWheel, { passive: !1 }), t.addEventListener("mousedown", this._onDown), t.addEventListener("mousemove", this._onMove), t.addEventListener("mouseleave", this._onLeave), t.addEventListener("dblclick", this._onDbl), t.addEventListener("click", this.handleClick.bind(this)), window.addEventListener("mouseup", this._onUp);
|
|
1715
|
+
}
|
|
1716
|
+
handleMouseMove(t) {
|
|
1717
|
+
var p;
|
|
1718
|
+
const e = this.canvas.getBoundingClientRect(), o = this.state.W / e.width, s = this.state.H / e.height, i = (t.clientX - e.left) * o, n = (t.clientY - e.top) * s, l = (i - this._zoom.panX) / this._zoom.zoom, h = (n - this._zoom.panY) / this._zoom.zoom, { a, b: r, c } = D.c2t(l, h, this.state.tri);
|
|
1719
|
+
if (a < -1e-7 || r < -1e-7 || c < -1e-7) {
|
|
1720
|
+
this.hideTooltip(), this.state.hoveredPoint !== null && (this.state.hoveredPoint = null, this.canvas.style.cursor = "default", this.render());
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
let d = null, f = 14;
|
|
1724
|
+
const g = (p = this.state.option.side) == null ? void 0 : p.data;
|
|
1725
|
+
(this.state.option.series || []).forEach((m) => {
|
|
1726
|
+
const y = m.data;
|
|
1727
|
+
if (!y) return;
|
|
1728
|
+
const u = D.resolveData(y, g);
|
|
1729
|
+
if (!u) return;
|
|
1730
|
+
const b = D.t2c(...u, this.state.tri), w = Math.hypot(b.x - l, b.y - h);
|
|
1731
|
+
w < f && (f = w, d = { series: m, raw: u });
|
|
1732
|
+
}), d !== this.state.hoveredPoint && (this.state.hoveredPoint = d, this.canvas.style.cursor = d ? "pointer" : "default", this.render()), this.showTooltip(l, h, a, r, c);
|
|
1733
|
+
}
|
|
1734
|
+
handleClick(t) {
|
|
1735
|
+
if (this._zoom.isDragging) return;
|
|
1736
|
+
const e = this.canvas.getBoundingClientRect(), o = this.state.W / e.width, s = this.state.H / e.height, i = (t.clientX - e.left) * o, n = (t.clientY - e.top) * s, l = (i - this._zoom.panX) / this._zoom.zoom, h = (n - this._zoom.panY) / this._zoom.zoom, { a, b: r, c } = D.c2t(l, h, this.state.tri);
|
|
1737
|
+
if (a < -1e-7 || r < -1e-7 || c < -1e-7) return;
|
|
1738
|
+
const d = this.getZoneAt(a, r, c);
|
|
1739
|
+
typeof this.events.click == "function" && this.events.click({ a, b: r, c, zone: d }), this.canvas.dispatchEvent(
|
|
1740
|
+
new CustomEvent("triangle:click", { detail: { a, b: r, c, zone: d }, bubbles: !0 })
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
showTooltip(t, e, o, s, i) {
|
|
1744
|
+
var y, u;
|
|
1745
|
+
const n = this.state.option.tooltip || {};
|
|
1746
|
+
if (n.show === !1 || !this.state.hoveredPoint && n.showDiagnostic === !1) {
|
|
1747
|
+
this.hideTooltip();
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
const l = this.getZoneAt(o, s, i), a = (this.state.option.side || {}).data || ["A", "B", "C"];
|
|
1751
|
+
if (typeof this.events.hover == "function" && this.events.hover({
|
|
1752
|
+
ratio: [o, s, i],
|
|
1753
|
+
zone: l,
|
|
1754
|
+
point: ((y = this.state.hoveredPoint) == null ? void 0 : y.series) || null
|
|
1755
|
+
}), n.formatter) {
|
|
1756
|
+
const b = n.formatter({
|
|
1757
|
+
a: o,
|
|
1758
|
+
b: s,
|
|
1759
|
+
c: i,
|
|
1760
|
+
sNames: a,
|
|
1761
|
+
zone: l,
|
|
1762
|
+
point: (u = this.state.hoveredPoint) == null ? void 0 : u.series
|
|
1763
|
+
}), w = t * this._zoom.zoom + this._zoom.panX, S = e * this._zoom.zoom + this._zoom.panY;
|
|
1764
|
+
this._tooltip.show(b, w, S, n, {}, l);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
const r = this._defaultTooltipHTML(o, s, i, a, l), c = t * this._zoom.zoom + this._zoom.panX, d = e * this._zoom.zoom + this._zoom.panY, f = this.canvas.getBoundingClientRect(), g = this.container.getBoundingClientRect(), p = f.left - g.left, m = f.top - g.top;
|
|
1768
|
+
this._tooltip.show(r, c + p, d + m, n, {}, l);
|
|
1769
|
+
}
|
|
1770
|
+
hideTooltip() {
|
|
1771
|
+
this._tooltip && this._tooltip.hide();
|
|
1772
|
+
}
|
|
1773
|
+
getZoneAt(t, e, o) {
|
|
1774
|
+
const s = D.t2c(t, e, o, this.state.tri), i = this.ctx;
|
|
1775
|
+
i.save(), i.setTransform(1, 0, 0, 1, 0, 0);
|
|
1776
|
+
const n = (this.state.zones || []).find((l) => l.path && i.isPointInPath(l.path, s.x, s.y));
|
|
1777
|
+
return i.restore(), n;
|
|
1778
|
+
}
|
|
1779
|
+
resetZoom() {
|
|
1780
|
+
this.state.scale = 1, this.state.tx = 0, this.state.ty = 0, this.render(), typeof this.events.viewChange == "function" && this.events.viewChange({ scale: this.state.scale, tx: this.state.tx, ty: this.state.ty });
|
|
1781
|
+
}
|
|
1782
|
+
dispose() {
|
|
1783
|
+
var t, e, o;
|
|
1784
|
+
(t = this.ro) == null || t.disconnect(), (e = this._tooltip) == null || e.dispose(), (o = this.canvas) == null || o.remove();
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* 默认 tooltip HTML 生成
|
|
1788
|
+
* @private
|
|
1789
|
+
*/
|
|
1790
|
+
_defaultTooltipHTML(t, e, o, s, i) {
|
|
1791
|
+
var m, y;
|
|
1792
|
+
const n = this.state.hoveredPoint, l = this.state.option.tooltip || {}, h = l.showPoint !== !1, a = l.showDiagnostic !== !1;
|
|
1793
|
+
let r = "";
|
|
1794
|
+
if (n && h) {
|
|
1795
|
+
let u = ((m = n.series) == null ? void 0 : m.name) || "";
|
|
1796
|
+
u && (r = `<div style="font-size:14px; font-weight:bold; margin-bottom:4px; color:#fff;">${u}</div>`);
|
|
1797
|
+
}
|
|
1798
|
+
const c = i ? `${i.name || i.id} ${i.desc || i.title || ""}` : "Outside", d = i && ((y = i.color) == null ? void 0 : y.replace(/[\d.]+\)$/, "1)")) || "#fff";
|
|
1799
|
+
let f = `
|
|
1800
|
+
${r}
|
|
1801
|
+
${c ? `<div style="color:${d}; font-size:13px; font-weight:bold; padding-bottom:8px; margin-bottom:8px; border-bottom:1px solid rgba(255,255,255,0.07);">${c}</div>` : ""}
|
|
1802
|
+
`;
|
|
1803
|
+
const g = ["#00e5ff", "#ff6b35", "#ffcc02"], p = n == null ? void 0 : n.item;
|
|
1804
|
+
return (h || !h && !a) && s.forEach((u, b) => {
|
|
1805
|
+
const w = g[b % 3];
|
|
1806
|
+
let S = "-";
|
|
1807
|
+
p && (Array.isArray(p) ? S = p[b] !== void 0 ? p[b] : "-" : typeof p == "object" && (p.value && Array.isArray(p.value) ? S = p.value[b] !== void 0 ? p.value[b] : "-" : p[u] !== void 0 && (S = p[u]))), f += `
|
|
1808
|
+
<div style="display:flex; align-items:center; margin-bottom:6px; font-size:13px;">
|
|
1809
|
+
<span style="display:inline-block; width:8px; height:8px; border-radius:50%; background-color:${w}; margin-right:8px;"></span>
|
|
1810
|
+
<span style="color:#cbd5e1; flex-grow:1;">${u}</span>
|
|
1811
|
+
<span style="color:#fff; font-weight:bold; margin-left:20px;">${S}</span>
|
|
1812
|
+
</div>
|
|
1813
|
+
`;
|
|
1814
|
+
}), f;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
function St(v) {
|
|
1818
|
+
const t = v.trim().toLowerCase();
|
|
1819
|
+
if (t.startsWith("#")) {
|
|
1820
|
+
let o = t.slice(1);
|
|
1821
|
+
o.length === 3 && (o = o[0] + o[0] + o[1] + o[1] + o[2] + o[2]);
|
|
1822
|
+
const s = parseInt(o.substring(0, 2), 16), i = parseInt(o.substring(2, 4), 16), n = parseInt(o.substring(4, 6), 16), l = o.length === 8 ? parseInt(o.substring(6, 8), 16) / 255 : 1;
|
|
1823
|
+
return [s, i, n, l];
|
|
1824
|
+
}
|
|
1825
|
+
const e = t.match(/rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\)/);
|
|
1826
|
+
return e ? [
|
|
1827
|
+
parseInt(e[1], 10),
|
|
1828
|
+
parseInt(e[2], 10),
|
|
1829
|
+
parseInt(e[3], 10),
|
|
1830
|
+
e[4] !== void 0 ? parseFloat(e[4]) : 1
|
|
1831
|
+
] : t === "transparent" ? [0, 0, 0, 0] : [255, 255, 255, 1];
|
|
1832
|
+
}
|
|
1833
|
+
function $t(v, t, e) {
|
|
1834
|
+
const [o, s, i, n] = St(v), [l, h, a, r] = St(t), c = Math.round(o + (l - o) * e), d = Math.round(s + (h - s) * e), f = Math.round(i + (a - i) * e), g = n + (r - n) * e;
|
|
1835
|
+
return `rgba(${c},${d},${f},${g})`;
|
|
1836
|
+
}
|
|
1837
|
+
const T = Object.freeze({
|
|
1838
|
+
// 故障区域定义(用于诊断逻辑与 3D/2D 绘图)
|
|
1839
|
+
ZONES: kt,
|
|
1840
|
+
// 坐标轴固定配置(X: C2H4/C2H6, Y: CH4/H2, Z: C2H2/C2H4)
|
|
1841
|
+
AXES: {
|
|
1842
|
+
x: { color: "#ff9a3c", label: "X (C₂H₄/C₂H₆)" },
|
|
1843
|
+
y: { color: "#4ac8ff", label: "Y (CH₄/H₂)" },
|
|
1844
|
+
z: { color: "#70e870", label: "Z (C₂H₂/C₂H₄)" }
|
|
1845
|
+
},
|
|
1846
|
+
// 绘图主题配置(深浅两色)
|
|
1847
|
+
THEMES: {
|
|
1848
|
+
dark: {
|
|
1849
|
+
wallFillAlpha: 0.05,
|
|
1850
|
+
// 立方体墙面填充透明度
|
|
1851
|
+
wallGridAlpha: "40",
|
|
1852
|
+
// 墙面网格线 Alpha (Hex)
|
|
1853
|
+
wallEdgeAlpha: "50",
|
|
1854
|
+
// 墙面边缘 Alpha (Hex)
|
|
1855
|
+
outerGrid: "rgba(100,150,220,0.10)",
|
|
1856
|
+
// 外部辅助网格
|
|
1857
|
+
plane2dGrid: "rgba(100,150,220,0.20)",
|
|
1858
|
+
// 2D 投影网格
|
|
1859
|
+
plane2dAxis: "rgba(120,160,220,0.50)",
|
|
1860
|
+
// 2D 投影轴线
|
|
1861
|
+
plane2dLabel: "rgba(150,180,220,0.60)",
|
|
1862
|
+
// 2D 区域文字标签颜色
|
|
1863
|
+
tickAlpha: "cc",
|
|
1864
|
+
// 刻度文字 Alpha
|
|
1865
|
+
pointStroke: "rgba(255,255,255,0.55)"
|
|
1866
|
+
// 样本点边缘描边
|
|
1867
|
+
},
|
|
1868
|
+
light: {
|
|
1869
|
+
wallFillAlpha: 0.06,
|
|
1870
|
+
wallGridAlpha: "28",
|
|
1871
|
+
wallEdgeAlpha: "55",
|
|
1872
|
+
outerGrid: "rgba(80,120,200,0.12)",
|
|
1873
|
+
plane2dGrid: "rgba(80,120,200,0.18)",
|
|
1874
|
+
plane2dAxis: "rgba(80,120,200,0.45)",
|
|
1875
|
+
plane2dLabel: "rgba(60,100,180,0.65)",
|
|
1876
|
+
tickAlpha: "bb",
|
|
1877
|
+
pointStroke: "rgba(0,0,0,0.25)"
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
// 2D 投影平面视角配置
|
|
1881
|
+
PLANES: {
|
|
1882
|
+
xz: {
|
|
1883
|
+
hAxis: "x",
|
|
1884
|
+
vAxis: "z",
|
|
1885
|
+
hLabel: "X C₂H₄/C₂H₆(热故障)",
|
|
1886
|
+
vLabel: "Z C₂H₂/C₂H₄(高能放电)",
|
|
1887
|
+
bgColor: {
|
|
1888
|
+
dark: "rgba(180,120,60,0.06)",
|
|
1889
|
+
light: "rgba(180,100,40,0.05)"
|
|
1890
|
+
}
|
|
1891
|
+
},
|
|
1892
|
+
yz: {
|
|
1893
|
+
hAxis: "y",
|
|
1894
|
+
vAxis: "z",
|
|
1895
|
+
hLabel: "Y CH₄/H₂(低能放电)",
|
|
1896
|
+
vLabel: "Z C₂H₂/C₂H₄(高能放电)",
|
|
1897
|
+
bgColor: {
|
|
1898
|
+
dark: "rgba(60,160,180,0.06)",
|
|
1899
|
+
light: "rgba(40,140,160,0.05)"
|
|
1900
|
+
}
|
|
1901
|
+
},
|
|
1902
|
+
xy: {
|
|
1903
|
+
hAxis: "x",
|
|
1904
|
+
vAxis: "y",
|
|
1905
|
+
hLabel: "X C₂H₄/C₂H₆(热故障)",
|
|
1906
|
+
vLabel: "Y CH₄/H₂(低能放电)",
|
|
1907
|
+
bgColor: { dark: "rgba(60,100,200,0.06)", light: "rgba(40,80,180,0.05)" }
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
TICKS: { vals: [0, 1 / 3, 2 / 3, 1], labels: ["0", "0.1", "1.0", "10"] },
|
|
1911
|
+
// 刻度映射
|
|
1912
|
+
CAM: { elev: -20, yaw: 115 },
|
|
1913
|
+
// 初始相机仰角、偏航角
|
|
1914
|
+
PROJ: { fov: 4, scaleK: 0.45, cx: 0.48, cy: 0.52 },
|
|
1915
|
+
// 投影及中心偏移参数
|
|
1916
|
+
PAD2D: { l: 70, r: 30, t: 50, b: 60 },
|
|
1917
|
+
// 2D 投影边距
|
|
1918
|
+
DRAG: {
|
|
1919
|
+
rotYSens: 0.45,
|
|
1920
|
+
rotXSens: 0.28,
|
|
1921
|
+
rotXMin: -70,
|
|
1922
|
+
rotXMax: 85,
|
|
1923
|
+
zoomMin: 0.3,
|
|
1924
|
+
zoomMax: 4,
|
|
1925
|
+
zoomIn: 1.08,
|
|
1926
|
+
zoomOut: 0.92
|
|
1927
|
+
},
|
|
1928
|
+
// 交互灵敏度与限制
|
|
1929
|
+
TWEEN: { step: 0.045 },
|
|
1930
|
+
// 视角切换动画步长
|
|
1931
|
+
POINT: {
|
|
1932
|
+
defaultSize: 7,
|
|
1933
|
+
defaultColor: "#aaaaaa",
|
|
1934
|
+
glowAlpha: 0.25,
|
|
1935
|
+
glowRadius: 2.2,
|
|
1936
|
+
hlOffset: 0.35
|
|
1937
|
+
},
|
|
1938
|
+
// 样本点视觉参数
|
|
1939
|
+
ZONE_VIS: { fillAlpha: 0.25, labelAlpha: 0.9 },
|
|
1940
|
+
// 3D 区域填充透明度(玻璃块效果)
|
|
1941
|
+
UNKNOWN: { name: "未分类(需综合判断)", color: "#888899" },
|
|
1942
|
+
// 无法匹配诊断规则时的默认项
|
|
1943
|
+
TEXT_STYLE: {
|
|
1944
|
+
fontSize: 14,
|
|
1945
|
+
fontFamily: "Segoe UI, Courier New",
|
|
1946
|
+
fontWeight: "normal"
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
class F {
|
|
1950
|
+
/**
|
|
1951
|
+
* 将原始三比值数值转换为 0-1 的归一化坐标 (非等比例分段对数变换)
|
|
1952
|
+
* 变换规则:[0, 0.1]->[0, 1/3], [0.1, 1]->[1/3, 2/3], [1, 10]->[2/3, 1]
|
|
1953
|
+
*/
|
|
1954
|
+
static realToU(t) {
|
|
1955
|
+
return t <= 0 ? 0 : t <= 0.1 ? t / 0.3 : t <= 1 ? 1 / 3 + (Math.log10(t) + 1) / 3 : t <= 10 ? 2 / 3 + Math.log10(t) / 3 : 1;
|
|
1956
|
+
}
|
|
1957
|
+
/** 将归一化单位值还原为原始比值 */
|
|
1958
|
+
static uToReal(t) {
|
|
1959
|
+
return t <= 0 ? 0 : t <= 1 / 3 ? t * 0.3 : t <= 2 / 3 ? Math.pow(10, (t - 1 / 3) * 3 - 1) : Math.pow(10, (t - 2 / 3) * 3);
|
|
1960
|
+
}
|
|
1961
|
+
/** 格式化数值输出 */
|
|
1962
|
+
static formatVal(t) {
|
|
1963
|
+
return t < 0.01 ? t.toFixed(4) : t.toFixed(3);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
class It {
|
|
1967
|
+
constructor(t, e, o, s = {}) {
|
|
1968
|
+
this.ctx = t, this.opts = s, this.layout = s.layout ?? "fill", this.resize(e, o), this.updateOptions(s);
|
|
1969
|
+
}
|
|
1970
|
+
/** 更新渲染配置 */
|
|
1971
|
+
updateOptions(t = {}) {
|
|
1972
|
+
this.opts = { ...this.opts, ...t }, (t.layout !== void 0 || t.grid !== void 0) && (t.layout !== void 0 && (this.layout = t.layout), this.resize(this.W, this.H));
|
|
1973
|
+
const e = this.opts.theme ?? "dark";
|
|
1974
|
+
this.theme = T.THEMES[e] ?? T.THEMES.dark, this.themeName = e, this.zone = this.processZones(this.opts.zone ?? T.ZONES);
|
|
1975
|
+
const o = {
|
|
1976
|
+
show: !0,
|
|
1977
|
+
fill: { show: !0, alpha: this.theme.wallFillAlpha, colors: { xy: void 0, xz: void 0, yz: void 0 } },
|
|
1978
|
+
grid: { show: !0, lineStyle: { lineWidth: 0.6, alpha: parseInt(this.theme.wallGridAlpha, 16) / 255, type: "solid" } },
|
|
1979
|
+
border: { show: !0, lineStyle: { lineWidth: 0.8, alpha: parseInt(this.theme.wallEdgeAlpha, 16) / 255, type: "solid" } }
|
|
1980
|
+
}, s = {
|
|
1981
|
+
show: !0,
|
|
1982
|
+
lineStyle: { lineWidth: 1.8, arrowSize: 9 },
|
|
1983
|
+
labelStyle: { show: !0, fontSize: 14, fontWeight: "bold", distance: 16 },
|
|
1984
|
+
tickLabelStyle: { show: !0, fontSize: 12, color: "rgba(255, 255, 255, 0.8)" },
|
|
1985
|
+
data: {
|
|
1986
|
+
x: { name: T.AXES.x.label, color: T.AXES.x.color },
|
|
1987
|
+
y: { name: T.AXES.y.label, color: T.AXES.y.color },
|
|
1988
|
+
z: { name: T.AXES.z.label, color: T.AXES.z.color }
|
|
1989
|
+
}
|
|
1990
|
+
}, i = {
|
|
1991
|
+
show: !0,
|
|
1992
|
+
fade3DAlpha: 0.03,
|
|
1993
|
+
grid: {
|
|
1994
|
+
show: !0,
|
|
1995
|
+
backgroundColor: void 0,
|
|
1996
|
+
lineStyle: { lineWidth: 0.5, alpha: 0.2, type: "solid" }
|
|
1997
|
+
},
|
|
1998
|
+
axis: {
|
|
1999
|
+
show: !0,
|
|
2000
|
+
lineStyle: { lineWidth: 1.5, alpha: 0.9 },
|
|
2001
|
+
labelStyle: { show: !0, fontSize: 13, distance: 25 },
|
|
2002
|
+
tickLabelStyle: { show: !0, fontSize: 11, distance: 8 }
|
|
2003
|
+
},
|
|
2004
|
+
zone: {
|
|
2005
|
+
show: !0,
|
|
2006
|
+
alpha: 0.45,
|
|
2007
|
+
borderStyle: { show: !0, lineWidth: 1.4 }
|
|
2008
|
+
}
|
|
2009
|
+
};
|
|
2010
|
+
this.opts.wall = E(o, this.opts.wall || {}), this.opts.axis = E(s, this.opts.axis || {}), this.opts.plane2D = E(i, this.opts.plane2D || {});
|
|
2011
|
+
}
|
|
2012
|
+
/** 预计算区域数据 */
|
|
2013
|
+
processZones(t) {
|
|
2014
|
+
let e = [], o = {};
|
|
2015
|
+
return Array.isArray(t) ? e = t : t && typeof t == "object" && Array.isArray(t.data) ? (e = t.data, o = t.labelStyle || {}) : e = T.ZONES, e.map((s) => {
|
|
2016
|
+
const [i, n, l] = ht(s.fill || "#ffffff"), h = s.labelStyle || o, a = h.color || s.fill;
|
|
2017
|
+
let r = [];
|
|
2018
|
+
h.fontWeight && r.push(h.fontWeight), h.fontSize && r.push(h.fontSize + "px"), h.fontFamily && r.push(h.fontFamily);
|
|
2019
|
+
const c = r.length > 0 ? r.join(" ") : null;
|
|
2020
|
+
let d = s.edgeColor || s.fill;
|
|
2021
|
+
return s.edgeAlpha !== void 0 && (d = _t(d, s.edgeAlpha)), {
|
|
2022
|
+
...s,
|
|
2023
|
+
fillBase: `rgba(${i},${n},${l},`,
|
|
2024
|
+
edgeStr: d,
|
|
2025
|
+
x0: F.realToU(s.p1[0]),
|
|
2026
|
+
x1: F.realToU(s.p2[0]),
|
|
2027
|
+
y0: F.realToU(s.p1[1]),
|
|
2028
|
+
y1: F.realToU(s.p2[1]),
|
|
2029
|
+
z0: F.realToU(s.p1[2]),
|
|
2030
|
+
z1: F.realToU(s.p2[2]),
|
|
2031
|
+
labelColor: a,
|
|
2032
|
+
labelFontStr: c
|
|
2033
|
+
};
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
/** 获取配置的字体样式 */
|
|
2037
|
+
getFont(t) {
|
|
2038
|
+
var l;
|
|
2039
|
+
const e = T.TEXT_STYLE, o = ((l = this.opts.textStyle) == null ? void 0 : l[t]) ?? this.opts.textStyle ?? {}, s = o.fontSize ?? e.fontSize, i = o.fontFamily ?? e.fontFamily;
|
|
2040
|
+
return `${o.fontWeight ?? (t === "axis" || t === "zone" ? "bold" : e.fontWeight)} ${s}px ${i}`;
|
|
2041
|
+
}
|
|
2042
|
+
/** 更新渲染器主题 */
|
|
2043
|
+
setTheme(t) {
|
|
2044
|
+
this.updateOptions({ theme: t });
|
|
2045
|
+
}
|
|
2046
|
+
/** 画布尺寸更新回调 */
|
|
2047
|
+
resize(t, e) {
|
|
2048
|
+
this.W = t, this.H = e;
|
|
2049
|
+
const o = this.opts.grid;
|
|
2050
|
+
if (o && (o.left !== void 0 || o.right !== void 0 || o.top !== void 0 || o.bottom !== void 0)) {
|
|
2051
|
+
const i = ct(t, e, o, 0);
|
|
2052
|
+
this.renderW = i.availW, this.renderH = i.availH, this.offsetX = i.left, this.offsetY = i.top;
|
|
2053
|
+
} else if (this.layout === "square") {
|
|
2054
|
+
const i = Math.min(t, e);
|
|
2055
|
+
this.renderW = i, this.renderH = i, this.offsetX = (t - i) / 2, this.offsetY = (e - i) / 2;
|
|
2056
|
+
} else
|
|
2057
|
+
this.renderW = t, this.renderH = e, this.offsetX = 0, this.offsetY = 0;
|
|
2058
|
+
this.CX = this.offsetX + this.renderW * T.PROJ.cx, this.CY = this.offsetY + this.renderH * T.PROJ.cy;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* 核心投影函数:将 0-1 坐标系的 3D 点映射为 2D 屏幕画布坐标
|
|
2062
|
+
* 逻辑:平移中心 -> 执行绕 X, Y 轴旋转矩阵 -> 应用视场角(FOV)进行透视缩放 -> 映射至屏幕中心
|
|
2063
|
+
*/
|
|
2064
|
+
proj(t, e, o) {
|
|
2065
|
+
const s = t - 0.5, i = e - 0.5, n = o - 0.5, l = this.cam.rotX * Math.PI / 180, h = this.cam.rotY * Math.PI / 180, a = Math.cos(l), r = Math.sin(l), c = Math.cos(h), d = Math.sin(h), f = s * c + i * d, g = -s * d + i * c, p = n * a - g * r, m = n * r + g * a, y = Math.min(this.renderW, this.renderH) * T.PROJ.scaleK * this.cam.zoomFactor, u = T.PROJ.fov, b = m + u;
|
|
2066
|
+
return {
|
|
2067
|
+
x: this.CX + f / b * y * u,
|
|
2068
|
+
y: this.CY - p / b * y * u,
|
|
2069
|
+
depth: m
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
/** 判断法向量是否背向相机 (Backface Culling) */
|
|
2073
|
+
isBackface(t, e, o) {
|
|
2074
|
+
const s = this.cam.rotX * Math.PI / 180, i = this.cam.rotY * Math.PI / 180, n = Math.cos(s), l = Math.sin(s), h = Math.cos(i), a = Math.sin(i), r = -t * a + e * h;
|
|
2075
|
+
return o * l + r * n > 0;
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* 渲染主循环
|
|
2079
|
+
* @param {Object} cam 相机状态对象
|
|
2080
|
+
* @param {Array} points 样本点数组
|
|
2081
|
+
* @param {string} view 当前视图 ID ('3d', 'xz' 等)
|
|
2082
|
+
* @param {number} flat 扁平化程度 (0-1), 用于 3D 转换 2D 平面的视觉过度
|
|
2083
|
+
*/
|
|
2084
|
+
draw(t, e, o, s) {
|
|
2085
|
+
var a;
|
|
2086
|
+
const i = this.ctx;
|
|
2087
|
+
i.clearRect(0, 0, this.W, this.H), this.cam = t;
|
|
2088
|
+
const n = ((a = this.opts.plane2D) == null ? void 0 : a.fade3DAlpha) ?? 0.03, l = 1 - s * (1 - n);
|
|
2089
|
+
i.globalAlpha = l, this.drawWalls(l), this.drawGrid(l), this.drawZones(l), this.drawAxes(l), this.drawTickLabels(l);
|
|
2090
|
+
const h = Array.isArray(e) ? e : [];
|
|
2091
|
+
[...h].sort(
|
|
2092
|
+
(r, c) => this.proj(F.realToU(r.position.x), F.realToU(r.position.y), F.realToU(r.position.z)).depth - this.proj(F.realToU(c.position.x), F.realToU(c.position.y), F.realToU(c.position.z)).depth
|
|
2093
|
+
).forEach((r) => this.drawPoint3D(r, l)), i.globalAlpha = 1, s > 0 && this.opts.plane2D.show && this.draw2DOverlay(o, s, h);
|
|
2094
|
+
}
|
|
2095
|
+
// 基础绘制
|
|
2096
|
+
face(t, e, o) {
|
|
2097
|
+
const s = this.ctx, i = t.map((n) => this.proj(...n));
|
|
2098
|
+
s.beginPath(), s.moveTo(i[0].x, i[0].y);
|
|
2099
|
+
for (let n = 1; n < i.length; n++) s.lineTo(i[n].x, i[n].y);
|
|
2100
|
+
s.closePath(), e && (s.fillStyle = e, s.fill()), o && (s.strokeStyle = o, s.lineWidth = 0.8, s.stroke());
|
|
2101
|
+
}
|
|
2102
|
+
line(t, e) {
|
|
2103
|
+
const o = this.ctx;
|
|
2104
|
+
o.beginPath(), o.moveTo(t.x, t.y), o.lineTo(e.x, e.y), o.stroke();
|
|
2105
|
+
}
|
|
2106
|
+
box(t, e, o, s, i, n, l, h) {
|
|
2107
|
+
[
|
|
2108
|
+
{ f: [[t, o, i], [e, o, i], [e, s, i], [t, s, i]], n: [0, 0, -1] },
|
|
2109
|
+
{ f: [[t, o, n], [e, o, n], [e, s, n], [t, s, n]], n: [0, 0, 1] },
|
|
2110
|
+
{ f: [[t, o, i], [e, o, i], [e, o, n], [t, o, n]], n: [0, -1, 0] },
|
|
2111
|
+
{ f: [[t, s, i], [e, s, i], [e, s, n], [t, s, n]], n: [0, 1, 0] },
|
|
2112
|
+
{ f: [[t, o, i], [t, s, i], [t, s, n], [t, o, n]], n: [-1, 0, 0] },
|
|
2113
|
+
{ f: [[e, o, i], [e, s, i], [e, s, n], [e, o, n]], n: [1, 0, 0] }
|
|
2114
|
+
].filter(({ n: r }) => !this.isBackface(r[0], r[1], r[2])).map(({ f: r }) => ({
|
|
2115
|
+
f: r,
|
|
2116
|
+
d: r.reduce((c, d) => c + this.proj(...d).depth, 0) / r.length
|
|
2117
|
+
})).sort((r, c) => r.d - c.d).forEach(({ f: r }) => this.face(r, l, h));
|
|
2118
|
+
}
|
|
2119
|
+
drawWalls(t = 1) {
|
|
2120
|
+
const e = this.ctx, o = this.opts.wall;
|
|
2121
|
+
if (!o.show) return;
|
|
2122
|
+
const s = this.opts.axis.data, i = T.TICKS.vals, n = [
|
|
2123
|
+
{
|
|
2124
|
+
// X-Z 面 (底层底面通常认为是 z=0 或 y=0)
|
|
2125
|
+
pts: [[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]],
|
|
2126
|
+
color: o.fill.colors.xz || s.x.color,
|
|
2127
|
+
segs: (l) => [[this.proj(l, 0, 0), this.proj(l, 0, 1)], [this.proj(0, 0, l), this.proj(1, 0, l)]],
|
|
2128
|
+
gridColor: s.x.color
|
|
2129
|
+
},
|
|
2130
|
+
{
|
|
2131
|
+
// Y-Z 面
|
|
2132
|
+
pts: [[0, 0, 0], [0, 1, 0], [0, 1, 1], [0, 0, 1]],
|
|
2133
|
+
color: o.fill.colors.yz || s.y.color,
|
|
2134
|
+
segs: (l) => [[this.proj(0, l, 0), this.proj(0, l, 1)], [this.proj(0, 0, l), this.proj(0, 1, l)]],
|
|
2135
|
+
gridColor: s.y.color
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
// X-Y 面
|
|
2139
|
+
pts: [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]],
|
|
2140
|
+
color: o.fill.colors.xy || s.z.color,
|
|
2141
|
+
segs: (l) => [[this.proj(l, 0, 0), this.proj(l, 1, 0)], [this.proj(0, l, 0), this.proj(1, l, 0)]],
|
|
2142
|
+
gridColor: s.z.color
|
|
2143
|
+
}
|
|
2144
|
+
];
|
|
2145
|
+
o.fill.show && (e.globalAlpha = o.fill.alpha * t, n.forEach((l) => this.face(l.pts, l.color)), e.globalAlpha = t), o.grid.show && (e.lineWidth = o.grid.lineStyle.lineWidth, e.globalAlpha = o.grid.lineStyle.alpha * t, o.grid.lineStyle.type === "dashed" ? e.setLineDash([4, 4]) : o.grid.lineStyle.type === "dotted" ? e.setLineDash([2, 2]) : e.setLineDash([]), n.forEach(({ gridColor: l, segs: h }) => {
|
|
2146
|
+
e.strokeStyle = l, i.forEach((a) => h(a).forEach(([r, c]) => this.line(r, c)));
|
|
2147
|
+
}), e.setLineDash([]), e.globalAlpha = t), o.border.show && (e.lineWidth = o.border.lineStyle.lineWidth, e.globalAlpha = o.border.lineStyle.alpha * t, o.border.lineStyle.type === "dashed" ? e.setLineDash([4, 4]) : o.border.lineStyle.type === "dotted" ? e.setLineDash([2, 2]) : e.setLineDash([]), n.forEach(({ pts: l, gridColor: h }) => {
|
|
2148
|
+
e.strokeStyle = h, l.forEach((a, r) => this.line(this.proj(...a), this.proj(...l[(r + 1) % l.length])));
|
|
2149
|
+
}), e.setLineDash([]), e.globalAlpha = t);
|
|
2150
|
+
}
|
|
2151
|
+
drawGrid(t = 1) {
|
|
2152
|
+
const e = this.ctx;
|
|
2153
|
+
e.globalAlpha = t, e.strokeStyle = this.theme.outerGrid, e.lineWidth = 0.4, T.TICKS.vals.forEach((o) => {
|
|
2154
|
+
this.line(this.proj(1, o, 0), this.proj(1, o, 1)), this.line(this.proj(1, 0, o), this.proj(1, 1, o));
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
drawZones(t) {
|
|
2158
|
+
const e = this.ctx, o = T.ZONE_VIS;
|
|
2159
|
+
[...this.zone].sort(
|
|
2160
|
+
(s, i) => this.proj((s.x0 + s.x1) / 2, (s.y0 + s.y1) / 2, (s.z0 + s.z1) / 2).depth - this.proj((i.x0 + i.x1) / 2, (i.y0 + i.y1) / 2, (i.z0 + i.z1) / 2).depth
|
|
2161
|
+
).forEach((s) => {
|
|
2162
|
+
e.globalAlpha = t;
|
|
2163
|
+
const i = s.fillAlpha !== void 0 ? s.fillAlpha : o.fillAlpha;
|
|
2164
|
+
if (this.box(s.x0, s.x1, s.y0, s.y1, s.z0, s.z1, s.fillBase + i + ")", s.edgeStr), t > 0.05) {
|
|
2165
|
+
e.globalAlpha = t * o.labelAlpha;
|
|
2166
|
+
const n = this.proj((s.x0 + s.x1) / 2, (s.y0 + s.y1) / 2, (s.z0 + s.z1) / 2);
|
|
2167
|
+
e.font = s.labelFontStr || this.getFont("zone"), e.textAlign = "center", e.fillStyle = s.labelColor, e.fillText(s.name, n.x, n.y);
|
|
2168
|
+
}
|
|
2169
|
+
e.globalAlpha = t;
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
drawAxes(t = 1) {
|
|
2173
|
+
const e = this.ctx;
|
|
2174
|
+
e.globalAlpha = t;
|
|
2175
|
+
const o = this.opts.axis;
|
|
2176
|
+
o.show && [
|
|
2177
|
+
{ from: [0, 0, 0], to: [1.18, 0, 0], lp: [1.22, 0, 0], ...o.data.x },
|
|
2178
|
+
{ from: [0, 0, 0], to: [0, 1.18, 0], lp: [0, 1.22, 0], ...o.data.y },
|
|
2179
|
+
{ from: [0, 0, 0], to: [0, 0, 1.22], lp: [0, 0, 1.28], ...o.data.z }
|
|
2180
|
+
].forEach((s) => {
|
|
2181
|
+
var l;
|
|
2182
|
+
const i = this.proj(...s.from), n = this.proj(...s.to);
|
|
2183
|
+
if (e.beginPath(), e.moveTo(i.x, i.y), e.lineTo(n.x, n.y), e.strokeStyle = s.color, e.lineWidth = o.lineStyle.lineWidth, e.stroke(), o.lineStyle.arrowSize > 0) {
|
|
2184
|
+
const h = n.x - i.x, a = n.y - i.y, r = Math.hypot(h, a) || 1, c = h / r, d = a / r, f = o.lineStyle.arrowSize;
|
|
2185
|
+
e.beginPath(), e.moveTo(n.x, n.y), e.lineTo(n.x - c * f + d * (f / 2.25), n.y - d * f - c * (f / 2.25)), e.lineTo(n.x - c * f - d * (f / 2.25), n.y - d * f + c * (f / 2.25)), e.closePath(), e.fillStyle = s.color, e.fill();
|
|
2186
|
+
}
|
|
2187
|
+
if (o.labelStyle.show) {
|
|
2188
|
+
const h = n.x - i.x, a = n.y - i.y, r = Math.hypot(h, a) || 1, c = n.x + h / r * o.labelStyle.distance, d = n.y + a / r * o.labelStyle.distance;
|
|
2189
|
+
e.fillStyle = s.color, e.font = `${o.labelStyle.fontWeight} ${o.labelStyle.fontSize}px ${((l = this.opts.textStyle) == null ? void 0 : l.fontFamily) || T.TEXT_STYLE.fontFamily}`, e.textAlign = "center", e.textBaseline = "middle", e.fillText(s.name, c, d);
|
|
2190
|
+
}
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
drawTickLabels(t = 1) {
|
|
2194
|
+
const e = this.ctx;
|
|
2195
|
+
e.globalAlpha = t;
|
|
2196
|
+
const { vals: o, labels: s } = T.TICKS, i = this.theme.tickAlpha;
|
|
2197
|
+
e.font = this.getFont("tick"), o.forEach((n, l) => {
|
|
2198
|
+
let h = this.proj(n, 0, 0);
|
|
2199
|
+
e.fillStyle = T.AXES.x.color + i, e.textAlign = "center", e.fillText(s[l], h.x, h.y + 13), h = this.proj(0, n, 0), e.fillStyle = T.AXES.y.color + i, e.textAlign = "left", e.fillText(s[l], h.x + 7, h.y + 4), h = this.proj(0, 0, n), e.fillStyle = T.AXES.z.color + i, e.textAlign = "right", e.fillText(s[l], h.x - 7, h.y + 4);
|
|
2200
|
+
}), e.textAlign = "center";
|
|
2201
|
+
}
|
|
2202
|
+
drawPoint3D(t, e) {
|
|
2203
|
+
const o = F.realToU(t.position.x), s = F.realToU(t.position.y), i = F.realToU(t.position.z);
|
|
2204
|
+
this.drawPointAt(this.proj(o, s, i), t, e);
|
|
2205
|
+
}
|
|
2206
|
+
drawPointAt(t, e, o = 1) {
|
|
2207
|
+
var p, m;
|
|
2208
|
+
const s = this.ctx, i = T.POINT, n = t.x ?? t.cx, l = t.y ?? t.cy, h = ((p = e.itemStyle) == null ? void 0 : p.radius) ?? i.defaultSize, a = ((m = e.itemStyle) == null ? void 0 : m.color) ?? e.color, r = e.name, c = e.textStyle || {}, d = e.glowStyle !== void 0, f = e.glowStyle || {};
|
|
2209
|
+
if (d && f.show !== !1) {
|
|
2210
|
+
let y = [], u = 3, b = f.glowDistance !== void 0 ? f.glowDistance : 4;
|
|
2211
|
+
const w = f.color ?? a;
|
|
2212
|
+
if (Array.isArray(f.colors) && f.colors.length > 0)
|
|
2213
|
+
y = f.colors, u = y.length;
|
|
2214
|
+
else {
|
|
2215
|
+
const S = f.startColor || _t(w, 0.15), x = f.endColor || "transparent";
|
|
2216
|
+
u = f.count !== void 0 ? parseInt(f.count, 10) : 3, y = [];
|
|
2217
|
+
for (let C = 0; C < u; C++) {
|
|
2218
|
+
const z = u > 1 ? C / (u - 1) : 0;
|
|
2219
|
+
y.push(Ht(S, x, z));
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
for (let S = u - 1; S >= 0; S--) {
|
|
2223
|
+
const x = y[S];
|
|
2224
|
+
s.save(), s.fillStyle = x, s.beginPath(), s.arc(n, l, h + (S + 1) * b, 0, Math.PI * 2), s.fill(), s.restore();
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
s.globalAlpha = o, s.beginPath(), s.arc(n, l, h, 0, Math.PI * 2);
|
|
2228
|
+
const g = s.createRadialGradient(n - h * i.hlOffset, l - h * i.hlOffset, 1, n, l, h);
|
|
2229
|
+
g.addColorStop(0, "rgba(255,255,255,0.9)"), g.addColorStop(0.4, a + "ee"), g.addColorStop(1, a + "88"), s.fillStyle = g, s.fill(), s.strokeStyle = this.theme.pointStroke, s.lineWidth = 1.3, s.stroke(), s.restore(), r && (s.font = c.bold ? `bold ${c.fontSize || 12}px ${c.fontFamily || "sans-serif"}` : `${c.fontSize || 12}px ${c.fontFamily || "sans-serif"}`, s.fillStyle = c.color ?? e.color, s.textAlign = "left", s.fillText(r, n + h + 3, l - 2));
|
|
2230
|
+
}
|
|
2231
|
+
draw2DOverlay(t, e, o) {
|
|
2232
|
+
var z;
|
|
2233
|
+
const s = T.PLANES[t];
|
|
2234
|
+
if (!s) return;
|
|
2235
|
+
const i = this.ctx, n = this.theme, l = this.opts.plane2D, h = this.opts.axis.data;
|
|
2236
|
+
i.save(), i.globalAlpha = e;
|
|
2237
|
+
let a = this.offsetX, r = this.offsetY, c = this.offsetX + this.renderW, d = this.offsetY + this.renderH;
|
|
2238
|
+
const f = this.opts.grid || {};
|
|
2239
|
+
if (!(f.left !== void 0 || f.right !== void 0 || f.top !== void 0 || f.bottom !== void 0)) {
|
|
2240
|
+
const _ = T.PAD2D || { l: 70, r: 30, t: 50, b: 60 };
|
|
2241
|
+
a = _.l, r = _.t, c = this.W - _.r, d = this.H - _.b;
|
|
2242
|
+
}
|
|
2243
|
+
const p = c - a, m = d - r, y = Math.min(p, m), u = a + p / 2, b = r + m / 2;
|
|
2244
|
+
a = u - y / 2, c = u + y / 2, r = b - y / 2, d = b + y / 2;
|
|
2245
|
+
const { vals: w, labels: S } = T.TICKS, x = (_, A) => ({
|
|
2246
|
+
cx: a + _ * (c - a),
|
|
2247
|
+
cy: d - A * (d - r)
|
|
2248
|
+
}), C = this.themeName ?? "dark";
|
|
2249
|
+
if (l.grid.show && (i.fillStyle = l.grid.backgroundColor ?? s.bgColor[C] ?? s.bgColor.dark, i.fillRect(a, r, c - a, d - r)), l.zone.show && this.zone.forEach((_) => {
|
|
2250
|
+
const [A, Y] = [_[s.hAxis + "0"], _[s.hAxis + "1"]], [P, k] = [_[s.vAxis + "0"], _[s.vAxis + "1"]], M = x(A, P), L = x(Y, k), Q = L.cx - M.cx, N = M.cy - L.cy, st = _.fillAlpha !== void 0 ? _.fillAlpha : l.zone.alpha;
|
|
2251
|
+
i.fillStyle = _.fillBase + st + ")", i.fillRect(M.cx, L.cy, Q, N), l.zone.borderStyle.show && (i.strokeStyle = _.edgeStr, i.lineWidth = l.zone.borderStyle.lineWidth, i.strokeRect(M.cx, L.cy, Q, N));
|
|
2252
|
+
const Z = (M.cx + L.cx) / 2, tt = (M.cy + L.cy) / 2;
|
|
2253
|
+
i.font = _.labelFontStr || this.getFont("zone"), i.textAlign = "center", i.fillStyle = _.labelColor, i.fillText(_.name, Z, tt + 4);
|
|
2254
|
+
}), l.grid.show && (i.strokeStyle = l.grid.lineStyle.color || n.plane2dGrid, i.lineWidth = l.grid.lineStyle.lineWidth, i.globalAlpha = e * l.grid.lineStyle.alpha, l.grid.lineStyle.type === "dashed" ? i.setLineDash([4, 4]) : l.grid.lineStyle.type === "dotted" ? i.setLineDash([2, 2]) : i.setLineDash([]), w.forEach((_) => {
|
|
2255
|
+
const A = x(_, 0).cx, Y = x(0, _).cy;
|
|
2256
|
+
i.beginPath(), i.moveTo(A, r), i.lineTo(A, d), i.stroke(), i.beginPath(), i.moveTo(a, Y), i.lineTo(c, Y), i.stroke();
|
|
2257
|
+
}), i.setLineDash([]), i.globalAlpha = e), l.axis.show) {
|
|
2258
|
+
const _ = h[s.hAxis].color, A = h[s.vAxis].color;
|
|
2259
|
+
i.lineWidth = l.axis.lineStyle.lineWidth, i.globalAlpha = e * l.axis.lineStyle.alpha, i.strokeStyle = _, i.beginPath(), i.moveTo(a, d), i.lineTo(c + 10, d), i.stroke(), i.strokeStyle = A, i.beginPath(), i.moveTo(a, d), i.lineTo(a, r - 10), i.stroke(), i.globalAlpha = e;
|
|
2260
|
+
const Y = ((z = this.opts.textStyle) == null ? void 0 : z.fontFamily) || T.TEXT_STYLE.fontFamily;
|
|
2261
|
+
if (l.axis.tickLabelStyle.show) {
|
|
2262
|
+
i.font = `${l.axis.tickLabelStyle.fontSize}px ${Y}`;
|
|
2263
|
+
const P = l.axis.tickLabelStyle.distance;
|
|
2264
|
+
w.forEach((k, M) => {
|
|
2265
|
+
i.fillStyle = _, i.textAlign = "center", i.fillText(S[M], x(k, 0).cx, d + P + l.axis.tickLabelStyle.fontSize / 2), i.fillStyle = A, i.textAlign = "right", i.fillText(S[M], a - P + 2, x(0, k).cy + l.axis.tickLabelStyle.fontSize / 3);
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
if (l.axis.labelStyle.show) {
|
|
2269
|
+
i.font = `bold ${l.axis.labelStyle.fontSize}px ${Y}`;
|
|
2270
|
+
const P = l.axis.labelStyle.distance;
|
|
2271
|
+
i.textAlign = "center", i.fillStyle = _, i.fillText(h[s.hAxis].name, (a + c) / 2, d + P + l.axis.labelStyle.fontSize), i.save(), i.translate(a - P - l.axis.labelStyle.fontSize, (r + d) / 2), i.rotate(-Math.PI / 2), i.fillStyle = A, i.fillText(h[s.vAxis].name, 0, 0), i.restore();
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
o.forEach((_) => {
|
|
2275
|
+
const A = x(F.realToU(_.position[s.hAxis]), F.realToU(_.position[s.vAxis]));
|
|
2276
|
+
this.drawPointAt(A, _, e);
|
|
2277
|
+
}), i.restore();
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
const ot = class ot {
|
|
2281
|
+
/**
|
|
2282
|
+
* 构造函数
|
|
2283
|
+
* @param {Object} opts 配置项
|
|
2284
|
+
* @param {string|HTMLElement} opts.container 容器选择器或元素
|
|
2285
|
+
* @param {string} opts.initialView 初始视角 (默认 '3d')
|
|
2286
|
+
* @param {string} opts.theme 初始主题 ('dark' | 'light')
|
|
2287
|
+
*/
|
|
2288
|
+
constructor(t = {}) {
|
|
2289
|
+
this.series = [], this.opts = t, this.cam = {
|
|
2290
|
+
rotX: T.CAM.elev,
|
|
2291
|
+
rotY: T.CAM.yaw,
|
|
2292
|
+
zoomFactor: 1
|
|
2293
|
+
}, this.view = t.initialView ?? "3d", this.layout = t.layout ?? "fill", this.drag = { active: !1, lastX: 0, lastY: 0 }, this.tween = {
|
|
2294
|
+
from: { rotX: 0, rotY: 0 },
|
|
2295
|
+
to: { rotX: 0, rotY: 0 },
|
|
2296
|
+
t: 1
|
|
2297
|
+
}, this.animId = null, this.pinchDist = null, this.events = { viewChange: null, dataChange: null };
|
|
2298
|
+
const e = typeof t.container == "string" ? document.querySelector(t.container) : t.container;
|
|
2299
|
+
e && this.buildDOM(e), this.renderer = new It(this.ctx, this.W, this.H, t), this.bindEvents(), this.applyView(), this.render();
|
|
2300
|
+
}
|
|
2301
|
+
/** 静态工厂方法,简化初始化调用 */
|
|
2302
|
+
static init(t, e = {}) {
|
|
2303
|
+
return new ot({ container: t, ...e });
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2306
|
+
* 设置图表配置(主要用于更新数据或视角)
|
|
2307
|
+
* @param {Object} opt
|
|
2308
|
+
*/
|
|
2309
|
+
setOption(t) {
|
|
2310
|
+
if (t) {
|
|
2311
|
+
if (this.opts = { ...this.opts, ...t }, (t.zone || t.textStyle || t.theme || t.wallColors || t.layout !== void 0 || t.grid !== void 0 || t.axis !== void 0 || t.wall !== void 0 || t.plane2D !== void 0) && this.renderer.updateOptions({
|
|
2312
|
+
zone: this.opts.zone,
|
|
2313
|
+
textStyle: this.opts.textStyle,
|
|
2314
|
+
theme: this.opts.theme,
|
|
2315
|
+
wallColors: this.opts.wallColors,
|
|
2316
|
+
layout: this.opts.layout,
|
|
2317
|
+
grid: this.opts.grid,
|
|
2318
|
+
axis: this.opts.axis,
|
|
2319
|
+
wall: this.opts.wall,
|
|
2320
|
+
plane2D: this.opts.plane2D
|
|
2321
|
+
}), t.series !== void 0) {
|
|
2322
|
+
const e = Array.isArray(t.series) ? t.series : [t.series];
|
|
2323
|
+
this.series = e.map((o) => ({
|
|
2324
|
+
name: o.name ?? "",
|
|
2325
|
+
color: o.color ?? T.POINT.defaultColor,
|
|
2326
|
+
itemStyle: o.itemStyle ?? {},
|
|
2327
|
+
textStyle: o.textStyle ?? {},
|
|
2328
|
+
glowStyle: o.glowStyle,
|
|
2329
|
+
position: o.position ?? { x: 0, y: 0, z: 0 }
|
|
2330
|
+
})), this.emit("dataChange", [...this.series]), this.render();
|
|
2331
|
+
}
|
|
2332
|
+
t.view && t.view !== this.view && this.setView(t.view);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* 切换视图角度(包含平滑补间动画)
|
|
2337
|
+
* @param {string} v 视图 ID
|
|
2338
|
+
*/
|
|
2339
|
+
setView(t) {
|
|
2340
|
+
if (!ot.VIEWS[t]) return;
|
|
2341
|
+
this.view = t, cancelAnimationFrame(this.animId);
|
|
2342
|
+
const e = this.getViewTarget(t);
|
|
2343
|
+
this.tween = {
|
|
2344
|
+
from: { rotX: this.cam.rotX, rotY: this.cam.rotY },
|
|
2345
|
+
to: e,
|
|
2346
|
+
t: 0
|
|
2347
|
+
}, this.cv.style.cursor = t === "3d" ? "grab" : "default", this.runTween(), this.emit("viewChange", t);
|
|
2348
|
+
}
|
|
2349
|
+
/** 切换主题渲染 */
|
|
2350
|
+
setTheme(t) {
|
|
2351
|
+
this.renderer.setTheme(t), this.render();
|
|
2352
|
+
}
|
|
2353
|
+
/**
|
|
2354
|
+
* 诊断逻辑:根据输入的三比值 X, Y, Z 判断所属故障区域
|
|
2355
|
+
* @returns {Array} 匹配到的诊断区域信息数组
|
|
2356
|
+
*/
|
|
2357
|
+
diagnose(t, e, o) {
|
|
2358
|
+
if (!this.renderer) return [{ ...this.opts.unknown ?? T.UNKNOWN }];
|
|
2359
|
+
const s = this.renderer.zone.filter((i) => t >= i.p1[0] && t < i.p2[0] && e >= i.p1[1] && e < i.p2[1] && o >= i.p1[2] && o < i.p2[2]).map((i) => ({ name: i.name, title: i.title, color: i.edgeColor || i.fill }));
|
|
2360
|
+
return s.length ? s : [{ ...this.opts.unknown ?? T.UNKNOWN }];
|
|
2361
|
+
}
|
|
2362
|
+
on(t, e) {
|
|
2363
|
+
t in this.events && (this.events[t] = e);
|
|
2364
|
+
}
|
|
2365
|
+
emit(t, ...e) {
|
|
2366
|
+
typeof this.events[t] == "function" && this.events[t](...e);
|
|
2367
|
+
}
|
|
2368
|
+
render() {
|
|
2369
|
+
!this.renderer || !this.ctx || this.renderer.draw(this.cam, this.series, this.view, this.flatness());
|
|
2370
|
+
}
|
|
2371
|
+
destroy() {
|
|
2372
|
+
var t, e;
|
|
2373
|
+
cancelAnimationFrame(this.animId), (t = this.resizeObs) == null || t.disconnect(), (e = this.cv) == null || e.remove(), window.removeEventListener("mousemove", this.onMouseMove), window.removeEventListener("mouseup", this.onMouseUp);
|
|
2374
|
+
}
|
|
2375
|
+
// 内部方法
|
|
2376
|
+
getViewTarget(t) {
|
|
2377
|
+
const e = ot.VIEWS[t];
|
|
2378
|
+
return {
|
|
2379
|
+
rotX: typeof e.rotX == "function" ? e.rotX() : e.rotX,
|
|
2380
|
+
rotY: typeof e.rotY == "function" ? e.rotY() : e.rotY
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
applyView() {
|
|
2384
|
+
if (this.view !== "3d") {
|
|
2385
|
+
const t = this.getViewTarget(this.view);
|
|
2386
|
+
this.cam.rotX = t.rotX, this.cam.rotY = t.rotY, this.cv.style.cursor = "default";
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
flatness() {
|
|
2390
|
+
if (this.view === "3d") return 0;
|
|
2391
|
+
const t = this.getViewTarget(this.view);
|
|
2392
|
+
return Math.max(0, 1 - (Math.abs(this.cam.rotX - t.rotX) + Math.abs(this.cam.rotY - t.rotY)) / 30);
|
|
2393
|
+
}
|
|
2394
|
+
runTween() {
|
|
2395
|
+
this.tween.t = Math.min(1, this.tween.t + T.TWEEN.step);
|
|
2396
|
+
const t = ((e) => e < 0.5 ? 2 * e * e : 1 - Math.pow(-2 * e + 2, 2) / 2)(this.tween.t);
|
|
2397
|
+
this.cam.rotX = this.tween.from.rotX + (this.tween.to.rotX - this.tween.from.rotX) * t, this.cam.rotY = this.tween.from.rotY + (this.tween.to.rotY - this.tween.from.rotY) * t, this.render(), this.tween.t < 1 && (this.animId = requestAnimationFrame(() => this.runTween()));
|
|
2398
|
+
}
|
|
2399
|
+
buildDOM(t) {
|
|
2400
|
+
const e = t.getBoundingClientRect();
|
|
2401
|
+
this.W = e.width > 0 ? Math.round(e.width) : 600, this.H = e.height > 0 ? Math.round(e.height) : 540, this.cv = document.createElement("canvas"), this.cv.className = "trc-canvas", this.cv.width = this.W, this.cv.height = this.H, this.cv.style.cssText = "width:100%;height:100%;display:block;touch-action:none;", this.ctx = this.cv.getContext("2d"), t.appendChild(this.cv), this.resizeObs = new ResizeObserver((o) => {
|
|
2402
|
+
var n;
|
|
2403
|
+
const { width: s, height: i } = o[0].contentRect;
|
|
2404
|
+
s > 0 && i > 0 && (this.W = Math.round(s), this.H = Math.round(i), this.cv.width = this.W, this.cv.height = this.H, (n = this.renderer) == null || n.resize(this.W, this.H), this.render());
|
|
2405
|
+
}), this.resizeObs.observe(t);
|
|
2406
|
+
}
|
|
2407
|
+
bindEvents() {
|
|
2408
|
+
const t = T.DRAG, e = this.cv;
|
|
2409
|
+
e.addEventListener("mousedown", (o) => {
|
|
2410
|
+
this.view === "3d" && (this.drag = { active: !0, lastX: o.clientX, lastY: o.clientY }, e.style.cursor = "grabbing", cancelAnimationFrame(this.animId));
|
|
2411
|
+
}), this.onMouseMove = (o) => {
|
|
2412
|
+
!this.drag.active || this.view !== "3d" || (this.cam.rotY += (o.clientX - this.drag.lastX) * t.rotYSens, this.cam.rotX = Math.max(
|
|
2413
|
+
t.rotXMin,
|
|
2414
|
+
Math.min(t.rotXMax, this.cam.rotX + (o.clientY - this.drag.lastY) * t.rotXSens)
|
|
2415
|
+
), this.drag.lastX = o.clientX, this.drag.lastY = o.clientY, this.render());
|
|
2416
|
+
}, this.onMouseUp = () => {
|
|
2417
|
+
this.drag.active = !1, this.view === "3d" && (e.style.cursor = "grab");
|
|
2418
|
+
}, window.addEventListener("mousemove", this.onMouseMove), window.addEventListener("mouseup", this.onMouseUp), e.addEventListener(
|
|
2419
|
+
"touchstart",
|
|
2420
|
+
(o) => {
|
|
2421
|
+
this.view === "3d" && (this.drag = {
|
|
2422
|
+
active: !0,
|
|
2423
|
+
lastX: o.touches[0].clientX,
|
|
2424
|
+
lastY: o.touches[0].clientY
|
|
2425
|
+
}, o.touches.length === 2 && (this.pinchDist = Math.hypot(
|
|
2426
|
+
o.touches[0].clientX - o.touches[1].clientX,
|
|
2427
|
+
o.touches[0].clientY - o.touches[1].clientY
|
|
2428
|
+
)));
|
|
2429
|
+
},
|
|
2430
|
+
{ passive: !0 }
|
|
2431
|
+
), e.addEventListener(
|
|
2432
|
+
"touchmove",
|
|
2433
|
+
(o) => {
|
|
2434
|
+
if (this.view === "3d")
|
|
2435
|
+
if (o.touches.length === 2 && this.pinchDist !== null) {
|
|
2436
|
+
const s = Math.hypot(
|
|
2437
|
+
o.touches[0].clientX - o.touches[1].clientX,
|
|
2438
|
+
o.touches[0].clientY - o.touches[1].clientY
|
|
2439
|
+
);
|
|
2440
|
+
this.cam.zoomFactor = Math.max(
|
|
2441
|
+
t.zoomMin,
|
|
2442
|
+
Math.min(t.zoomMax, this.cam.zoomFactor * s / this.pinchDist)
|
|
2443
|
+
), this.pinchDist = s, this.render();
|
|
2444
|
+
} else this.drag.active && o.touches.length === 1 && (this.cam.rotY += (o.touches[0].clientX - this.drag.lastX) * t.rotYSens, this.cam.rotX = Math.max(
|
|
2445
|
+
t.rotXMin,
|
|
2446
|
+
Math.min(t.rotXMax, this.cam.rotX + (o.touches[0].clientY - this.drag.lastY) * t.rotXSens)
|
|
2447
|
+
), this.drag.lastX = o.touches[0].clientX, this.drag.lastY = o.touches[0].clientY, this.render());
|
|
2448
|
+
},
|
|
2449
|
+
{ passive: !0 }
|
|
2450
|
+
), e.addEventListener(
|
|
2451
|
+
"touchend",
|
|
2452
|
+
(o) => {
|
|
2453
|
+
o.touches.length < 2 && (this.pinchDist = null), o.touches.length === 0 && (this.drag.active = !1);
|
|
2454
|
+
},
|
|
2455
|
+
{ passive: !0 }
|
|
2456
|
+
), e.addEventListener(
|
|
2457
|
+
"wheel",
|
|
2458
|
+
(o) => {
|
|
2459
|
+
this.view === "3d" && (o.preventDefault(), this.cam.zoomFactor = Math.max(
|
|
2460
|
+
t.zoomMin,
|
|
2461
|
+
Math.min(t.zoomMax, this.cam.zoomFactor * (o.deltaY > 0 ? t.zoomOut : t.zoomIn))
|
|
2462
|
+
), this.render());
|
|
2463
|
+
},
|
|
2464
|
+
{ passive: !1 }
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
};
|
|
2468
|
+
K(ot, "VIEWS", {
|
|
2469
|
+
"3d": { rotX: () => T.CAM.elev, rotY: () => T.CAM.yaw },
|
|
2470
|
+
xz: { rotX: 0, rotY: 0 },
|
|
2471
|
+
yz: { rotX: 0, rotY: 90 },
|
|
2472
|
+
xy: { rotX: 90, rotY: 0 }
|
|
2473
|
+
});
|
|
2474
|
+
let xt = ot;
|
|
2475
|
+
function ht(v) {
|
|
2476
|
+
const t = v.trim().toLowerCase();
|
|
2477
|
+
if (t.startsWith("#")) {
|
|
2478
|
+
let o = t.slice(1);
|
|
2479
|
+
o.length === 3 && (o = o[0] + o[0] + o[1] + o[1] + o[2] + o[2]);
|
|
2480
|
+
const s = parseInt(o.substring(0, 2), 16), i = parseInt(o.substring(2, 4), 16), n = parseInt(o.substring(4, 6), 16), l = o.length === 8 ? parseInt(o.substring(6, 8), 16) / 255 : 1;
|
|
2481
|
+
return [s, i, n, l];
|
|
2482
|
+
}
|
|
2483
|
+
const e = t.match(/rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\)/);
|
|
2484
|
+
return e ? [
|
|
2485
|
+
parseInt(e[1], 10),
|
|
2486
|
+
parseInt(e[2], 10),
|
|
2487
|
+
parseInt(e[3], 10),
|
|
2488
|
+
e[4] !== void 0 ? parseFloat(e[4]) : 1
|
|
2489
|
+
] : t === "transparent" ? [0, 0, 0, 0] : [255, 255, 255, 1];
|
|
2490
|
+
}
|
|
2491
|
+
function _t(v, t) {
|
|
2492
|
+
const [e, o, s, i] = ht(v);
|
|
2493
|
+
return `rgba(${e},${o},${s},${t !== void 0 ? t : i})`;
|
|
2494
|
+
}
|
|
2495
|
+
function Ht(v, t, e) {
|
|
2496
|
+
const [o, s, i, n] = ht(v), [l, h, a, r] = ht(t), c = Math.round(o + (l - o) * e), d = Math.round(s + (h - s) * e), f = Math.round(i + (a - i) * e), g = n + (r - n) * e;
|
|
2497
|
+
return `rgba(${c},${d},${f},${g})`;
|
|
2498
|
+
}
|
|
2499
|
+
const jt = {
|
|
2500
|
+
theme: "light",
|
|
2501
|
+
backgroundColor: "transparent",
|
|
2502
|
+
grid: {
|
|
2503
|
+
left: 72,
|
|
2504
|
+
right: 20,
|
|
2505
|
+
top: 20,
|
|
2506
|
+
bottom: 56,
|
|
2507
|
+
lineStyle: { type: "solid", width: 0.4, color: null },
|
|
2508
|
+
majorLineStyle: { type: "solid", width: 0.8, color: null }
|
|
2509
|
+
},
|
|
2510
|
+
zone: {
|
|
2511
|
+
data: Wt,
|
|
2512
|
+
labelStyle: { show: !0, fontSize: 12, borderRadius: 4 },
|
|
2513
|
+
borderStyle: { show: !0, stroke: 1, type: "solid", color: null }
|
|
2514
|
+
},
|
|
2515
|
+
xAxis: {
|
|
2516
|
+
title: "",
|
|
2517
|
+
min: -2,
|
|
2518
|
+
max: 3,
|
|
2519
|
+
// log10 range
|
|
2520
|
+
labelStyle: { fontSize: 11, fontWeight: "normal", offsetX: 0, offsetY: 0, color: null },
|
|
2521
|
+
lineStyle: { show: !0, stroke: 1.5, type: "solid", color: null },
|
|
2522
|
+
titleStyle: { fontSize: 13, color: null, fontWeight: "normal", align: "center", offsetX: 0, offsetY: 0 }
|
|
2523
|
+
},
|
|
2524
|
+
yAxis: {
|
|
2525
|
+
title: "",
|
|
2526
|
+
min: -3,
|
|
2527
|
+
max: 3,
|
|
2528
|
+
// log10 range
|
|
2529
|
+
labelStyle: { fontSize: 11, fontWeight: "normal", offsetX: 0, offsetY: 0, color: null },
|
|
2530
|
+
lineStyle: { show: !0, stroke: 1.5, type: "solid", color: null },
|
|
2531
|
+
titleStyle: { fontSize: 13, color: null, fontWeight: "normal", align: "center", offsetX: 0, offsetY: 0 }
|
|
2532
|
+
},
|
|
2533
|
+
tooltip: { show: !0, formatter: null },
|
|
2534
|
+
series: []
|
|
2535
|
+
}, Ct = {
|
|
2536
|
+
light: {
|
|
2537
|
+
backgroundColor: "#ffffff",
|
|
2538
|
+
text: "#333333",
|
|
2539
|
+
textSecondary: "#666666",
|
|
2540
|
+
gridLine: "rgba(0,0,0,0.12)",
|
|
2541
|
+
gridLineMajor: "rgba(0,0,0,0.22)",
|
|
2542
|
+
axisLine: "rgba(0,0,0,0.5)",
|
|
2543
|
+
zoneBorder: "rgba(0,0,0,0.35)",
|
|
2544
|
+
zoneLabelBg: "rgba(255,255,255,0.75)",
|
|
2545
|
+
tooltipBg: "rgba(255,255,255,0.95)",
|
|
2546
|
+
tooltipBorder: "rgba(0,0,0,0.15)",
|
|
2547
|
+
tooltipTextColor: "#222222"
|
|
2548
|
+
},
|
|
2549
|
+
dark: {
|
|
2550
|
+
backgroundColor: "#1a1a2e",
|
|
2551
|
+
text: "rgba(255,255,255,0.85)",
|
|
2552
|
+
textSecondary: "rgba(255,255,255,0.5)",
|
|
2553
|
+
gridLine: "rgba(255,255,255,0.08)",
|
|
2554
|
+
gridLineMajor: "rgba(255,255,255,0.18)",
|
|
2555
|
+
axisLine: "rgba(255,255,255,0.55)",
|
|
2556
|
+
zoneBorder: "rgba(255,255,255,0.3)",
|
|
2557
|
+
zoneLabelBg: "rgba(0,0,0,0.5)",
|
|
2558
|
+
tooltipBg: "rgba(20,20,40,0.95)",
|
|
2559
|
+
tooltipBorder: "rgba(255,255,255,0.15)",
|
|
2560
|
+
tooltipTextColor: "rgba(255,255,255,0.9)"
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
function Tt(v) {
|
|
2564
|
+
const t = Math.round(v);
|
|
2565
|
+
return "10" + String(t).split("").map(
|
|
2566
|
+
(o) => ({ "-": "⁻", 0: "⁰", 1: "¹", 2: "²", 3: "³", 4: "⁴", 5: "⁵", 6: "⁶", 7: "⁷", 8: "⁸", 9: "⁹" })[o] || o
|
|
2567
|
+
).join("");
|
|
2568
|
+
}
|
|
2569
|
+
class zt {
|
|
2570
|
+
constructor(t, e = {}) {
|
|
2571
|
+
if (typeof t == "string" && (t = document.querySelector(t)), !t) throw new Error("[ETRAChart] Container element not found");
|
|
2572
|
+
this._container = t, this._opt = E(E({}, jt), e), this._bus = new At(), this._initDOM(), this._zoom = new ut(this._opt.zoom || {}), this._tooltipRenderer = new yt(this._container, this._theme), this._hoverZone = null, this._hoverPoint = null, typeof ResizeObserver < "u" && (this._ro = new ResizeObserver(() => this.resize()), this._ro.observe(this._container)), this._onWheel = (s) => {
|
|
2573
|
+
s.preventDefault();
|
|
2574
|
+
const i = this._canvas.getBoundingClientRect(), n = s.clientX - i.left, l = s.clientY - i.top;
|
|
2575
|
+
this._zoom.onWheel(s.deltaY, n, l), this._bus.emit("zoom", this._zoom.state), this._render();
|
|
2576
|
+
}, this._onDown = (s) => {
|
|
2577
|
+
s.button === 0 && (this._zoom.startDrag(s.clientX, s.clientY), this._canvas.style.cursor = "grabbing");
|
|
2578
|
+
}, this._onMove = (s) => {
|
|
2579
|
+
const i = this._canvas.getBoundingClientRect();
|
|
2580
|
+
if (this._zoom.moveDrag(s.clientX, s.clientY, 1)) {
|
|
2581
|
+
this._render();
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
if (!this._coordMap || !this._drawnPoints) return;
|
|
2585
|
+
const n = s.clientX - i.left, l = s.clientY - i.top, { panX: h, panY: a, zoom: r } = this._zoom, c = (n - h) / r, d = (l - a) / r;
|
|
2586
|
+
let f = null, g = 1 / 0;
|
|
2587
|
+
for (const p of this._drawnPoints) {
|
|
2588
|
+
const m = Math.hypot(c - p.px, d - p.py), y = p.series.size || 6;
|
|
2589
|
+
m < y * 2 + 5 && m < g && (g = m, f = p);
|
|
2590
|
+
}
|
|
2591
|
+
f !== this._hoverPoint && (this._hoverPoint = f, this._render(), f ? (this._canvas.style.cursor = "pointer", this._bus.emit("hover", f)) : (this._canvas.style.cursor = "default", this._bus.emit("hover", null)));
|
|
2592
|
+
}, this._onUp = () => {
|
|
2593
|
+
this._zoom.endDrag() && (this._canvas.style.cursor = this._hoverPoint ? "pointer" : "default", this._bus.emit("zoom", this._zoom.state));
|
|
2594
|
+
}, this._onLeave = () => {
|
|
2595
|
+
this._hoverPoint && (this._hoverPoint = null, this._canvas.style.cursor = "default", this._render(), this._bus.emit("hover", null));
|
|
2596
|
+
}, this._onDbl = () => {
|
|
2597
|
+
this._zoom.reset(), this._bus.emit("zoom", this._zoom.state), this._render();
|
|
2598
|
+
};
|
|
2599
|
+
const o = this._canvas;
|
|
2600
|
+
o.addEventListener("wheel", this._onWheel, { passive: !1 }), o.addEventListener("mousedown", this._onDown), o.addEventListener("mousemove", this._onMove), o.addEventListener("mouseleave", this._onLeave), o.addEventListener("dblclick", this._onDbl), window.addEventListener("mouseup", this._onUp), this._render();
|
|
2601
|
+
}
|
|
2602
|
+
static init(t, e) {
|
|
2603
|
+
return new zt(t, e);
|
|
2604
|
+
}
|
|
2605
|
+
get _theme() {
|
|
2606
|
+
return Ct[this._opt.theme] || Ct.light;
|
|
2607
|
+
}
|
|
2608
|
+
_initDOM() {
|
|
2609
|
+
this._container.style.position = "relative", this._container.style.overflow = "hidden", this._canvas = document.createElement("canvas"), this._canvas.style.display = "block", this._canvas.style.width = "100%", this._canvas.style.height = "100%", this._container.appendChild(this._canvas), this._ctx = this._canvas.getContext("2d");
|
|
2610
|
+
}
|
|
2611
|
+
setOption(t) {
|
|
2612
|
+
return this._opt = E(this._opt, t), this._render(), this;
|
|
2613
|
+
}
|
|
2614
|
+
on(t, e) {
|
|
2615
|
+
return this._bus.on(t, e), this;
|
|
2616
|
+
}
|
|
2617
|
+
off(t, e) {
|
|
2618
|
+
return this._bus.off(t, e), this;
|
|
2619
|
+
}
|
|
2620
|
+
resize() {
|
|
2621
|
+
return this._render(), this;
|
|
2622
|
+
}
|
|
2623
|
+
dispose() {
|
|
2624
|
+
this._ro && this._ro.disconnect(), this._tooltipRenderer.dispose(), this._canvas && this._canvas.parentNode && this._canvas.parentNode.removeChild(this._canvas), this._bus.dispose(), window.removeEventListener("mouseup", this._onUp);
|
|
2625
|
+
}
|
|
2626
|
+
_dims() {
|
|
2627
|
+
const t = this._container.getBoundingClientRect(), e = t.width || 600, o = t.height || 400, s = window.devicePixelRatio || 1;
|
|
2628
|
+
(this._canvas.width !== Math.round(e * s) || this._canvas.height !== Math.round(o * s)) && (this._canvas.width = Math.round(e * s), this._canvas.height = Math.round(o * s));
|
|
2629
|
+
const i = ct(e, o, this._opt.grid, 0);
|
|
2630
|
+
return { W: e, H: o, box: i, dpr: s };
|
|
2631
|
+
}
|
|
2632
|
+
_render() {
|
|
2633
|
+
const { W: t, H: e, box: o, dpr: s } = this._dims(), i = this._ctx;
|
|
2634
|
+
if (!i) return;
|
|
2635
|
+
const n = this._theme, l = this._opt;
|
|
2636
|
+
i.setTransform(s, 0, 0, s, 0, 0), i.clearRect(0, 0, t, e);
|
|
2637
|
+
const h = l.backgroundColor || n.backgroundColor || "transparent";
|
|
2638
|
+
h !== "transparent" && (i.fillStyle = h, i.fillRect(0, 0, t, e));
|
|
2639
|
+
const a = l.xAxis.min, r = l.xAxis.max, c = l.yAxis.min, d = l.yAxis.max, f = o.availW, g = o.availH, p = o.left, m = o.top, y = (w) => p + (w - a) / (r - a) * f, u = (w) => m + (d - w) / (d - c) * g;
|
|
2640
|
+
this._coordMap = { xMin: a, xMax: r, yMin: c, yMax: d, plotW: f, plotH: g, gL: p, gT: m, toPixX: y, toPixY: u }, i.save(), this._zoom.applyTransform(i), this._drawGrid(i, y, u, n, l), this._drawZones(i, y, u, n, l);
|
|
2641
|
+
const b = this._drawSeries(i, y, u, n, l);
|
|
2642
|
+
if (this._drawAxes(i, y, u, n, l, t, e, p, m, f, g), i.restore(), l.tooltip.show && this._hoverPoint) {
|
|
2643
|
+
const w = this._hoverPoint, S = y(w.logX) * this._zoom.zoom + this._zoom.panX, x = u(w.logY) * this._zoom.zoom + this._zoom.panY;
|
|
2644
|
+
let C = "";
|
|
2645
|
+
typeof l.tooltip.formatter == "function" ? C = l.tooltip.formatter({
|
|
2646
|
+
series: w.series,
|
|
2647
|
+
logX: w.logX,
|
|
2648
|
+
logY: w.logY,
|
|
2649
|
+
rawX: Math.pow(10, w.logX),
|
|
2650
|
+
rawY: Math.pow(10, w.logY),
|
|
2651
|
+
zone: w.zone
|
|
2652
|
+
}) : C = this._defaultTooltipHTML(w, l), this._tooltipRenderer.show(C, S, x, l.tooltip, n, w.zone);
|
|
2653
|
+
} else
|
|
2654
|
+
this._tooltipRenderer.hide();
|
|
2655
|
+
b.length > 0 && this._bus.emit("diagnose", b);
|
|
2656
|
+
}
|
|
2657
|
+
_drawGrid(t, e, o, s, i) {
|
|
2658
|
+
const n = i.xAxis.min, l = i.xAxis.max, h = i.yAxis.min, a = i.yAxis.max, r = this._coordMap.gL, c = this._coordMap.gT, d = this._coordMap.plotW, f = this._coordMap.plotH, g = i.grid.lineStyle || {}, p = g.color || s.gridLineMajor, m = s.gridLine;
|
|
2659
|
+
for (let y = Math.ceil(n); y <= Math.floor(l); y++) {
|
|
2660
|
+
const u = e(y);
|
|
2661
|
+
t.beginPath(), t.moveTo(u, c), t.lineTo(u, c + f), t.strokeStyle = p, t.lineWidth = g.width || 0.4, g.type === "dashed" ? t.setLineDash([4, 3]) : t.setLineDash([]), t.stroke(), t.setLineDash([]), t.strokeStyle = m, t.lineWidth = 0.25;
|
|
2662
|
+
for (let b = 2; b <= 9; b++) {
|
|
2663
|
+
const w = y + Math.log10(b);
|
|
2664
|
+
if (w > l) break;
|
|
2665
|
+
const S = e(w);
|
|
2666
|
+
t.beginPath(), t.moveTo(S, c), t.lineTo(S, c + f), t.stroke();
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
for (let y = Math.ceil(h); y <= Math.floor(a); y++) {
|
|
2670
|
+
const u = o(y);
|
|
2671
|
+
t.beginPath(), t.moveTo(r, u), t.lineTo(r + d, u), t.strokeStyle = p, t.lineWidth = g.width || 0.4, g.type === "dashed" ? t.setLineDash([4, 3]) : t.setLineDash([]), t.stroke(), t.setLineDash([]), t.strokeStyle = m, t.lineWidth = 0.25;
|
|
2672
|
+
for (let b = 2; b <= 9; b++) {
|
|
2673
|
+
const w = y + Math.log10(b);
|
|
2674
|
+
if (w > a) break;
|
|
2675
|
+
const S = o(w);
|
|
2676
|
+
t.beginPath(), t.moveTo(r, S), t.lineTo(r + d, S), t.stroke();
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
_drawZones(t, e, o, s, i) {
|
|
2681
|
+
const n = i.zone;
|
|
2682
|
+
if (!(!n || !n.data)) {
|
|
2683
|
+
for (const l of n.data)
|
|
2684
|
+
if (!(!l.poly || l.poly.length < 3) && (t.beginPath(), l.poly.forEach((h, a) => {
|
|
2685
|
+
const r = e(h[0]), c = o(h[1]);
|
|
2686
|
+
a === 0 ? t.moveTo(r, c) : t.lineTo(r, c);
|
|
2687
|
+
}), t.closePath(), t.fillStyle = l.color || "rgba(128,128,128,0.2)", t.fill(), n.borderStyle.show && (t.strokeStyle = n.borderStyle.color || l.color || s.zoneBorder, t.lineWidth = n.borderStyle.stroke || 1, n.borderStyle.type === "dashed" ? t.setLineDash([5, 3]) : t.setLineDash([]), t.stroke(), t.setLineDash([])), n.labelStyle.show && (l.title || l.name))) {
|
|
2688
|
+
let h = 0, a = 0;
|
|
2689
|
+
l.poly.forEach((b) => {
|
|
2690
|
+
h += e(b[0]), a += o(b[1]);
|
|
2691
|
+
});
|
|
2692
|
+
const r = h / l.poly.length, c = a / l.poly.length, d = this._coordMap.gL, f = this._coordMap.gT, g = this._coordMap.plotW, p = this._coordMap.plotH, m = Math.max(d + 4, Math.min(d + g - 4, r)), y = Math.max(f + 4, Math.min(f + p - 4, c)), u = n.labelStyle.fontSize || 12;
|
|
2693
|
+
if (t.textAlign = "center", t.textBaseline = "middle", l.title && l.name && l.name !== l.title)
|
|
2694
|
+
t.font = `600 ${u + 1}px system-ui, sans-serif`, t.fillStyle = l.labelColor || s.text, t.fillText(l.name, m, y - u * 0.7), t.font = `${u - 1}px system-ui, sans-serif`, t.fillStyle = l.labelColor || s.textSecondary, t.fillText(l.title, m, y + u * 0.9);
|
|
2695
|
+
else {
|
|
2696
|
+
const b = l.title || l.name;
|
|
2697
|
+
t.font = `500 ${u}px system-ui, sans-serif`, t.fillStyle = l.labelColor || s.text, t.fillText(b, m, y);
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
_drawAxes(t, e, o, s, i, n, l, h, a, r, c) {
|
|
2703
|
+
const d = i.xAxis.min, f = i.xAxis.max, g = i.yAxis.min, p = i.yAxis.max;
|
|
2704
|
+
t.beginPath(), t.rect(h, a, r, c), t.strokeStyle = s.axisLine, t.lineWidth = 1, t.stroke();
|
|
2705
|
+
const m = i.xAxis.labelStyle.color || s.textSecondary, y = i.yAxis.labelStyle.color || s.textSecondary;
|
|
2706
|
+
t.textAlign = "center", t.textBaseline = "middle";
|
|
2707
|
+
for (let u = Math.ceil(d); u <= Math.floor(f); u++) {
|
|
2708
|
+
const b = e(u), w = i.xAxis.labelStyle;
|
|
2709
|
+
t.font = `${w.fontWeight || "normal"} ${w.fontSize || 11}px system-ui, sans-serif`, t.fillStyle = m, t.fillText(Tt(u), b + (w.offsetX || 0), a + c + 16 + (w.offsetY || 0));
|
|
2710
|
+
}
|
|
2711
|
+
t.textAlign = "right";
|
|
2712
|
+
for (let u = Math.ceil(g); u <= Math.floor(p); u++) {
|
|
2713
|
+
const b = o(u), w = i.yAxis.labelStyle;
|
|
2714
|
+
t.font = `${w.fontWeight || "normal"} ${w.fontSize || 11}px system-ui, sans-serif`, t.fillStyle = y, t.fillText(Tt(u), h - 6 + (w.offsetX || 0), b + (w.offsetY || 0));
|
|
2715
|
+
}
|
|
2716
|
+
if (i.xAxis.title) {
|
|
2717
|
+
const u = i.xAxis.titleStyle, b = { left: h, center: h + r / 2, right: h + r }, w = (b[u.align] || b.center) + (u.offsetX || 0), S = a + c + 38 + (u.offsetY || 0);
|
|
2718
|
+
t.textAlign = u.align === "left" ? "left" : u.align === "right" ? "right" : "center", t.font = `${u.fontWeight || "normal"} ${u.fontSize || 13}px system-ui, sans-serif`, t.fillStyle = u.color || s.text, t.fillText(i.xAxis.title, w, S);
|
|
2719
|
+
}
|
|
2720
|
+
if (i.yAxis.title) {
|
|
2721
|
+
const u = i.yAxis.titleStyle, b = { left: a + c, center: a + c / 2, right: a }, w = b[u.align] || b.center, S = 14 + (u.offsetX || 0), x = w + (u.offsetY || 0);
|
|
2722
|
+
t.save(), t.translate(S, x), t.rotate(-Math.PI / 2), t.textAlign = u.align === "left" ? "right" : u.align === "right" ? "left" : "center", t.font = `${u.fontWeight || "normal"} ${u.fontSize || 13}px system-ui, sans-serif`, t.fillStyle = u.color || s.text, t.fillText(i.yAxis.title, 0, 0), t.restore();
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
_drawSeries(t, e, o, s, i) {
|
|
2726
|
+
var l, h, a;
|
|
2727
|
+
const n = [];
|
|
2728
|
+
for (const r of i.series || []) {
|
|
2729
|
+
let c = 0, d = 0;
|
|
2730
|
+
if (Array.isArray(r.data) && r.data.length >= 2) {
|
|
2731
|
+
const x = typeof r.data[0] == "object" ? r.data[0].value : r.data[0], C = typeof r.data[1] == "object" ? r.data[1].value : r.data[1];
|
|
2732
|
+
c = r.logScale === !1 ? x : Math.log10(x || 1e-9), d = r.logScale === !1 ? C : Math.log10(C || 1e-9);
|
|
2733
|
+
} else if (r.dataX !== void 0 && r.dataY !== void 0)
|
|
2734
|
+
c = r.logScale === !1 ? r.dataX : Math.log10(r.dataX || 1e-9), d = r.logScale === !1 ? r.dataY : Math.log10(r.dataY || 1e-9);
|
|
2735
|
+
else
|
|
2736
|
+
continue;
|
|
2737
|
+
const f = e(c), g = o(d);
|
|
2738
|
+
let p = null;
|
|
2739
|
+
if (i.zone && i.zone.data) {
|
|
2740
|
+
for (const x of i.zone.data)
|
|
2741
|
+
if (x.poly && gt(c, d, x.poly)) {
|
|
2742
|
+
p = x;
|
|
2743
|
+
break;
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
n.push({ series: r, zone: p, logX: c, logY: d, px: f, py: g });
|
|
2747
|
+
const m = r.color || "#00e5ff", y = r.size || 6, u = r.shape || "circle", b = this._hoverPoint && this._hoverPoint.series === r;
|
|
2748
|
+
t.save(), t.translate(f, g);
|
|
2749
|
+
const w = r.glowStyle || {};
|
|
2750
|
+
(w.show || b && w.show !== !1) && (t.shadowColor = w.color || Lt(m, 0.8), t.shadowBlur = w.blur || 15), t.fillStyle = m, t.strokeStyle = ((l = r.itemStyle) == null ? void 0 : l.borderColor) || "none", t.lineWidth = ((h = r.itemStyle) == null ? void 0 : h.borderWidth) || 0, (t.strokeStyle === "none" || !((a = r.itemStyle) != null && a.borderColor)) && (t.lineWidth = 0), t.beginPath(), nt(t, 0, 0, u, y, 1, 0), t.fill(), t.lineWidth > 0 && t.stroke();
|
|
2751
|
+
const S = r.textStyle;
|
|
2752
|
+
if (S && S.show && r.name) {
|
|
2753
|
+
t.shadowBlur = 0;
|
|
2754
|
+
const x = {
|
|
2755
|
+
top: [0, -(y + 6)],
|
|
2756
|
+
bottom: [0, y + 12],
|
|
2757
|
+
left: [-(y + 6), 4],
|
|
2758
|
+
right: [y + 6, 4]
|
|
2759
|
+
}, [C, z] = x[S.position] || x.top;
|
|
2760
|
+
t.textAlign = S.position === "left" ? "right" : S.position === "right" ? "left" : "center", t.textBaseline = "middle", t.font = `${S.fontWeight || "normal"} ${S.fontSize || 12}px system-ui, sans-serif`, t.fillStyle = S.color || m, t.fillText(r.name, C, z);
|
|
2761
|
+
}
|
|
2762
|
+
t.restore();
|
|
2763
|
+
}
|
|
2764
|
+
return this._drawnPoints = n, n;
|
|
2765
|
+
}
|
|
2766
|
+
_defaultTooltipHTML(t, e) {
|
|
2767
|
+
const o = t.series, s = Math.pow(10, t.logX).toPrecision(4), i = Math.pow(10, t.logY).toPrecision(4), n = e.xAxis.title || "X", l = e.yAxis.title || "Y", h = t.zone ? t.zone.title || t.zone.name : "Outside", a = t.zone && t.zone.color || "#fff";
|
|
2768
|
+
return `
|
|
2769
|
+
<div style="font-weight:bold; font-size:14px; margin-bottom:6px; color:#fff;">${o.name || "Data Point"}</div>
|
|
2770
|
+
<div style="font-size:13px; font-weight:bold; color:${a}; border-bottom:1px solid rgba(255,255,255,0.1); padding-bottom:6px; margin-bottom:8px;">${h}</div>
|
|
2771
|
+
<div style="font-size:13px; color:#cbd5e1; display:flex; justify-content:space-between; margin-bottom:4px;">
|
|
2772
|
+
<span>${n}:</span><span style="font-weight:bold; color:#fff; margin-left:12px;">${s}</span>
|
|
2773
|
+
</div>
|
|
2774
|
+
<div style="font-size:13px; color:#cbd5e1; display:flex; justify-content:space-between;">
|
|
2775
|
+
<span>${l}:</span><span style="font-weight:bold; color:#fff; margin-left:12px;">${i}</span>
|
|
2776
|
+
</div>
|
|
2777
|
+
`;
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
export {
|
|
2781
|
+
Ut as DiagnosticTools,
|
|
2782
|
+
vt as DuvalPentagon,
|
|
2783
|
+
Gt as DuvalTriangle,
|
|
2784
|
+
zt as ETRAChart,
|
|
2785
|
+
Wt as ETRA_ZONES_A,
|
|
2786
|
+
Zt as ETRA_ZONES_B,
|
|
2787
|
+
it as PENTAGON_ZONES_1,
|
|
2788
|
+
lt as PENTAGON_ZONES_2,
|
|
2789
|
+
kt as THREE_RADIO_ZONES,
|
|
2790
|
+
Pt as TRIANGLE_ZONES_1,
|
|
2791
|
+
Ot as TRIANGLE_ZONES_4,
|
|
2792
|
+
Nt as TRIANGLE_ZONES_5,
|
|
2793
|
+
xt as ThreeRatioChart,
|
|
2794
|
+
ft as computedPercent
|
|
2795
|
+
};
|
|
2796
|
+
//# sourceMappingURL=index.js.map
|