@wick-charts/vue 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -6
- package/dist/BarSeries.vue.js +26 -20
- package/dist/CandlestickSeries.vue.js +30 -24
- package/dist/ChartContainer.vue.js +125 -72
- package/dist/HeatmapSeries.vue.js +40 -0
- package/dist/HeatmapSeries.vue2.js +4 -0
- package/dist/LineSeries.vue.js +26 -20
- package/dist/PieSeries.vue.js +24 -18
- package/dist/core/dist/animation/animator.js +21 -21
- package/dist/core/dist/animation/config.js +114 -87
- package/dist/core/dist/animation/easing.js +3 -1
- package/dist/core/dist/animation/intro-wave.js +68 -0
- package/dist/core/dist/axis/dom-labels.js +13 -14
- package/dist/core/dist/chart/badge-animator.js +56 -0
- package/dist/core/dist/chart/edge-indicators.js +28 -13
- package/dist/core/dist/chart/last-value.js +18 -17
- package/dist/core/dist/chart/options.js +27 -16
- package/dist/core/dist/chart/y-target.js +13 -12
- package/dist/core/dist/chart.js +868 -441
- package/dist/core/dist/components/annotation-chip.js +28 -0
- package/dist/core/dist/components/crosshair.js +8 -6
- package/dist/core/dist/components/edge-indicator.js +58 -26
- package/dist/core/dist/components/loading-indicator.js +46 -0
- package/dist/core/dist/components/marker.js +49 -31
- package/dist/core/dist/components/reference-line.js +27 -36
- package/dist/core/dist/components/time-region.js +30 -28
- package/dist/core/dist/data/store.js +38 -19
- package/dist/core/dist/data/sync.js +60 -38
- package/dist/core/dist/heatmap-tooltip.js +16 -0
- package/dist/core/dist/interactions/handler.js +86 -46
- package/dist/core/dist/perf/perf-hud.js +5 -5
- package/dist/core/dist/scales/x-scale.js +85 -37
- package/dist/core/dist/scales/y-scale.js +38 -27
- package/dist/core/dist/series/bar-intro.js +18 -0
- package/dist/core/dist/series/bar.js +337 -205
- package/dist/core/dist/series/base-multi-layer.js +224 -96
- package/dist/core/dist/series/candlestick-intro.js +57 -0
- package/dist/core/dist/series/candlestick.js +454 -276
- package/dist/core/dist/series/heatmap.js +387 -0
- package/dist/core/dist/series/line-intro.js +85 -0
- package/dist/core/dist/series/line.js +590 -282
- package/dist/core/dist/series/painters/builtins.js +2 -2
- package/dist/core/dist/series/pie.js +396 -259
- package/dist/core/dist/series/register-builtins.js +8 -7
- package/dist/core/dist/series/types.js +6 -0
- package/dist/core/dist/series/wave-intro.js +26 -0
- package/dist/core/dist/theme/create.js +69 -63
- package/dist/core/dist/theme/themes/mint-breeze.js +2 -2
- package/dist/core/dist/theme/themes/sand-dune.js +2 -2
- package/dist/core/dist/utils/color.js +109 -34
- package/dist/core/dist/utils/deep-equal.js +18 -0
- package/dist/core/dist/utils/time.js +25 -19
- package/dist/index.d.ts +1494 -122
- package/dist/index.js +177 -130
- package/dist/ui/Crosshair.vue.js +20 -21
- package/dist/ui/HeatmapTooltip.vue.js +96 -0
- package/dist/ui/HeatmapTooltip.vue2.js +4 -0
- package/dist/ui/InfoBar.vue.js +46 -47
- package/dist/ui/Legend.vue.js +80 -79
- package/dist/ui/PieLegend.vue.js +5 -5
- package/dist/ui/PieTooltip.vue.js +77 -67
- package/dist/ui/Tooltip.vue.js +60 -60
- package/dist/ui/YAxis.vue.js +2 -2
- package/dist/ui/YLabel.vue.js +94 -61
- package/package.json +2 -2
- package/src/BarSeries.vue +23 -7
- package/src/CandlestickSeries.vue +23 -7
- package/src/ChartContainer.vue +250 -13
- package/src/HeatmapSeries.vue +69 -0
- package/src/LineSeries.vue +23 -7
- package/src/PieSeries.vue +22 -6
- package/src/index.ts +84 -3
- package/src/ui/Crosshair.vue +1 -2
- package/src/ui/HeatmapTooltip.vue +144 -0
- package/src/ui/InfoBar.vue +1 -2
- package/src/ui/Legend.vue +13 -0
- package/src/ui/Marker.vue +1 -1
- package/src/ui/PieTooltip.vue +10 -0
- package/src/ui/ReferenceLine.vue +2 -2
- package/src/ui/TimeRegion.vue +2 -2
- package/src/ui/Tooltip.vue +2 -2
- package/src/ui/YLabel.vue +123 -8
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { DEFAULT_PIE_ENTRY as ct } from "../animation/config.js";
|
|
2
|
+
import { IntroWave as ut } from "../animation/intro-wave.js";
|
|
3
|
+
import { isLightColor as ft, lighten as dt } from "../utils/color.js";
|
|
4
|
+
import { smoothToward as nt, clamp as U } from "../utils/math.js";
|
|
5
|
+
const lt = {
|
|
4
6
|
mode: "outside",
|
|
5
7
|
content: "both",
|
|
6
8
|
fontSize: 11,
|
|
@@ -18,11 +20,15 @@ const tt = {
|
|
|
18
20
|
// 1.8 gives each label room to breathe without spreading so aggressively
|
|
19
21
|
// that PAV collapses into a centered block on dense datasets.
|
|
20
22
|
labelGap: 1.8
|
|
21
|
-
},
|
|
23
|
+
}, mt = {
|
|
22
24
|
innerRadiusRatio: 0,
|
|
23
25
|
// 1.15° ≈ 0.02 rad — same visual default as before the radians→degrees switch.
|
|
24
26
|
padAngle: 1.15,
|
|
25
|
-
|
|
27
|
+
// Softly rounded slice corners by default, matching the bar/candlestick
|
|
28
|
+
// families. Pass 0 for sharp wedges. Thin rings and narrow slices clamp it
|
|
29
|
+
// down automatically.
|
|
30
|
+
cornerRadius: 3,
|
|
31
|
+
sliceLabels: { ...lt },
|
|
26
32
|
// Motion off by default: label draw-in + hover explode both skipped.
|
|
27
33
|
animate: !1,
|
|
28
34
|
// Small-slice grouping on by default: dense datasets collapse into the top
|
|
@@ -31,54 +37,72 @@ const tt = {
|
|
|
31
37
|
// Radial depth gradient (lighter center → flat edge) on by default. Set
|
|
32
38
|
// false for flat, solid-color slices.
|
|
33
39
|
gradient: !0
|
|
34
|
-
},
|
|
40
|
+
}, ot = {
|
|
35
41
|
maxSlices: 8,
|
|
36
42
|
label: "Other"
|
|
37
43
|
};
|
|
38
|
-
function
|
|
39
|
-
return
|
|
44
|
+
function gt(a) {
|
|
45
|
+
return a === !1 ? null : a === !0 || a === void 0 ? ot : { ...ot, ...a };
|
|
40
46
|
}
|
|
41
|
-
const
|
|
42
|
-
function
|
|
43
|
-
return { ...
|
|
47
|
+
const et = Math.PI / 180;
|
|
48
|
+
function pt(a, e) {
|
|
49
|
+
return { ...a ?? {}, ...e };
|
|
44
50
|
}
|
|
45
|
-
function it(
|
|
46
|
-
return { ...
|
|
51
|
+
function it(a) {
|
|
52
|
+
return { ...lt, ...a ?? {} };
|
|
47
53
|
}
|
|
48
|
-
function
|
|
54
|
+
function bt(a) {
|
|
49
55
|
const e = { color: "rgba(0, 0, 0, 0.22)", blur: 24, offsetX: 0, offsetY: 10 };
|
|
50
|
-
return
|
|
56
|
+
return a === !0 || a === !1 ? e : { ...e, ...a };
|
|
51
57
|
}
|
|
52
|
-
function
|
|
58
|
+
function xt(a) {
|
|
53
59
|
const e = { color: "rgba(0, 0, 0, 0.1)", depth: 0.3 };
|
|
54
|
-
return
|
|
60
|
+
return a === !0 || a === !1 ? e : { ...e, ...a };
|
|
61
|
+
}
|
|
62
|
+
const H = Math.PI * 2, rt = 0.85, Mt = 0.5, yt = 20;
|
|
63
|
+
function st(a) {
|
|
64
|
+
const { bitmapWidth: e, bitmapHeight: t, padTop: n, padBottom: o } = a, s = Math.max(0, a.labelReserve ?? 0), h = Math.min(Math.max(0, n), t), r = Math.min(Math.max(0, o), t - h), u = t - h - r, m = Math.max(0, e - 2 * s), R = e / 2, g = Math.min(m, u) / 2, y = Math.min(e, t) / 2, p = Math.max(0, Math.max(g, y * Mt) * rt), T = Math.min(2, Math.max(0, t / 2 - p)), b = h + u / 2, C = Math.min(Math.max(b, p + T), t - p - T);
|
|
65
|
+
return { cx: R, cy: C, maxR: p };
|
|
66
|
+
}
|
|
67
|
+
function Z(a) {
|
|
68
|
+
const { ringR: e, theta: t, radius: n, outward: o, angleSign: s } = a, h = o ? e - n : e + n, r = Math.asin(U(n / h, 0, 1)) * s, u = t + r;
|
|
69
|
+
return {
|
|
70
|
+
fx: h * Math.cos(u),
|
|
71
|
+
fy: h * Math.sin(u),
|
|
72
|
+
lineR: h * Math.cos(r),
|
|
73
|
+
archAngle: u
|
|
74
|
+
};
|
|
55
75
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return { cx: C, cy: x, maxR: R };
|
|
76
|
+
function vt(a) {
|
|
77
|
+
const { outerR: e, innerR: t, halfSpan: n } = a, o = Math.sin(Math.min(n, Math.PI / 2 - 1e-3)), s = e * o / (1 + o), h = t > 0 ? t * o / (1 - o) : 1 / 0, r = (e - t) / 2;
|
|
78
|
+
return Math.max(0, Math.min(s, h, r));
|
|
60
79
|
}
|
|
61
|
-
function
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
function St(a, e, t) {
|
|
81
|
+
if (t <= 0) return "";
|
|
82
|
+
if (a.measureText(e).width <= t) return e;
|
|
83
|
+
let n = e.length - 1;
|
|
84
|
+
for (; n > 0 && a.measureText(`${e.slice(0, n)}…`).width > t; )
|
|
85
|
+
n--;
|
|
86
|
+
return n <= 0 ? "" : `${e.slice(0, n)}…`;
|
|
65
87
|
}
|
|
66
|
-
function
|
|
67
|
-
if (!
|
|
68
|
-
const t =
|
|
69
|
-
return
|
|
88
|
+
function Rt(a, e) {
|
|
89
|
+
if (!a.startsWith("#")) return a;
|
|
90
|
+
const t = a.length === 4 ? `#${a[1]}${a[1]}${a[2]}${a[2]}${a[3]}${a[3]}` : a;
|
|
91
|
+
return dt(t, e);
|
|
70
92
|
}
|
|
71
|
-
function
|
|
72
|
-
if (
|
|
93
|
+
function at(a) {
|
|
94
|
+
if (a.startsWith("#")) {
|
|
95
|
+
const t = a.length === 4 ? `#${a[1]}${a[1]}${a[2]}${a[2]}${a[3]}${a[3]}` : a;
|
|
73
96
|
return {
|
|
74
|
-
r: parseInt(
|
|
75
|
-
g: parseInt(
|
|
76
|
-
b: parseInt(
|
|
97
|
+
r: parseInt(t.slice(1, 3), 16) || 0,
|
|
98
|
+
g: parseInt(t.slice(3, 5), 16) || 0,
|
|
99
|
+
b: parseInt(t.slice(5, 7), 16) || 0,
|
|
77
100
|
a: 1
|
|
78
101
|
};
|
|
79
|
-
|
|
102
|
+
}
|
|
103
|
+
const e = a.match(/^rgba?\s*\(([^)]+)\)$/i);
|
|
80
104
|
if (e) {
|
|
81
|
-
const t = e[1].split(",").map((
|
|
105
|
+
const t = e[1].split(",").map((n) => n.trim());
|
|
82
106
|
return {
|
|
83
107
|
r: Number.parseFloat(t[0]) || 0,
|
|
84
108
|
g: Number.parseFloat(t[1]) || 0,
|
|
@@ -88,11 +112,11 @@ function J(l) {
|
|
|
88
112
|
}
|
|
89
113
|
return { r: 0, g: 0, b: 0, a: 1 };
|
|
90
114
|
}
|
|
91
|
-
function
|
|
92
|
-
const t =
|
|
93
|
-
return `#${s.toString(16).padStart(2, "0")}${
|
|
115
|
+
function wt(a, e) {
|
|
116
|
+
const t = at(a), n = at(e), o = n.a, s = Math.round(n.r * o + t.r * (1 - o)), h = Math.round(n.g * o + t.g * (1 - o)), r = Math.round(n.b * o + t.b * (1 - o));
|
|
117
|
+
return `#${s.toString(16).padStart(2, "0")}${h.toString(16).padStart(2, "0")}${r.toString(16).padStart(2, "0")}`;
|
|
94
118
|
}
|
|
95
|
-
class
|
|
119
|
+
class It {
|
|
96
120
|
kind = "pie";
|
|
97
121
|
#n = [];
|
|
98
122
|
/**
|
|
@@ -108,39 +132,54 @@ class ft {
|
|
|
108
132
|
* grouping didn't apply. Lets the color resolution pick the theme's muted
|
|
109
133
|
* `pie.otherColor` for the aggregate instead of the next palette color.
|
|
110
134
|
*/
|
|
111
|
-
#
|
|
135
|
+
#u = -1;
|
|
112
136
|
#e;
|
|
113
137
|
/** Index of the hovered slice (-1 = none). Set via {@link setHoverIndex}. */
|
|
114
|
-
#
|
|
138
|
+
#a = -1;
|
|
115
139
|
/** Animated offset per slice (0..1), smoothly interpolated toward target. */
|
|
116
140
|
#s = [];
|
|
117
|
-
#
|
|
141
|
+
#f = 0;
|
|
118
142
|
/**
|
|
119
143
|
* Outside-label reveal progress (0..1). Eased toward 1 on mount / data-swap
|
|
120
144
|
* when `animate` is on; drives the leader-line draw-in and label fade-in
|
|
121
145
|
* stagger. When `animate` is off (the default) this stays pinned at 1 so
|
|
122
146
|
* labels paint fully-revealed on the first frame.
|
|
123
147
|
*/
|
|
124
|
-
#
|
|
148
|
+
#o = 1;
|
|
149
|
+
/**
|
|
150
|
+
* Initial-load reveal clock — armed on the first empty → non-empty seed;
|
|
151
|
+
* slices unfurl clockwise from 12 o'clock, then {@link #labelReveal}
|
|
152
|
+
* chains in. Armed with `entryMs / 2` because the wave's full window is
|
|
153
|
+
* `2 ×` its per-element duration and the pie treats `entryMs` as the
|
|
154
|
+
* total sweep time.
|
|
155
|
+
*/
|
|
156
|
+
#r = new ut();
|
|
125
157
|
/**
|
|
126
158
|
* Cached horizontal bitmap-pixel reserve for outside labels. Invalidated on
|
|
127
159
|
* `setData` / `updateOptions`; computed lazily on first `render` call per
|
|
128
160
|
* frame using the live canvas context for accurate `measureText`.
|
|
129
161
|
*/
|
|
130
162
|
#l = null;
|
|
163
|
+
/**
|
|
164
|
+
* Horizontal reserve the last `render` actually applied — 0 when outside
|
|
165
|
+
* labels were auto-hidden below `MIN_LABELED_PIE_RADIUS`. `hitTest` reads
|
|
166
|
+
* this instead of the measurement cache so hover geometry always matches
|
|
167
|
+
* the painted frame.
|
|
168
|
+
*/
|
|
169
|
+
#g = 0;
|
|
131
170
|
/** Subscribers notified on setData (pie has no TimeSeriesStore to piggy-back on). */
|
|
132
|
-
#
|
|
171
|
+
#h = [];
|
|
133
172
|
constructor(e) {
|
|
134
|
-
this.#e = this.#
|
|
173
|
+
this.#e = this.#p(mt, e ?? {});
|
|
135
174
|
}
|
|
136
175
|
/**
|
|
137
176
|
* Merge partial options onto a base, deep-merging `sliceLabels` and `other`
|
|
138
177
|
* so callers can override one field (e.g. `mode` or `maxSlices`) without
|
|
139
178
|
* dropping sibling defaults.
|
|
140
179
|
*/
|
|
141
|
-
#
|
|
142
|
-
const
|
|
143
|
-
return t.sliceLabels !== void 0 && (
|
|
180
|
+
#p(e, t) {
|
|
181
|
+
const n = { ...e, ...t };
|
|
182
|
+
return t.sliceLabels !== void 0 && (n.sliceLabels = pt(e.sliceLabels, t.sliceLabels)), typeof t.other == "object" && typeof e.other == "object" && (n.other = { ...e.other, ...t.other }), n;
|
|
144
183
|
}
|
|
145
184
|
/**
|
|
146
185
|
* Build the display array from the raw data. When grouping applies, the
|
|
@@ -149,18 +188,18 @@ class ft {
|
|
|
149
188
|
* layout-sanitized value so negative / non-finite entries can never outrank
|
|
150
189
|
* a real slice.
|
|
151
190
|
*/
|
|
152
|
-
#
|
|
153
|
-
this.#
|
|
154
|
-
const e =
|
|
191
|
+
#b() {
|
|
192
|
+
this.#u = -1;
|
|
193
|
+
const e = gt(this.#e.other);
|
|
155
194
|
if (!e || this.#n.length <= e.maxSlices) return this.#n;
|
|
156
|
-
const t = Math.max(1, e.maxSlices - 1),
|
|
157
|
-
for (const r of
|
|
158
|
-
|
|
195
|
+
const t = Math.max(1, e.maxSlices - 1), n = this.#n.map((r, u) => ({ index: u, value: this.#i(r.value) })).sort((r, u) => u.value - r.value), o = /* @__PURE__ */ new Set();
|
|
196
|
+
for (const r of n.slice(0, t))
|
|
197
|
+
o.add(r.index);
|
|
159
198
|
const s = [];
|
|
160
|
-
let
|
|
199
|
+
let h = 0;
|
|
161
200
|
for (let r = 0; r < this.#n.length; r++)
|
|
162
|
-
|
|
163
|
-
return s.push({ label: e.label, value:
|
|
201
|
+
o.has(r) ? s.push(this.#n[r]) : h += this.#i(this.#n[r].value);
|
|
202
|
+
return s.push({ label: e.label, value: h, color: e.color }), this.#u = s.length - 1, s;
|
|
164
203
|
}
|
|
165
204
|
/**
|
|
166
205
|
* Paint color for one display slice: explicit slice color → theme-driven
|
|
@@ -169,28 +208,28 @@ class ft {
|
|
|
169
208
|
* `pie.otherColor`, so hand-built theme literals without the `pie` block
|
|
170
209
|
* get the same muted tone.
|
|
171
210
|
*/
|
|
172
|
-
#
|
|
173
|
-
const { index: t, palette:
|
|
174
|
-
return s?.color ? s.color : t === this.#
|
|
211
|
+
#c(e) {
|
|
212
|
+
const { index: t, palette: n, theme: o } = e, s = this.#t[t];
|
|
213
|
+
return s?.color ? s.color : t === this.#u ? o.pie?.otherColor ?? o.axis.textColor : n[t % n.length];
|
|
175
214
|
}
|
|
176
215
|
getData() {
|
|
177
216
|
return this.#n;
|
|
178
217
|
}
|
|
179
218
|
setData(e) {
|
|
180
|
-
const t = e ?? [];
|
|
181
|
-
this.#n =
|
|
182
|
-
for (const o of this.#
|
|
219
|
+
const t = this.#n.length > 0, n = e ?? [];
|
|
220
|
+
this.#n = n, this.#t = this.#b(), this.#s = new Array(this.#t.length).fill(0), !t && this.#t.length > 0 && this.#r.arm(this.#M() / 2), this.#o = this.#e.animate || this.#r.active ? 0 : 1, this.#l = null;
|
|
221
|
+
for (const o of this.#h)
|
|
183
222
|
o();
|
|
184
223
|
}
|
|
185
224
|
onDataChanged(e) {
|
|
186
|
-
return this.#
|
|
187
|
-
const t = this.#
|
|
188
|
-
t >= 0 && this.#
|
|
225
|
+
return this.#h.push(e), () => {
|
|
226
|
+
const t = this.#h.indexOf(e);
|
|
227
|
+
t >= 0 && this.#h.splice(t, 1);
|
|
189
228
|
};
|
|
190
229
|
}
|
|
191
230
|
updateOptions(e) {
|
|
192
231
|
const t = this.#e.sliceLabels?.mode;
|
|
193
|
-
this.#e = this.#
|
|
232
|
+
this.#e = this.#p(this.#e, e ?? {}), this.#e.sliceLabels?.mode === "outside" && t !== "outside" && (this.#o = this.#e.animate ? 0 : 1), this.#t = this.#b(), this.#s.length !== this.#t.length && (this.#s = new Array(this.#t.length).fill(0)), this.#l = null;
|
|
194
233
|
}
|
|
195
234
|
getColor() {
|
|
196
235
|
return this.#t[0]?.color ?? this.#e.colors?.[0] ?? "#888";
|
|
@@ -210,67 +249,85 @@ class ft {
|
|
|
210
249
|
e.typography.fontFamily !== t.typography.fontFamily && (this.#l = null);
|
|
211
250
|
}
|
|
212
251
|
/** Hit-test: which slice is at this bitmap coordinate? Returns index or -1. */
|
|
213
|
-
hitTest(e, t,
|
|
252
|
+
hitTest(e, t, n, o, s) {
|
|
214
253
|
if (this.#t.length === 0) return -1;
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
217
|
-
const r = this.#
|
|
218
|
-
bitmapWidth:
|
|
219
|
-
bitmapHeight:
|
|
254
|
+
const h = this.#d();
|
|
255
|
+
if (h <= 0) return -1;
|
|
256
|
+
const r = this.#g, { cx: u, cy: m, maxR: R } = st({
|
|
257
|
+
bitmapWidth: n,
|
|
258
|
+
bitmapHeight: o,
|
|
220
259
|
padTop: s?.top ?? 0,
|
|
221
260
|
padBottom: s?.bottom ?? 0,
|
|
222
261
|
labelReserve: r
|
|
223
|
-
}),
|
|
224
|
-
if (
|
|
225
|
-
let
|
|
226
|
-
|
|
227
|
-
let
|
|
228
|
-
for (let
|
|
229
|
-
const
|
|
230
|
-
if (
|
|
231
|
-
|
|
262
|
+
}), g = R, y = g * this.#e.innerRadiusRatio, p = e - u, T = t - m, b = Math.sqrt(p * p + T * T);
|
|
263
|
+
if (b > g || b < y) return -1;
|
|
264
|
+
let C = Math.atan2(T, p) + Math.PI / 2;
|
|
265
|
+
C < 0 && (C += H);
|
|
266
|
+
let P = 0;
|
|
267
|
+
for (let f = 0; f < this.#t.length; f++) {
|
|
268
|
+
const v = this.#i(this.#t[f].value) / h * H;
|
|
269
|
+
if (C >= P && C < P + v) return f;
|
|
270
|
+
P += v;
|
|
232
271
|
}
|
|
233
272
|
return -1;
|
|
234
273
|
}
|
|
235
274
|
setHoverIndex(e) {
|
|
236
|
-
return this.#
|
|
275
|
+
return this.#a === e ? !1 : (this.#a = e, !0);
|
|
237
276
|
}
|
|
238
277
|
getHoverIndex() {
|
|
239
|
-
return this.#
|
|
278
|
+
return this.#a;
|
|
240
279
|
}
|
|
241
280
|
getHoverInfo(e) {
|
|
242
|
-
if (this.#
|
|
243
|
-
const t = this.#t[this.#
|
|
281
|
+
if (this.#a < 0) return null;
|
|
282
|
+
const t = this.#t[this.#a];
|
|
244
283
|
if (!t) return null;
|
|
245
|
-
const
|
|
284
|
+
const n = this.#d(), o = this.#e.colors ?? e.seriesColors;
|
|
246
285
|
return {
|
|
247
286
|
label: t.label,
|
|
248
287
|
value: t.value,
|
|
249
|
-
percent:
|
|
250
|
-
color: this.#
|
|
288
|
+
percent: n > 0 ? this.#i(t.value) / n * 100 : 0,
|
|
289
|
+
color: this.#c({ index: this.#a, palette: o, theme: e })
|
|
251
290
|
};
|
|
252
291
|
}
|
|
253
292
|
getSliceInfo(e) {
|
|
254
293
|
if (this.#t.length === 0) return null;
|
|
255
|
-
const t = this.#
|
|
256
|
-
return this.#t.map((
|
|
257
|
-
label:
|
|
258
|
-
value:
|
|
259
|
-
percent: t > 0 ? this.#o
|
|
260
|
-
color: this.#
|
|
294
|
+
const t = this.#d(), n = this.#e.colors ?? e.seriesColors;
|
|
295
|
+
return this.#t.map((o, s) => ({
|
|
296
|
+
label: o.label,
|
|
297
|
+
value: o.value,
|
|
298
|
+
percent: t > 0 ? this.#i(o.value) / t * 100 : 0,
|
|
299
|
+
color: this.#c({ index: s, palette: n, theme: e })
|
|
261
300
|
}));
|
|
262
301
|
}
|
|
302
|
+
/** Resolved slice-sweep duration — `entryMs` with the `false` sentinel
|
|
303
|
+
* normalized to `0` (pie stores raw public options, unlike the
|
|
304
|
+
* time-series renderers which normalize at the merge boundary). */
|
|
305
|
+
#M() {
|
|
306
|
+
const e = this.#e.entryMs;
|
|
307
|
+
return e === !1 ? 0 : e ?? ct;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Settle every reveal clock when a render pass has nothing to paint
|
|
311
|
+
* (empty display or non-positive total). The clocks only advance inside
|
|
312
|
+
* the paint path, so leaving them armed here would hold
|
|
313
|
+
* {@link needsAnimation} true forever and busy-loop the frame scheduler
|
|
314
|
+
* over a blank chart.
|
|
315
|
+
*/
|
|
316
|
+
#x() {
|
|
317
|
+
this.#r.finish(), this.#o = 1;
|
|
318
|
+
}
|
|
263
319
|
/** Returns true if animation is still in progress. */
|
|
264
320
|
get needsAnimation() {
|
|
321
|
+
if (this.#r.active || it(this.#e.sliceLabels).mode === "outside" && this.#o < 1) return !0;
|
|
265
322
|
if (!this.#e.animate) return !1;
|
|
266
323
|
for (let e = 0; e < this.#s.length; e++) {
|
|
267
|
-
const t = e === this.#
|
|
324
|
+
const t = e === this.#a ? 1 : 0;
|
|
268
325
|
if (Math.abs(this.#s[e] - t) > 0.01) return !0;
|
|
269
326
|
}
|
|
270
|
-
return
|
|
327
|
+
return !1;
|
|
271
328
|
}
|
|
272
329
|
dispose() {
|
|
273
|
-
this.#
|
|
330
|
+
this.#h = [], this.#n = [], this.#t = [], this.#u = -1, this.#s = [], this.#f = 0;
|
|
274
331
|
}
|
|
275
332
|
/**
|
|
276
333
|
* @internal Test-only accessor for the animated slice offsets. The backing
|
|
@@ -289,21 +346,21 @@ class ft {
|
|
|
289
346
|
* contribute nothing to the geometry. The original value is still surfaced
|
|
290
347
|
* verbatim in reported SliceInfo / HoverInfo.
|
|
291
348
|
*/
|
|
292
|
-
#
|
|
349
|
+
#i(e) {
|
|
293
350
|
return Number.isFinite(e) && e > 0 ? e : 0;
|
|
294
351
|
}
|
|
295
352
|
/** Sum of layout-sanitized slice values — the denominator for every arc
|
|
296
353
|
* sweep and percentage. Always finite and ≥ 0. */
|
|
297
|
-
#
|
|
354
|
+
#d() {
|
|
298
355
|
let e = 0;
|
|
299
356
|
for (const t of this.#t)
|
|
300
|
-
e += this.#
|
|
357
|
+
e += this.#i(t.value);
|
|
301
358
|
return e;
|
|
302
359
|
}
|
|
303
360
|
/** Build the text shown for one slice given the resolved content mode. */
|
|
304
|
-
#
|
|
305
|
-
const
|
|
306
|
-
return
|
|
361
|
+
#m(e, t, n) {
|
|
362
|
+
const o = t > 0 ? this.#i(e.value) / t * 100 : 0, s = `${o.toFixed(o >= 10 ? 0 : 1)}%`;
|
|
363
|
+
return n === "percent" ? s : n === "label" ? e.label : `${e.label} ${s}`;
|
|
307
364
|
}
|
|
308
365
|
/**
|
|
309
366
|
* Bitmap-pixel horizontal reserve needed for outside labels. Measured using
|
|
@@ -314,17 +371,72 @@ class ft {
|
|
|
314
371
|
* the active `context.font` string so a theme swap that changes the font
|
|
315
372
|
* family / size busts the cache and triggers a fresh measurement.
|
|
316
373
|
*/
|
|
317
|
-
#
|
|
374
|
+
#y(e, t, n, o) {
|
|
318
375
|
const s = e.font;
|
|
319
|
-
if (this.#l && this.#l.fontScale ===
|
|
376
|
+
if (this.#l && this.#l.fontScale === o && this.#l.font === s)
|
|
320
377
|
return this.#l.reserve;
|
|
321
|
-
let
|
|
322
|
-
for (const
|
|
323
|
-
const
|
|
324
|
-
|
|
378
|
+
let h = 0;
|
|
379
|
+
for (const m of this.#t) {
|
|
380
|
+
const R = this.#m(m, n, t.content), g = e.measureText(R).width;
|
|
381
|
+
g > h && (h = g);
|
|
325
382
|
}
|
|
326
|
-
const r = Math.max(t.distance, t.elbowLen),
|
|
327
|
-
return this.#l = { fontScale:
|
|
383
|
+
const r = Math.max(t.distance, t.elbowLen), u = (h + (r + Math.max(0, t.railWidth) + t.legPad + 4) * o) / rt;
|
|
384
|
+
return this.#l = { fontScale: o, font: s, reserve: u }, u;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Trace one slice's outline with its four corners rounded (two on the
|
|
388
|
+
* outer rim, plus two on the inner rim when `innerR > 0`) instead of the
|
|
389
|
+
* plain `arc` + `lineTo` sharp-corner path. `cornerRadius` must already be
|
|
390
|
+
* clamped by the caller ({@link maxCornerRadius}) — this only draws.
|
|
391
|
+
*
|
|
392
|
+
* Each fillet is a small circle tangent to the ring and to the slice's
|
|
393
|
+
* straight edge; the main arcs are inset to the fillets' tangent angles
|
|
394
|
+
* instead of the slice's raw start/end angles. A closure keeps the sweep
|
|
395
|
+
* direction for each fillet's tiny arc correct regardless of which corner
|
|
396
|
+
* it's rounding, by always turning the shorter way between its two tangent
|
|
397
|
+
* points rather than reasoning about clockwise/anticlockwise per corner.
|
|
398
|
+
*/
|
|
399
|
+
#v(e, t) {
|
|
400
|
+
const { sliceCx: n, sliceCy: o, outerR: s, innerR: h, startAngle: r, endAngle: u, cornerRadius: m } = t, R = (f, v) => [
|
|
401
|
+
n + f * Math.cos(v),
|
|
402
|
+
o + f * Math.sin(v)
|
|
403
|
+
], g = (f, v, A, S) => {
|
|
404
|
+
const c = n + f, l = o + v, M = Math.atan2(A[1] - l, A[0] - c);
|
|
405
|
+
let i = Math.atan2(S[1] - l, S[0] - c) - M;
|
|
406
|
+
for (; i > Math.PI; ) i -= H;
|
|
407
|
+
for (; i <= -Math.PI; ) i += H;
|
|
408
|
+
e.arc(c, l, m, M, M + i, i < 0);
|
|
409
|
+
}, y = Z({
|
|
410
|
+
ringR: s,
|
|
411
|
+
theta: r,
|
|
412
|
+
radius: m,
|
|
413
|
+
outward: !0,
|
|
414
|
+
angleSign: 1
|
|
415
|
+
}), p = Z({
|
|
416
|
+
ringR: s,
|
|
417
|
+
theta: u,
|
|
418
|
+
radius: m,
|
|
419
|
+
outward: !0,
|
|
420
|
+
angleSign: -1
|
|
421
|
+
}), T = R(y.lineR, r), b = R(s, y.archAngle), C = R(s, p.archAngle), P = R(p.lineR, u);
|
|
422
|
+
if (h > 0) {
|
|
423
|
+
const f = Z({
|
|
424
|
+
ringR: h,
|
|
425
|
+
theta: r,
|
|
426
|
+
radius: m,
|
|
427
|
+
outward: !1,
|
|
428
|
+
angleSign: 1
|
|
429
|
+
}), v = Z({
|
|
430
|
+
ringR: h,
|
|
431
|
+
theta: u,
|
|
432
|
+
radius: m,
|
|
433
|
+
outward: !1,
|
|
434
|
+
angleSign: -1
|
|
435
|
+
}), A = R(f.lineR, r), S = R(v.lineR, u), c = R(h, v.archAngle), l = R(h, f.archAngle);
|
|
436
|
+
e.moveTo(...A), e.lineTo(...T), g(y.fx, y.fy, T, b), e.arc(n, o, s, y.archAngle, p.archAngle), g(p.fx, p.fy, C, P), e.lineTo(...S), g(v.fx, v.fy, S, c), e.arc(n, o, h, v.archAngle, f.archAngle, !0), g(f.fx, f.fy, l, A);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
e.moveTo(n, o), e.lineTo(...T), g(y.fx, y.fy, T, b), e.arc(n, o, s, y.archAngle, p.archAngle), g(p.fx, p.fy, C, P), e.lineTo(n, o);
|
|
328
440
|
}
|
|
329
441
|
/**
|
|
330
442
|
* Draw one outside label: anchor dot → radial elbow (per-slice, at slice
|
|
@@ -333,20 +445,24 @@ class ft {
|
|
|
333
445
|
* lines from cutting across the pie when several small slices cluster near
|
|
334
446
|
* 12 o'clock or 6 o'clock.
|
|
335
447
|
*/
|
|
336
|
-
#
|
|
337
|
-
const { context: t, entry:
|
|
448
|
+
#S(e) {
|
|
449
|
+
const { context: t, entry: n, textColor: o, labels: s, hpr: h, progress: r, bitmapWidth: u } = e;
|
|
338
450
|
if (r <= 0) return;
|
|
339
|
-
const { anchorX:
|
|
340
|
-
if (t.save(), t.globalAlpha =
|
|
341
|
-
t.save(), t.globalAlpha =
|
|
342
|
-
const
|
|
343
|
-
if (t.lineTo(
|
|
344
|
-
const
|
|
345
|
-
t.lineTo(
|
|
451
|
+
const { anchorX: m, anchorY: R, elbowX: g, elbowY: y, side: p, text: T, color: b, labelX: C, labelY: P } = n, f = C + p * s.legPad * h, v = U(r / 0.15, 0, 1), A = U((r - 0.15) / 0.45, 0, 1), S = U((r - 0.6) / 0.4, 0, 1);
|
|
452
|
+
if (t.save(), t.globalAlpha = v, t.fillStyle = b, t.beginPath(), t.arc(m, R, 2.5 * h, 0, H), t.fill(), t.restore(), A > 0) {
|
|
453
|
+
t.save(), t.globalAlpha = A, t.strokeStyle = b, t.lineWidth = 1 * h, t.lineCap = "round", t.beginPath(), t.moveTo(m, R);
|
|
454
|
+
const c = m + (g - m) * A, l = R + (y - R) * A;
|
|
455
|
+
if (t.lineTo(c, l), S > 0) {
|
|
456
|
+
const M = g + (C - g) * S, i = y + (P - y) * S;
|
|
457
|
+
t.lineTo(M, i);
|
|
346
458
|
}
|
|
347
459
|
t.stroke(), t.restore();
|
|
348
460
|
}
|
|
349
|
-
|
|
461
|
+
if (S > 0) {
|
|
462
|
+
const c = 2 * h, l = p >= 0 ? u - c - f : f - c, M = St(t, T, l);
|
|
463
|
+
if (M.length === 0) return;
|
|
464
|
+
t.save(), t.globalAlpha = S, t.fillStyle = o, t.textAlign = p >= 0 ? "left" : "right", t.textBaseline = "middle", t.fillText(M, f, P), t.restore();
|
|
465
|
+
}
|
|
350
466
|
}
|
|
351
467
|
/**
|
|
352
468
|
* Lay out one outside label per eligible slice. Returns entries in original
|
|
@@ -363,183 +479,204 @@ class ft {
|
|
|
363
479
|
* to the opposite side would mean flipping text direction without moving
|
|
364
480
|
* the label, which would push text across the pie.
|
|
365
481
|
*/
|
|
366
|
-
#
|
|
367
|
-
const { cx: t, cy:
|
|
368
|
-
let
|
|
369
|
-
const
|
|
370
|
-
for (let
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
373
|
-
const
|
|
374
|
-
anchorX:
|
|
375
|
-
anchorY:
|
|
376
|
-
elbowX:
|
|
377
|
-
elbowY:
|
|
378
|
-
side:
|
|
379
|
-
labelX:
|
|
380
|
-
labelY:
|
|
381
|
-
text: this.#
|
|
382
|
-
color: this.#
|
|
482
|
+
#R(e) {
|
|
483
|
+
const { cx: t, cy: n, outerR: o, total: s, palette: h, theme: r, labels: u, hpr: m, bitmapHeight: R } = e, g = new Array(this.#t.length).fill(null), y = u.elbowLen * m, p = Math.max(u.distance, u.elbowLen) * m, T = Math.max(0, u.railWidth) * m, b = [];
|
|
484
|
+
let C = -Math.PI / 2;
|
|
485
|
+
const P = u.minSliceAngle * et;
|
|
486
|
+
for (let S = 0; S < this.#t.length; S++) {
|
|
487
|
+
const c = this.#t[S], l = this.#i(c.value) / s * H;
|
|
488
|
+
if (l >= P) {
|
|
489
|
+
const M = C + l / 2, i = this.#s[S] * (8 * m), x = Math.cos(M), d = Math.sin(M), I = x >= 0 ? 1 : -1, Y = t + x * (o + i), k = n + d * (o + i), B = t + x * (o + i + y), V = n + d * (o + i + y), X = t + x * (o + i + p) + I * T, tt = n + d * (o + i + p), D = {
|
|
490
|
+
anchorX: Y,
|
|
491
|
+
anchorY: k,
|
|
492
|
+
elbowX: B,
|
|
493
|
+
elbowY: V,
|
|
494
|
+
side: I,
|
|
495
|
+
labelX: X,
|
|
496
|
+
labelY: tt,
|
|
497
|
+
text: this.#m(c, s, u.content),
|
|
498
|
+
color: this.#c({ index: S, palette: h, theme: r })
|
|
383
499
|
};
|
|
384
|
-
|
|
500
|
+
g[S] = D, b.push({ entry: D });
|
|
385
501
|
}
|
|
386
|
-
|
|
502
|
+
C += l;
|
|
387
503
|
}
|
|
388
|
-
const
|
|
389
|
-
for (const
|
|
390
|
-
const
|
|
391
|
-
for (const
|
|
392
|
-
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
for (let
|
|
397
|
-
for (
|
|
398
|
-
const
|
|
399
|
-
if (
|
|
400
|
-
|
|
504
|
+
const f = Math.max(1, u.fontSize * u.labelGap) * m, v = u.fontSize * m, A = R - u.fontSize * m;
|
|
505
|
+
for (const S of [1, -1]) {
|
|
506
|
+
const c = [];
|
|
507
|
+
for (const i of b)
|
|
508
|
+
i.entry.side === S && c.push(i.entry);
|
|
509
|
+
if (c.length === 0) continue;
|
|
510
|
+
c.sort((i, x) => i.labelY - x.labelY);
|
|
511
|
+
const l = [];
|
|
512
|
+
for (let i = 0; i < c.length; i++)
|
|
513
|
+
for (l.push({ count: 1, sumIdeal: c[i].labelY, startIdx: i }); l.length >= 2; ) {
|
|
514
|
+
const x = l[l.length - 2], d = l[l.length - 1], I = x.sumIdeal / x.count, Y = d.sumIdeal / d.count, k = I + (x.count - 1) * f / 2;
|
|
515
|
+
if (Y - (d.count - 1) * f / 2 >= k + f) break;
|
|
516
|
+
x.count += d.count, x.sumIdeal += d.sumIdeal, l.pop();
|
|
401
517
|
}
|
|
402
|
-
if (
|
|
403
|
-
const
|
|
404
|
-
if (
|
|
405
|
-
for (
|
|
406
|
-
const
|
|
407
|
-
if (
|
|
408
|
-
|
|
518
|
+
if (l.length > 0) {
|
|
519
|
+
const i = l[0], x = i.sumIdeal / i.count - (i.count - 1) * f / 2;
|
|
520
|
+
if (x < v)
|
|
521
|
+
for (i.sumIdeal += (v - x) * i.count; l.length >= 2; ) {
|
|
522
|
+
const d = l[0], I = l[1], Y = d.sumIdeal / d.count, k = I.sumIdeal / I.count, B = Y + (d.count - 1) * f / 2;
|
|
523
|
+
if (k - (I.count - 1) * f / 2 >= B + f) break;
|
|
524
|
+
d.count += I.count, d.sumIdeal += I.sumIdeal, l.splice(1, 1);
|
|
409
525
|
}
|
|
410
526
|
}
|
|
411
|
-
if (
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
414
|
-
for (
|
|
415
|
-
const
|
|
416
|
-
if (
|
|
417
|
-
|
|
527
|
+
if (l.length > 0) {
|
|
528
|
+
const i = l[l.length - 1], x = i.sumIdeal / i.count + (i.count - 1) * f / 2;
|
|
529
|
+
if (x > A)
|
|
530
|
+
for (i.sumIdeal -= (x - A) * i.count; l.length >= 2; ) {
|
|
531
|
+
const d = l[l.length - 2], I = l[l.length - 1], Y = d.sumIdeal / d.count, k = I.sumIdeal / I.count, B = Y + (d.count - 1) * f / 2;
|
|
532
|
+
if (k - (I.count - 1) * f / 2 >= B + f) break;
|
|
533
|
+
d.count += I.count, d.sumIdeal += I.sumIdeal, l.pop();
|
|
418
534
|
}
|
|
419
535
|
}
|
|
420
|
-
const
|
|
421
|
-
for (const
|
|
422
|
-
const
|
|
423
|
-
|
|
536
|
+
const M = (v + A) / 2;
|
|
537
|
+
for (const i of l) {
|
|
538
|
+
const x = (i.count - 1) * f, d = i.sumIdeal / i.count;
|
|
539
|
+
x > A - v && d - x / 2 < v && (i.sumIdeal = M * i.count);
|
|
424
540
|
}
|
|
425
|
-
for (const
|
|
426
|
-
const
|
|
427
|
-
for (let
|
|
428
|
-
|
|
541
|
+
for (const i of l) {
|
|
542
|
+
const x = i.sumIdeal / i.count;
|
|
543
|
+
for (let d = 0; d < i.count; d++)
|
|
544
|
+
c[i.startIdx + d].labelY = x + (d - (i.count - 1) / 2) * f;
|
|
429
545
|
}
|
|
430
546
|
}
|
|
431
|
-
for (const
|
|
432
|
-
const
|
|
433
|
-
let
|
|
434
|
-
if (Math.abs(
|
|
435
|
-
const
|
|
436
|
-
|
|
547
|
+
for (const S of b) {
|
|
548
|
+
const c = S.entry, l = c.labelY - c.anchorY;
|
|
549
|
+
let M;
|
|
550
|
+
if (Math.abs(l) <= y) {
|
|
551
|
+
const d = c.side * Math.sqrt(Math.max(0, y * y - l * l));
|
|
552
|
+
M = c.anchorX + d;
|
|
437
553
|
} else
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
|
|
554
|
+
M = (c.anchorX + c.labelX) / 2;
|
|
555
|
+
const i = Math.min(c.anchorX, c.labelX), x = Math.max(c.anchorX, c.labelX);
|
|
556
|
+
c.elbowX = Math.max(i, Math.min(x, M)), c.elbowY = c.labelY;
|
|
441
557
|
}
|
|
442
|
-
return
|
|
558
|
+
return g;
|
|
443
559
|
}
|
|
444
560
|
render(e) {
|
|
445
|
-
if (this.#t.length === 0)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
561
|
+
if (this.#t.length === 0) {
|
|
562
|
+
this.#x();
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const { scope: t, theme: n, padding: o } = e, { context: s, bitmapSize: h, horizontalPixelRatio: r, verticalPixelRatio: u } = t, m = performance.now(), R = this.#f ? Math.min(0.05, (m - this.#f) / 1e3) : 0;
|
|
566
|
+
this.#f = m;
|
|
567
|
+
const g = this.#d();
|
|
568
|
+
if (g <= 0) {
|
|
569
|
+
this.#x();
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
this.#r.tick(m);
|
|
573
|
+
const y = this.#r.active, p = -Math.PI / 2 + this.#r.sweep() * H, T = 12;
|
|
451
574
|
for (; this.#s.length < this.#t.length; )
|
|
452
575
|
this.#s.push(0);
|
|
453
576
|
if (this.#e.animate)
|
|
454
|
-
for (let
|
|
455
|
-
const
|
|
456
|
-
this.#s[
|
|
577
|
+
for (let w = 0; w < this.#t.length; w++) {
|
|
578
|
+
const O = w === this.#a ? 1 : 0;
|
|
579
|
+
this.#s[w] = nt(this.#s[w], O, T, R);
|
|
580
|
+
}
|
|
581
|
+
const b = it(this.#e.sliceLabels);
|
|
582
|
+
s.font = `${b.fontSize * r}px ${n.typography.fontFamily}`, b.mode === "outside" && !y && this.#o < 1 && (this.#o = nt(this.#o, 1, 6, R), this.#o >= 0.995 && (this.#o = 1));
|
|
583
|
+
const C = this.#e.colors ?? n.seriesColors;
|
|
584
|
+
let P = b.mode === "outside" ? this.#y(s, b, g, r) : 0;
|
|
585
|
+
const f = {
|
|
586
|
+
bitmapWidth: h.width,
|
|
587
|
+
bitmapHeight: h.height,
|
|
588
|
+
padTop: o.top * u,
|
|
589
|
+
padBottom: o.bottom * u
|
|
590
|
+
};
|
|
591
|
+
let { cx: v, cy: A, maxR: S } = st({ ...f, labelReserve: P }), c = b.mode === "outside";
|
|
592
|
+
c && S < yt * r && (c = !1, P = 0, { cx: v, cy: A, maxR: S } = st({ ...f, labelReserve: P })), this.#g = P;
|
|
593
|
+
const l = S, M = l * this.#e.innerRadiusRatio, i = this.#e.padAngle * et, x = (this.#e.cornerRadius ?? 3) * r, d = 8 * r, I = this.#e.shadow ?? !1, Y = bt(I), k = this.#e.innerShadow ?? !1, B = xt(k), V = this.#e.gradient !== !1;
|
|
594
|
+
let X = -Math.PI / 2;
|
|
595
|
+
for (let w = 0; w < this.#t.length; w++) {
|
|
596
|
+
const O = this.#t[w], $ = this.#i(O.value) / g * H, W = X + i / 2;
|
|
597
|
+
let L = X + $ - i / 2;
|
|
598
|
+
const _ = X + $ / 2, F = this.#c({ index: w, palette: C, theme: n });
|
|
599
|
+
if (y) {
|
|
600
|
+
if (W >= p) {
|
|
601
|
+
X += $;
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
L = Math.min(L, p);
|
|
457
605
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const X = this.#e.colors ?? o.seriesColors, $ = M.mode === "outside" ? this.#b(s, M, x, r) : 0, { cx: L, cy: A, maxR: b } = Z({
|
|
461
|
-
bitmapWidth: d.width,
|
|
462
|
-
bitmapHeight: d.height,
|
|
463
|
-
padTop: i.top * c,
|
|
464
|
-
padBottom: i.bottom * c,
|
|
465
|
-
labelReserve: $
|
|
466
|
-
}), I = b, S = I * this.#e.innerRadiusRatio, v = this.#e.padAngle * U, u = 8 * r, a = this.#e.shadow ?? !1, w = at(a), n = this.#e.innerShadow ?? !1, f = rt(n), h = this.#e.gradient !== !1;
|
|
467
|
-
let m = -Math.PI / 2;
|
|
468
|
-
for (let p = 0; p < this.#t.length; p++) {
|
|
469
|
-
const W = this.#t[p], y = this.#o(W.value) / x * N, Y = m + v / 2, B = m + y - v / 2, D = m + y / 2, F = this.#h({ index: p, palette: X, theme: o });
|
|
470
|
-
if (B <= Y) {
|
|
471
|
-
m += y;
|
|
606
|
+
if (L <= W) {
|
|
607
|
+
X += $;
|
|
472
608
|
continue;
|
|
473
609
|
}
|
|
474
|
-
const
|
|
475
|
-
if (s.beginPath(), s.arc(
|
|
476
|
-
const
|
|
477
|
-
if (
|
|
478
|
-
const G = Math.max(0, Math.min(0.999, 1 -
|
|
479
|
-
|
|
610
|
+
const K = this.#s[w] * d, q = Math.cos(_) * K, J = Math.sin(_) * K, N = v + q, E = A + J, j = x > 0.5 ? Math.min(x, vt({ outerR: l, innerR: M, halfSpan: (L - W) / 2 })) : 0;
|
|
611
|
+
if (s.beginPath(), j > 0.5 ? this.#v(s, { sliceCx: N, sliceCy: E, outerR: l, innerR: M, startAngle: W, endAngle: L, cornerRadius: j }) : (s.arc(N, E, l, W, L), M > 0 ? s.arc(N, E, M, L, W, !0) : s.lineTo(N, E)), s.closePath(), V || k) {
|
|
612
|
+
const z = s.createRadialGradient(N, E, M || 0, N, E, l);
|
|
613
|
+
if (z.addColorStop(0, V ? Rt(F, 0.15) : F), k) {
|
|
614
|
+
const G = Math.max(0, Math.min(0.999, 1 - B.depth));
|
|
615
|
+
z.addColorStop(G, F), z.addColorStop(1, wt(F, B.color));
|
|
480
616
|
} else
|
|
481
|
-
|
|
482
|
-
s.fillStyle =
|
|
617
|
+
z.addColorStop(1, F);
|
|
618
|
+
s.fillStyle = z;
|
|
483
619
|
} else
|
|
484
620
|
s.fillStyle = F;
|
|
485
|
-
const
|
|
486
|
-
if (
|
|
487
|
-
const
|
|
488
|
-
s.shadowColor =
|
|
621
|
+
const Q = this.#s[w];
|
|
622
|
+
if (I || Q > 0.01) {
|
|
623
|
+
const z = I ? 1 : 0, G = Q;
|
|
624
|
+
s.shadowColor = I ? Y.color : "rgba(0,0,0,0.25)", s.shadowBlur = (Y.blur * z + 12 * G) * r, s.shadowOffsetX = Y.offsetX * z * r + q * 0.3 * G, s.shadowOffsetY = Y.offsetY * z * u + J * 0.3 * G;
|
|
489
625
|
}
|
|
490
|
-
s.fill(), s.shadowColor = "transparent", s.shadowBlur = 0, s.shadowOffsetX = 0, s.shadowOffsetY = 0,
|
|
626
|
+
s.fill(), s.shadowColor = "transparent", s.shadowBlur = 0, s.shadowOffsetX = 0, s.shadowOffsetY = 0, X += $;
|
|
491
627
|
}
|
|
492
|
-
if (
|
|
493
|
-
|
|
494
|
-
const
|
|
495
|
-
if (
|
|
496
|
-
const
|
|
497
|
-
cx:
|
|
628
|
+
if (b.mode === "none" || b.mode === "outside" && !c) return;
|
|
629
|
+
X = -Math.PI / 2;
|
|
630
|
+
const tt = n.tooltip.textColor;
|
|
631
|
+
if (b.mode === "outside") {
|
|
632
|
+
const w = this.#R({
|
|
633
|
+
cx: v,
|
|
498
634
|
cy: A,
|
|
499
|
-
outerR:
|
|
500
|
-
total:
|
|
501
|
-
palette:
|
|
502
|
-
theme:
|
|
503
|
-
labels:
|
|
635
|
+
outerR: l,
|
|
636
|
+
total: g,
|
|
637
|
+
palette: C,
|
|
638
|
+
theme: n,
|
|
639
|
+
labels: b,
|
|
504
640
|
hpr: r,
|
|
505
|
-
bitmapHeight:
|
|
506
|
-
}),
|
|
507
|
-
for (let
|
|
508
|
-
const
|
|
509
|
-
if (
|
|
510
|
-
const
|
|
511
|
-
this.#
|
|
641
|
+
bitmapHeight: h.height
|
|
642
|
+
}), O = w.length > 1 ? Math.min(0.06, 0.9 / (w.length - 1)) : 0;
|
|
643
|
+
for (let $ = 0; $ < w.length; $++) {
|
|
644
|
+
const W = w[$];
|
|
645
|
+
if (W === null) continue;
|
|
646
|
+
const L = $ * O, _ = U((this.#o - L) / (1 - L), 0, 1);
|
|
647
|
+
this.#S({
|
|
512
648
|
context: s,
|
|
513
|
-
entry:
|
|
514
|
-
textColor:
|
|
515
|
-
labels:
|
|
649
|
+
entry: W,
|
|
650
|
+
textColor: tt,
|
|
651
|
+
labels: b,
|
|
516
652
|
hpr: r,
|
|
517
|
-
progress:
|
|
653
|
+
progress: _,
|
|
654
|
+
bitmapWidth: h.width
|
|
518
655
|
});
|
|
519
656
|
}
|
|
520
657
|
return;
|
|
521
658
|
}
|
|
522
|
-
const
|
|
659
|
+
const D = M > 0 ? (l + M) / 2 : l * 0.65;
|
|
523
660
|
s.textAlign = "center", s.textBaseline = "middle";
|
|
524
|
-
const
|
|
525
|
-
for (let
|
|
526
|
-
const
|
|
527
|
-
if (
|
|
528
|
-
const
|
|
529
|
-
if (!
|
|
530
|
-
const
|
|
531
|
-
s.fillStyle =
|
|
661
|
+
const ht = b.minSliceAngle * et;
|
|
662
|
+
for (let w = 0; w < this.#t.length; w++) {
|
|
663
|
+
const O = this.#t[w], $ = this.#i(O.value) / g * H;
|
|
664
|
+
if ($ >= ht) {
|
|
665
|
+
const W = X + $ / 2, L = this.#s[w] * d, _ = this.#c({ index: w, palette: C, theme: n }), F = this.#m(O, g, b.content), K = s.measureText(F).width, q = b.fontSize * 1.2 * r, J = $ < Math.PI ? 2 * D * Math.sin($ / 2) : 2 * D, N = K > J - 4 * r, E = M > 0 && D - M < q, j = y ? U((p - (X + $)) / 0.35, 0, 1) : 1;
|
|
666
|
+
if (!N && !E && j > 0) {
|
|
667
|
+
const Q = v + Math.cos(W) * (D + L), z = A + Math.sin(W) * (D + L), G = j < 1;
|
|
668
|
+
G && (s.save(), s.globalAlpha *= j), s.fillStyle = ft(_) ? "#000000" : "#ffffff", s.fillText(F, Q, z), G && s.restore();
|
|
532
669
|
}
|
|
533
670
|
}
|
|
534
|
-
|
|
671
|
+
X += $;
|
|
535
672
|
}
|
|
536
673
|
}
|
|
537
674
|
}
|
|
538
|
-
const
|
|
675
|
+
const Pt = {
|
|
539
676
|
type: "pie",
|
|
540
|
-
create: (
|
|
677
|
+
create: (a, e) => new It({ ...e })
|
|
541
678
|
};
|
|
542
679
|
export {
|
|
543
|
-
|
|
544
|
-
|
|
680
|
+
It as PieRenderer,
|
|
681
|
+
Pt as PieSeriesDef
|
|
545
682
|
};
|