resolvcharts 0.3.0 → 0.4.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 +53 -3
- package/dist/index.cjs +569 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +8 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +569 -124
- package/dist/index.mjs.map +1 -1
- package/dist/resolvcharts.global.min.js +14 -14
- package/dist/resolvcharts.global.min.js.map +1 -1
- package/package.json +14 -17
package/dist/index.mjs
CHANGED
|
@@ -462,12 +462,12 @@ let smScratch = /* @__PURE__ */ new Float64Array(0);
|
|
|
462
462
|
* Monotone cubic path (Fritsch–Carlson tangents) — never overshoots data.
|
|
463
463
|
* `pts` is interleaved [x0, y0, x1, y1, ...] with `n` points.
|
|
464
464
|
*/
|
|
465
|
-
function smoothPath(ctx, pts, n) {
|
|
465
|
+
function smoothPath(ctx, pts, n, connect = false) {
|
|
466
466
|
if (n < 3) {
|
|
467
467
|
for (let i = 0; i < n; i++) {
|
|
468
468
|
const x = pts[2 * i];
|
|
469
469
|
const y = pts[2 * i + 1];
|
|
470
|
-
if (i === 0) ctx.moveTo(x, y);
|
|
470
|
+
if (i === 0 && !connect) ctx.moveTo(x, y);
|
|
471
471
|
else ctx.lineTo(x, y);
|
|
472
472
|
}
|
|
473
473
|
return;
|
|
@@ -498,7 +498,8 @@ function smoothPath(ctx, pts, n) {
|
|
|
498
498
|
if (Math.abs(ti1) > lim) tang[i + 1] = Math.sign(ti1) * lim;
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
|
-
ctx.
|
|
501
|
+
if (connect) ctx.lineTo(pts[0], pts[1]);
|
|
502
|
+
else ctx.moveTo(pts[0], pts[1]);
|
|
502
503
|
for (let i = 0; i < segs; i++) {
|
|
503
504
|
const x0 = pts[2 * i];
|
|
504
505
|
const x1 = pts[2 * (i + 1)];
|
|
@@ -508,6 +509,27 @@ function smoothPath(ctx, pts, n) {
|
|
|
508
509
|
ctx.bezierCurveTo(x0 + h, y0 + h * tang[i], x1 - h, y1 - h * tang[i + 1], x1, y1);
|
|
509
510
|
}
|
|
510
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Trace `n` interleaved [x, y] pixel points into `path` honoring `curve`
|
|
514
|
+
* (linear / step / smooth). `connect` continues the current subpath with a
|
|
515
|
+
* lineTo instead of starting a new one — used for closed band shapes.
|
|
516
|
+
*/
|
|
517
|
+
function traceCurve(path, pts, n, curve, connect = false) {
|
|
518
|
+
if (n === 0) return;
|
|
519
|
+
const join = (x, y) => connect ? path.lineTo(x, y) : path.moveTo(x, y);
|
|
520
|
+
if (curve === "step") {
|
|
521
|
+
join(pts[0], pts[1]);
|
|
522
|
+
for (let i = 1; i < n; i++) {
|
|
523
|
+
const x = pts[2 * i];
|
|
524
|
+
path.lineTo(x, pts[2 * i - 1]);
|
|
525
|
+
path.lineTo(x, pts[2 * i + 1]);
|
|
526
|
+
}
|
|
527
|
+
} else if (curve === "smooth") smoothPath(path, pts, n, connect);
|
|
528
|
+
else {
|
|
529
|
+
join(pts[0], pts[1]);
|
|
530
|
+
for (let i = 1; i < n; i++) path.lineTo(pts[2 * i], pts[2 * i + 1]);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
511
533
|
//#endregion
|
|
512
534
|
//#region src/charts/line.ts
|
|
513
535
|
/** fraction of the tween the trailing edge lags behind the leading point */
|
|
@@ -536,19 +558,7 @@ function buildPath(r, s, yFallback) {
|
|
|
536
558
|
lastX: 0,
|
|
537
559
|
lastY: 0
|
|
538
560
|
};
|
|
539
|
-
|
|
540
|
-
if (curve === "step") {
|
|
541
|
-
path.moveTo(pts[0], pts[1]);
|
|
542
|
-
for (let i = 1; i < dn; i++) {
|
|
543
|
-
const x = pts[2 * i];
|
|
544
|
-
path.lineTo(x, pts[2 * i - 1]);
|
|
545
|
-
path.lineTo(x, pts[2 * i + 1]);
|
|
546
|
-
}
|
|
547
|
-
} else if (curve === "smooth") smoothPath(path, pts, dn);
|
|
548
|
-
else {
|
|
549
|
-
path.moveTo(pts[0], pts[1]);
|
|
550
|
-
for (let i = 1; i < dn; i++) path.lineTo(pts[2 * i], pts[2 * i + 1]);
|
|
551
|
-
}
|
|
561
|
+
traceCurve(path, pts, dn, r.opts.curve ?? "linear");
|
|
552
562
|
return {
|
|
553
563
|
path,
|
|
554
564
|
firstX: pts[0],
|
|
@@ -675,6 +685,72 @@ function renderBaseline(r) {
|
|
|
675
685
|
ctx.setLineDash([]);
|
|
676
686
|
}
|
|
677
687
|
//#endregion
|
|
688
|
+
//#region src/charts/bubble.ts
|
|
689
|
+
const MIN_R = 3;
|
|
690
|
+
const MAX_R = 16;
|
|
691
|
+
/**
|
|
692
|
+
* Bubble chart: scatter points sized by each series' `r` channel, mapped
|
|
693
|
+
* linearly to [MIN_R, MAX_R] px across the dataset's radius extent.
|
|
694
|
+
*/
|
|
695
|
+
function renderBubble(r) {
|
|
696
|
+
if (!isXY(r.data)) return;
|
|
697
|
+
const d = r.data;
|
|
698
|
+
const ctx = r.ctx;
|
|
699
|
+
const t = r.t;
|
|
700
|
+
const lerp = t < 1;
|
|
701
|
+
const fallback = r.yScale.domain[0];
|
|
702
|
+
let lo = Infinity;
|
|
703
|
+
let hi = -Infinity;
|
|
704
|
+
for (const s of d.series) {
|
|
705
|
+
const rr = s.r;
|
|
706
|
+
if (!rr) continue;
|
|
707
|
+
for (let i = 0; i < rr.length; i++) {
|
|
708
|
+
const v = rr[i];
|
|
709
|
+
if (v < lo) lo = v;
|
|
710
|
+
if (v > hi) hi = v;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (lo === Infinity) {
|
|
714
|
+
lo = 0;
|
|
715
|
+
hi = 1;
|
|
716
|
+
}
|
|
717
|
+
const span = hi - lo || 1;
|
|
718
|
+
const grow = lerp ? t : 1;
|
|
719
|
+
const rad = (v) => (MIN_R + (v - lo) / span * 13) * grow;
|
|
720
|
+
const xMin = r.plot.x - MAX_R;
|
|
721
|
+
const xMax = r.plot.x + r.plot.w + MAX_R;
|
|
722
|
+
ctx.save();
|
|
723
|
+
ctx.beginPath();
|
|
724
|
+
ctx.rect(r.plot.x, r.plot.y, r.plot.w, r.plot.h);
|
|
725
|
+
ctx.clip();
|
|
726
|
+
for (const s of d.series) {
|
|
727
|
+
const y = s.y;
|
|
728
|
+
const prev = s.prevY;
|
|
729
|
+
const rr = s.r;
|
|
730
|
+
ctx.fillStyle = withAlpha(s.color, .45);
|
|
731
|
+
ctx.strokeStyle = s.color;
|
|
732
|
+
ctx.lineWidth = 1.5;
|
|
733
|
+
ctx.beginPath();
|
|
734
|
+
for (let i = 0; i < d.sortedX.length; i++) {
|
|
735
|
+
const x = r.xScale(d.sortedX[i]);
|
|
736
|
+
if (x < xMin || x > xMax) continue;
|
|
737
|
+
let yv = y[i];
|
|
738
|
+
if (lerp) {
|
|
739
|
+
const p = prev ? prev[i] : fallback;
|
|
740
|
+
yv = p + (yv - p) * t;
|
|
741
|
+
}
|
|
742
|
+
const yp = r.yScale(yv);
|
|
743
|
+
const pr = rad(rr ? rr[i] : lo + span / 2);
|
|
744
|
+
if (pr <= 0) continue;
|
|
745
|
+
ctx.moveTo(x + pr, yp);
|
|
746
|
+
ctx.arc(x, yp, pr, 0, Math.PI * 2);
|
|
747
|
+
}
|
|
748
|
+
ctx.fill();
|
|
749
|
+
ctx.stroke();
|
|
750
|
+
}
|
|
751
|
+
ctx.restore();
|
|
752
|
+
}
|
|
753
|
+
//#endregion
|
|
678
754
|
//#region src/charts/candle.ts
|
|
679
755
|
function renderCandles(r, ohlcStyle) {
|
|
680
756
|
if (r.data.kind !== "ohlc") return;
|
|
@@ -849,20 +925,7 @@ function hitCandle(r, px) {
|
|
|
849
925
|
let ovScratch = /* @__PURE__ */ new Float64Array(0);
|
|
850
926
|
function overlayPath(r, pts, n) {
|
|
851
927
|
const path = new Path2D();
|
|
852
|
-
|
|
853
|
-
const curve = r.opts.curve ?? "linear";
|
|
854
|
-
if (curve === "step") {
|
|
855
|
-
path.moveTo(pts[0], pts[1]);
|
|
856
|
-
for (let i = 1; i < n; i++) {
|
|
857
|
-
const x = pts[2 * i];
|
|
858
|
-
path.lineTo(x, pts[2 * i - 1]);
|
|
859
|
-
path.lineTo(x, pts[2 * i + 1]);
|
|
860
|
-
}
|
|
861
|
-
} else if (curve === "smooth") smoothPath(path, pts, n);
|
|
862
|
-
else {
|
|
863
|
-
path.moveTo(pts[0], pts[1]);
|
|
864
|
-
for (let i = 1; i < n; i++) path.lineTo(pts[2 * i], pts[2 * i + 1]);
|
|
865
|
-
}
|
|
928
|
+
traceCurve(path, pts, n, r.opts.curve ?? "linear");
|
|
866
929
|
return path;
|
|
867
930
|
}
|
|
868
931
|
function renderCombo(r) {
|
|
@@ -1015,14 +1078,16 @@ function formatSI(v) {
|
|
|
1015
1078
|
//#region src/charts/donut.ts
|
|
1016
1079
|
const INNER = .65;
|
|
1017
1080
|
const GAP = .015;
|
|
1018
|
-
const START = -Math.PI / 2;
|
|
1081
|
+
const START$1 = -Math.PI / 2;
|
|
1082
|
+
/** donut keeps a center hole; pie fills to the middle */
|
|
1083
|
+
const innerRatio = (r) => r.opts.type === "pie" ? 0 : INNER;
|
|
1019
1084
|
function donutGeometry(r, d, t) {
|
|
1020
1085
|
const cx = r.plot.x + r.plot.w / 2;
|
|
1021
1086
|
const cy = r.plot.y + r.plot.h / 2;
|
|
1022
1087
|
const rOuter = Math.max(1, Math.min(r.plot.w, r.plot.h) / 2);
|
|
1023
|
-
const rInner = rOuter *
|
|
1088
|
+
const rInner = rOuter * innerRatio(r);
|
|
1024
1089
|
const angles = [];
|
|
1025
|
-
let a = START;
|
|
1090
|
+
let a = START$1;
|
|
1026
1091
|
const sweep = Math.PI * 2 * Math.min(1, Math.max(0, t));
|
|
1027
1092
|
for (const s of d.slices) {
|
|
1028
1093
|
const frac = d.total > 0 ? s.value / d.total : 0;
|
|
@@ -1055,12 +1120,13 @@ function renderDonut(r) {
|
|
|
1055
1120
|
if (a1 <= a0) continue;
|
|
1056
1121
|
ctx.beginPath();
|
|
1057
1122
|
ctx.arc(geo.cx, geo.cy, geo.rOuter, a0, a1);
|
|
1058
|
-
ctx.arc(geo.cx, geo.cy, geo.rInner, a1, a0, true);
|
|
1123
|
+
if (geo.rInner > 0) ctx.arc(geo.cx, geo.cy, geo.rInner, a1, a0, true);
|
|
1124
|
+
else ctx.lineTo(geo.cx, geo.cy);
|
|
1059
1125
|
ctx.closePath();
|
|
1060
1126
|
ctx.fillStyle = s.color;
|
|
1061
1127
|
ctx.fill();
|
|
1062
1128
|
}
|
|
1063
|
-
if (d.slices.length > 0 && r.t > .5) {
|
|
1129
|
+
if (r.opts.type !== "pie" && d.slices.length > 0 && r.t > .5) {
|
|
1064
1130
|
ctx.fillStyle = r.theme.text;
|
|
1065
1131
|
ctx.font = `600 20px ${r.theme.fontFamily}`;
|
|
1066
1132
|
ctx.textAlign = "center";
|
|
@@ -1077,8 +1143,8 @@ function hitDonut(r, px, py) {
|
|
|
1077
1143
|
const dist = Math.hypot(dx, dy);
|
|
1078
1144
|
if (dist < geo.rInner || dist > geo.rOuter) return null;
|
|
1079
1145
|
let angle = Math.atan2(dy, dx);
|
|
1080
|
-
while (angle < START) angle += Math.PI * 2;
|
|
1081
|
-
while (angle >= START + Math.PI * 2) angle -= Math.PI * 2;
|
|
1146
|
+
while (angle < START$1) angle += Math.PI * 2;
|
|
1147
|
+
while (angle >= START$1 + Math.PI * 2) angle -= Math.PI * 2;
|
|
1082
1148
|
for (let i = 0; i < geo.angles.length; i++) {
|
|
1083
1149
|
const an = geo.angles[i];
|
|
1084
1150
|
const s = d.slices[i];
|
|
@@ -1097,6 +1163,389 @@ function hitDonut(r, px, py) {
|
|
|
1097
1163
|
return null;
|
|
1098
1164
|
}
|
|
1099
1165
|
//#endregion
|
|
1166
|
+
//#region src/core/theme.ts
|
|
1167
|
+
const FONT = "Inter, system-ui, -apple-system, \"Segoe UI\", sans-serif";
|
|
1168
|
+
/** Built-in light theme.
|
|
1169
|
+
* @category Theming
|
|
1170
|
+
*/
|
|
1171
|
+
const lightTheme = {
|
|
1172
|
+
bg: "#ffffff",
|
|
1173
|
+
text: "#1f1f1f",
|
|
1174
|
+
textMuted: "#8a8a8a",
|
|
1175
|
+
grid: "rgba(0,0,0,0.07)",
|
|
1176
|
+
axisBaseline: "rgba(0,0,0,0.15)",
|
|
1177
|
+
crosshair: "rgba(0,0,0,0.25)",
|
|
1178
|
+
series: ["#0070f3"],
|
|
1179
|
+
contextGray: "#b0b0b0",
|
|
1180
|
+
up: "#0e9f6e",
|
|
1181
|
+
down: "#e5484d",
|
|
1182
|
+
tooltipBg: "#ffffff",
|
|
1183
|
+
tooltipText: "#1f1f1f",
|
|
1184
|
+
tooltipMuted: "#8a8a8a",
|
|
1185
|
+
tooltipBorder: "rgba(0,0,0,0.08)",
|
|
1186
|
+
tooltipShadow: "0 4px 14px rgba(0,0,0,0.08)",
|
|
1187
|
+
fontFamily: FONT
|
|
1188
|
+
};
|
|
1189
|
+
/** Built-in dark theme.
|
|
1190
|
+
* @category Theming
|
|
1191
|
+
*/
|
|
1192
|
+
const darkTheme = {
|
|
1193
|
+
bg: "#0a0a0a",
|
|
1194
|
+
text: "#ededed",
|
|
1195
|
+
textMuted: "#8a8a8a",
|
|
1196
|
+
grid: "rgba(255,255,255,0.07)",
|
|
1197
|
+
axisBaseline: "rgba(255,255,255,0.18)",
|
|
1198
|
+
crosshair: "rgba(255,255,255,0.3)",
|
|
1199
|
+
series: ["#3b9dff"],
|
|
1200
|
+
contextGray: "#5c5c5c",
|
|
1201
|
+
up: "#34c98e",
|
|
1202
|
+
down: "#f2555a",
|
|
1203
|
+
tooltipBg: "#161616",
|
|
1204
|
+
tooltipText: "#ededed",
|
|
1205
|
+
tooltipMuted: "#8a8a8a",
|
|
1206
|
+
tooltipBorder: "rgba(255,255,255,0.1)",
|
|
1207
|
+
tooltipShadow: "0 4px 14px rgba(0,0,0,0.4)",
|
|
1208
|
+
fontFamily: FONT
|
|
1209
|
+
};
|
|
1210
|
+
const categoricalLight = [
|
|
1211
|
+
"#0070f3",
|
|
1212
|
+
"#0e9f6e",
|
|
1213
|
+
"#8e4ec6",
|
|
1214
|
+
"#f5a623"
|
|
1215
|
+
];
|
|
1216
|
+
const categoricalDark = [
|
|
1217
|
+
"#3b9dff",
|
|
1218
|
+
"#34c98e",
|
|
1219
|
+
"#a97ef0",
|
|
1220
|
+
"#f5b84d"
|
|
1221
|
+
];
|
|
1222
|
+
/** Merge a theme option (`"light" | "dark" | partial tokens`) into full {@link ThemeTokens}.
|
|
1223
|
+
* @category Theming
|
|
1224
|
+
*/
|
|
1225
|
+
function resolveTheme(opt, accent) {
|
|
1226
|
+
const base = opt === "dark" ? {
|
|
1227
|
+
...darkTheme,
|
|
1228
|
+
series: [...categoricalDark]
|
|
1229
|
+
} : opt === "light" || opt === void 0 ? {
|
|
1230
|
+
...lightTheme,
|
|
1231
|
+
series: [...categoricalLight]
|
|
1232
|
+
} : {
|
|
1233
|
+
...lightTheme,
|
|
1234
|
+
series: [...categoricalLight],
|
|
1235
|
+
...opt
|
|
1236
|
+
};
|
|
1237
|
+
if (accent) base.series[0] = accent;
|
|
1238
|
+
return base;
|
|
1239
|
+
}
|
|
1240
|
+
function seriesColor(theme, index, explicit) {
|
|
1241
|
+
if (explicit) return explicit;
|
|
1242
|
+
return theme.series[index % theme.series.length];
|
|
1243
|
+
}
|
|
1244
|
+
//#endregion
|
|
1245
|
+
//#region src/charts/funnel.ts
|
|
1246
|
+
const ROW_GAP = 2;
|
|
1247
|
+
const LABEL_MIN_W = 72;
|
|
1248
|
+
/**
|
|
1249
|
+
* Funnel: one stage per `categories` entry using the first series' values.
|
|
1250
|
+
* Each stage is a trapezoid whose bottom edge matches the next stage's top
|
|
1251
|
+
* width — the classic funnel silhouette.
|
|
1252
|
+
*/
|
|
1253
|
+
function stages(r, d, t) {
|
|
1254
|
+
const s = d.series[0];
|
|
1255
|
+
if (!s) return [];
|
|
1256
|
+
const n = d.categories.length;
|
|
1257
|
+
let max = 0;
|
|
1258
|
+
for (let i = 0; i < s.values.length; i++) {
|
|
1259
|
+
const v = s.values[i];
|
|
1260
|
+
if (v > max) max = v;
|
|
1261
|
+
}
|
|
1262
|
+
max ||= 1;
|
|
1263
|
+
const rowH = r.plot.h / Math.max(1, n);
|
|
1264
|
+
const out = [];
|
|
1265
|
+
for (let i = 0; i < n; i++) {
|
|
1266
|
+
const cur = s.values[i] ?? 0;
|
|
1267
|
+
const prev = s.prevValues?.[i] ?? 0;
|
|
1268
|
+
const v = t < 1 ? prev + (cur - prev) * t : cur;
|
|
1269
|
+
const cur1 = s.values[i + 1] ?? cur;
|
|
1270
|
+
const prev1 = s.prevValues?.[i + 1] ?? 0;
|
|
1271
|
+
const v1 = i + 1 < n ? t < 1 ? prev1 + (cur1 - prev1) * t : cur1 : v;
|
|
1272
|
+
out.push({
|
|
1273
|
+
cat: d.categories[i] ?? "",
|
|
1274
|
+
value: cur,
|
|
1275
|
+
top: r.plot.y + rowH * i,
|
|
1276
|
+
bot: r.plot.y + rowH * (i + 1) - ROW_GAP,
|
|
1277
|
+
w0: Math.max(0, v) / max * r.plot.w,
|
|
1278
|
+
w1: Math.max(0, v1) / max * r.plot.w,
|
|
1279
|
+
color: seriesColor(r.theme, i, void 0)
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
return out;
|
|
1283
|
+
}
|
|
1284
|
+
function stageRect(r, st) {
|
|
1285
|
+
const cx = r.plot.x + r.plot.w / 2;
|
|
1286
|
+
const w = Math.max(st.w0, st.w1);
|
|
1287
|
+
return {
|
|
1288
|
+
x: cx - w / 2,
|
|
1289
|
+
y: st.top,
|
|
1290
|
+
w,
|
|
1291
|
+
h: st.bot - st.top
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function renderFunnel(r) {
|
|
1295
|
+
if (r.data.kind !== "category") return;
|
|
1296
|
+
const d = r.data;
|
|
1297
|
+
const ctx = r.ctx;
|
|
1298
|
+
const cx = r.plot.x + r.plot.w / 2;
|
|
1299
|
+
for (const st of stages(r, d, r.t)) {
|
|
1300
|
+
ctx.beginPath();
|
|
1301
|
+
ctx.moveTo(cx - st.w0 / 2, st.top);
|
|
1302
|
+
ctx.lineTo(cx + st.w0 / 2, st.top);
|
|
1303
|
+
ctx.lineTo(cx + st.w1 / 2, st.bot);
|
|
1304
|
+
ctx.lineTo(cx - st.w1 / 2, st.bot);
|
|
1305
|
+
ctx.closePath();
|
|
1306
|
+
ctx.fillStyle = st.color;
|
|
1307
|
+
ctx.fill();
|
|
1308
|
+
const label = `${st.cat} · ${formatSI(st.value)}`;
|
|
1309
|
+
ctx.font = `11px ${r.theme.fontFamily}`;
|
|
1310
|
+
ctx.textBaseline = "middle";
|
|
1311
|
+
if (Math.min(st.w0, st.w1) >= LABEL_MIN_W) {
|
|
1312
|
+
ctx.fillStyle = "#ffffff";
|
|
1313
|
+
ctx.textAlign = "center";
|
|
1314
|
+
ctx.fillText(label, cx, (st.top + st.bot) / 2);
|
|
1315
|
+
} else {
|
|
1316
|
+
ctx.fillStyle = r.theme.textMuted;
|
|
1317
|
+
ctx.textAlign = "left";
|
|
1318
|
+
ctx.fillText(label, cx + Math.max(st.w0, st.w1) / 2 + 8, (st.top + st.bot) / 2);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
function hitFunnel(r, px, py) {
|
|
1323
|
+
if (r.data.kind !== "category") return null;
|
|
1324
|
+
const d = r.data;
|
|
1325
|
+
const s = d.series[0];
|
|
1326
|
+
if (!s) return null;
|
|
1327
|
+
const cx = r.plot.x + r.plot.w / 2;
|
|
1328
|
+
for (const st of stages(r, d, 1)) {
|
|
1329
|
+
if (py < st.top || py > st.bot) continue;
|
|
1330
|
+
const frac = st.bot - st.top > 0 ? (py - st.top) / (st.bot - st.top) : 0;
|
|
1331
|
+
const w = st.w0 + (st.w1 - st.w0) * frac;
|
|
1332
|
+
if (Math.abs(px - cx) > w / 2 + 4) continue;
|
|
1333
|
+
return {
|
|
1334
|
+
kind: "bar",
|
|
1335
|
+
category: st.cat,
|
|
1336
|
+
seriesIndex: 0,
|
|
1337
|
+
label: s.label,
|
|
1338
|
+
color: st.color,
|
|
1339
|
+
value: st.value,
|
|
1340
|
+
px: cx,
|
|
1341
|
+
py,
|
|
1342
|
+
rect: stageRect(r, st)
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
//#endregion
|
|
1348
|
+
//#region src/charts/radar.ts
|
|
1349
|
+
const RINGS = 4;
|
|
1350
|
+
const LABEL_PAD = 16;
|
|
1351
|
+
const MIN_AXES = 3;
|
|
1352
|
+
const START = -Math.PI / 2;
|
|
1353
|
+
function radarGeo(r, d) {
|
|
1354
|
+
const cx = r.plot.x + r.plot.w / 2;
|
|
1355
|
+
const cy = r.plot.y + r.plot.h / 2;
|
|
1356
|
+
const radius = Math.max(1, Math.min(r.plot.w, r.plot.h) / 2 - LABEL_PAD);
|
|
1357
|
+
let max = 0;
|
|
1358
|
+
for (const s of d.series) for (let i = 0; i < s.values.length; i++) {
|
|
1359
|
+
const v = s.values[i];
|
|
1360
|
+
if (v > max) max = v;
|
|
1361
|
+
}
|
|
1362
|
+
return {
|
|
1363
|
+
cx,
|
|
1364
|
+
cy,
|
|
1365
|
+
radius,
|
|
1366
|
+
max: max || 1
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
const angleOf = (i, n) => START + i / n * Math.PI * 2;
|
|
1370
|
+
function renderRadar(r) {
|
|
1371
|
+
if (r.data.kind !== "category") return;
|
|
1372
|
+
const d = r.data;
|
|
1373
|
+
const n = d.categories.length;
|
|
1374
|
+
if (n < MIN_AXES) return;
|
|
1375
|
+
const ctx = r.ctx;
|
|
1376
|
+
const { cx, cy, radius, max } = radarGeo(r, d);
|
|
1377
|
+
const animate = r.t < 1;
|
|
1378
|
+
ctx.strokeStyle = r.theme.grid;
|
|
1379
|
+
ctx.lineWidth = 1;
|
|
1380
|
+
for (let ring = 1; ring <= RINGS; ring++) {
|
|
1381
|
+
const rr = radius * ring / RINGS;
|
|
1382
|
+
ctx.beginPath();
|
|
1383
|
+
for (let i = 0; i <= n; i++) {
|
|
1384
|
+
const a = angleOf(i % n, n);
|
|
1385
|
+
const x = cx + Math.cos(a) * rr;
|
|
1386
|
+
const y = cy + Math.sin(a) * rr;
|
|
1387
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
1388
|
+
else ctx.lineTo(x, y);
|
|
1389
|
+
}
|
|
1390
|
+
ctx.stroke();
|
|
1391
|
+
}
|
|
1392
|
+
ctx.beginPath();
|
|
1393
|
+
for (let i = 0; i < n; i++) {
|
|
1394
|
+
const a = angleOf(i, n);
|
|
1395
|
+
ctx.moveTo(cx, cy);
|
|
1396
|
+
ctx.lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
|
|
1397
|
+
}
|
|
1398
|
+
ctx.stroke();
|
|
1399
|
+
for (const s of d.series) {
|
|
1400
|
+
ctx.beginPath();
|
|
1401
|
+
for (let i = 0; i < n; i++) {
|
|
1402
|
+
const cur = s.values[i] ?? 0;
|
|
1403
|
+
const prev = s.prevValues?.[i] ?? 0;
|
|
1404
|
+
const v = animate ? prev + (cur - prev) * r.t : cur;
|
|
1405
|
+
const rr = Math.max(0, v) / max * radius;
|
|
1406
|
+
const a = angleOf(i, n);
|
|
1407
|
+
const x = cx + Math.cos(a) * rr;
|
|
1408
|
+
const y = cy + Math.sin(a) * rr;
|
|
1409
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
1410
|
+
else ctx.lineTo(x, y);
|
|
1411
|
+
}
|
|
1412
|
+
ctx.closePath();
|
|
1413
|
+
ctx.fillStyle = withAlpha(s.color, .16);
|
|
1414
|
+
ctx.fill();
|
|
1415
|
+
ctx.strokeStyle = s.color;
|
|
1416
|
+
ctx.lineWidth = 2;
|
|
1417
|
+
ctx.lineJoin = "round";
|
|
1418
|
+
ctx.stroke();
|
|
1419
|
+
ctx.fillStyle = s.color;
|
|
1420
|
+
ctx.beginPath();
|
|
1421
|
+
for (let i = 0; i < n; i++) {
|
|
1422
|
+
const cur = s.values[i] ?? 0;
|
|
1423
|
+
const prev = s.prevValues?.[i] ?? 0;
|
|
1424
|
+
const v = animate ? prev + (cur - prev) * r.t : cur;
|
|
1425
|
+
const rr = Math.max(0, v) / max * radius;
|
|
1426
|
+
const a = angleOf(i, n);
|
|
1427
|
+
const x = cx + Math.cos(a) * rr;
|
|
1428
|
+
const y = cy + Math.sin(a) * rr;
|
|
1429
|
+
ctx.moveTo(x + 2.5, y);
|
|
1430
|
+
ctx.arc(x, y, 2.5, 0, Math.PI * 2);
|
|
1431
|
+
}
|
|
1432
|
+
ctx.fill();
|
|
1433
|
+
}
|
|
1434
|
+
ctx.fillStyle = r.theme.textMuted;
|
|
1435
|
+
ctx.font = `11px ${r.theme.fontFamily}`;
|
|
1436
|
+
for (let i = 0; i < n; i++) {
|
|
1437
|
+
const a = angleOf(i, n);
|
|
1438
|
+
const lx = cx + Math.cos(a) * (radius + LABEL_PAD - 4);
|
|
1439
|
+
const ly = cy + Math.sin(a) * (radius + LABEL_PAD - 4);
|
|
1440
|
+
ctx.textAlign = Math.cos(a) > .3 ? "left" : Math.cos(a) < -.3 ? "right" : "center";
|
|
1441
|
+
ctx.textBaseline = Math.sin(a) > .3 ? "top" : Math.sin(a) < -.3 ? "bottom" : "middle";
|
|
1442
|
+
ctx.fillText(d.categories[i] ?? "", lx, ly);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
function hitRadar(r, px, py) {
|
|
1446
|
+
if (r.data.kind !== "category") return null;
|
|
1447
|
+
const d = r.data;
|
|
1448
|
+
const n = d.categories.length;
|
|
1449
|
+
if (n < MIN_AXES) return null;
|
|
1450
|
+
const { cx, cy, radius } = radarGeo(r, d);
|
|
1451
|
+
const dx = px - cx;
|
|
1452
|
+
const dy = py - cy;
|
|
1453
|
+
if (Math.hypot(dx, dy) > radius + LABEL_PAD) return null;
|
|
1454
|
+
let rel = Math.atan2(dy, dx) - START;
|
|
1455
|
+
while (rel < 0) rel += Math.PI * 2;
|
|
1456
|
+
while (rel >= Math.PI * 2) rel -= Math.PI * 2;
|
|
1457
|
+
const i = Math.round(rel / (Math.PI * 2) * n) % n;
|
|
1458
|
+
return {
|
|
1459
|
+
kind: "point",
|
|
1460
|
+
index: i,
|
|
1461
|
+
x: i,
|
|
1462
|
+
px,
|
|
1463
|
+
py,
|
|
1464
|
+
values: d.series.map((s) => ({
|
|
1465
|
+
label: s.label,
|
|
1466
|
+
color: s.color,
|
|
1467
|
+
y: s.values[i] ?? 0
|
|
1468
|
+
})),
|
|
1469
|
+
title: d.categories[i] ?? ""
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
/** Hover accent: highlighted spoke + vertex dots (replaces the xy crosshair). */
|
|
1473
|
+
function drawRadarHover(r, info) {
|
|
1474
|
+
if (r.data.kind !== "category" || info.kind !== "point") return;
|
|
1475
|
+
const d = r.data;
|
|
1476
|
+
const n = d.categories.length;
|
|
1477
|
+
if (info.index < 0 || info.index >= n) return;
|
|
1478
|
+
const o = r.backend.overlay;
|
|
1479
|
+
const { cx, cy, radius, max } = radarGeo(r, d);
|
|
1480
|
+
const a = angleOf(info.index, n);
|
|
1481
|
+
o.strokeStyle = r.theme.crosshair;
|
|
1482
|
+
o.lineWidth = 1;
|
|
1483
|
+
o.beginPath();
|
|
1484
|
+
o.moveTo(cx, cy);
|
|
1485
|
+
o.lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
|
|
1486
|
+
o.stroke();
|
|
1487
|
+
for (const s of d.series) {
|
|
1488
|
+
const v = s.values[info.index] ?? 0;
|
|
1489
|
+
const rr = Math.max(0, v) / max * radius;
|
|
1490
|
+
o.fillStyle = s.color;
|
|
1491
|
+
o.strokeStyle = r.theme.bg;
|
|
1492
|
+
o.lineWidth = 2;
|
|
1493
|
+
o.beginPath();
|
|
1494
|
+
o.arc(cx + Math.cos(a) * rr, cy + Math.sin(a) * rr, 4, 0, Math.PI * 2);
|
|
1495
|
+
o.fill();
|
|
1496
|
+
o.stroke();
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
//#endregion
|
|
1500
|
+
//#region src/charts/rangearea.ts
|
|
1501
|
+
/** pooled scratch for the reversed lower edge — consumed within the call */
|
|
1502
|
+
let revScratch = /* @__PURE__ */ new Float64Array(0);
|
|
1503
|
+
/**
|
|
1504
|
+
* Range-area chart: a filled band between each series' `y` (lower edge) and
|
|
1505
|
+
* `y2` (upper edge) — confidence intervals, min/max envelopes. Series
|
|
1506
|
+
* without `y2` are skipped.
|
|
1507
|
+
*/
|
|
1508
|
+
function renderRangeArea(r) {
|
|
1509
|
+
if (!isXY(r.data)) return;
|
|
1510
|
+
const d = r.data;
|
|
1511
|
+
const ctx = r.ctx;
|
|
1512
|
+
const curve = r.opts.curve ?? "linear";
|
|
1513
|
+
ctx.save();
|
|
1514
|
+
ctx.beginPath();
|
|
1515
|
+
ctx.rect(r.plot.x, r.plot.y, r.plot.w, r.plot.h);
|
|
1516
|
+
ctx.clip();
|
|
1517
|
+
for (const s of d.series) {
|
|
1518
|
+
if (!s.y2) continue;
|
|
1519
|
+
const hi = seriesPoints(r, {
|
|
1520
|
+
y: s.y2,
|
|
1521
|
+
prevY: s.prevY2
|
|
1522
|
+
});
|
|
1523
|
+
const lo = seriesPoints(r, s);
|
|
1524
|
+
const n = Math.min(hi.length, lo.length) / 2;
|
|
1525
|
+
if (n === 0) continue;
|
|
1526
|
+
if (revScratch.length < n * 2) revScratch = new Float64Array(n * 2);
|
|
1527
|
+
for (let i = 0; i < n; i++) {
|
|
1528
|
+
revScratch[2 * i] = lo[2 * (n - 1 - i)];
|
|
1529
|
+
revScratch[2 * i + 1] = lo[2 * (n - 1 - i) + 1];
|
|
1530
|
+
}
|
|
1531
|
+
const band = new Path2D();
|
|
1532
|
+
traceCurve(band, hi, n, curve);
|
|
1533
|
+
traceCurve(band, revScratch, n, curve, true);
|
|
1534
|
+
band.closePath();
|
|
1535
|
+
ctx.fillStyle = withAlpha(s.color, .2);
|
|
1536
|
+
ctx.fill(band);
|
|
1537
|
+
ctx.strokeStyle = s.color;
|
|
1538
|
+
ctx.lineWidth = 1.5;
|
|
1539
|
+
ctx.lineJoin = "round";
|
|
1540
|
+
ctx.lineCap = "round";
|
|
1541
|
+
const edges = new Path2D();
|
|
1542
|
+
traceCurve(edges, hi, n, curve);
|
|
1543
|
+
traceCurve(edges, lo, n, curve);
|
|
1544
|
+
ctx.stroke(edges);
|
|
1545
|
+
}
|
|
1546
|
+
ctx.restore();
|
|
1547
|
+
}
|
|
1548
|
+
//#endregion
|
|
1100
1549
|
//#region src/charts/scatter.ts
|
|
1101
1550
|
function renderScatter(r) {
|
|
1102
1551
|
if (!isXY(r.data)) return;
|
|
@@ -1422,85 +1871,6 @@ var Scheduler = class {
|
|
|
1422
1871
|
const sharedScheduler = new Scheduler();
|
|
1423
1872
|
const easeOutCubic = (t) => 1 - (1 - t) ** 3;
|
|
1424
1873
|
//#endregion
|
|
1425
|
-
//#region src/core/theme.ts
|
|
1426
|
-
const FONT = "Inter, system-ui, -apple-system, \"Segoe UI\", sans-serif";
|
|
1427
|
-
/** Built-in light theme.
|
|
1428
|
-
* @category Theming
|
|
1429
|
-
*/
|
|
1430
|
-
const lightTheme = {
|
|
1431
|
-
bg: "#ffffff",
|
|
1432
|
-
text: "#1f1f1f",
|
|
1433
|
-
textMuted: "#8a8a8a",
|
|
1434
|
-
grid: "rgba(0,0,0,0.07)",
|
|
1435
|
-
axisBaseline: "rgba(0,0,0,0.15)",
|
|
1436
|
-
crosshair: "rgba(0,0,0,0.25)",
|
|
1437
|
-
series: ["#0070f3"],
|
|
1438
|
-
contextGray: "#b0b0b0",
|
|
1439
|
-
up: "#0e9f6e",
|
|
1440
|
-
down: "#e5484d",
|
|
1441
|
-
tooltipBg: "#ffffff",
|
|
1442
|
-
tooltipText: "#1f1f1f",
|
|
1443
|
-
tooltipMuted: "#8a8a8a",
|
|
1444
|
-
tooltipBorder: "rgba(0,0,0,0.08)",
|
|
1445
|
-
tooltipShadow: "0 4px 14px rgba(0,0,0,0.08)",
|
|
1446
|
-
fontFamily: FONT
|
|
1447
|
-
};
|
|
1448
|
-
/** Built-in dark theme.
|
|
1449
|
-
* @category Theming
|
|
1450
|
-
*/
|
|
1451
|
-
const darkTheme = {
|
|
1452
|
-
bg: "#0a0a0a",
|
|
1453
|
-
text: "#ededed",
|
|
1454
|
-
textMuted: "#8a8a8a",
|
|
1455
|
-
grid: "rgba(255,255,255,0.07)",
|
|
1456
|
-
axisBaseline: "rgba(255,255,255,0.18)",
|
|
1457
|
-
crosshair: "rgba(255,255,255,0.3)",
|
|
1458
|
-
series: ["#3b9dff"],
|
|
1459
|
-
contextGray: "#5c5c5c",
|
|
1460
|
-
up: "#34c98e",
|
|
1461
|
-
down: "#f2555a",
|
|
1462
|
-
tooltipBg: "#161616",
|
|
1463
|
-
tooltipText: "#ededed",
|
|
1464
|
-
tooltipMuted: "#8a8a8a",
|
|
1465
|
-
tooltipBorder: "rgba(255,255,255,0.1)",
|
|
1466
|
-
tooltipShadow: "0 4px 14px rgba(0,0,0,0.4)",
|
|
1467
|
-
fontFamily: FONT
|
|
1468
|
-
};
|
|
1469
|
-
const categoricalLight = [
|
|
1470
|
-
"#0070f3",
|
|
1471
|
-
"#0e9f6e",
|
|
1472
|
-
"#8e4ec6",
|
|
1473
|
-
"#f5a623"
|
|
1474
|
-
];
|
|
1475
|
-
const categoricalDark = [
|
|
1476
|
-
"#3b9dff",
|
|
1477
|
-
"#34c98e",
|
|
1478
|
-
"#a97ef0",
|
|
1479
|
-
"#f5b84d"
|
|
1480
|
-
];
|
|
1481
|
-
/** Merge a theme option (`"light" | "dark" | partial tokens`) into full {@link ThemeTokens}.
|
|
1482
|
-
* @category Theming
|
|
1483
|
-
*/
|
|
1484
|
-
function resolveTheme(opt, accent) {
|
|
1485
|
-
const base = opt === "dark" ? {
|
|
1486
|
-
...darkTheme,
|
|
1487
|
-
series: [...categoricalDark]
|
|
1488
|
-
} : opt === "light" || opt === void 0 ? {
|
|
1489
|
-
...lightTheme,
|
|
1490
|
-
series: [...categoricalLight]
|
|
1491
|
-
} : {
|
|
1492
|
-
...lightTheme,
|
|
1493
|
-
series: [...categoricalLight],
|
|
1494
|
-
...opt
|
|
1495
|
-
};
|
|
1496
|
-
if (accent) base.series[0] = accent;
|
|
1497
|
-
return base;
|
|
1498
|
-
}
|
|
1499
|
-
function seriesColor(theme, index, explicit) {
|
|
1500
|
-
if (explicit) return explicit;
|
|
1501
|
-
return theme.series[index % theme.series.length];
|
|
1502
|
-
}
|
|
1503
|
-
//#endregion
|
|
1504
1874
|
//#region src/core/ticks.ts
|
|
1505
1875
|
const STEPS = [
|
|
1506
1876
|
1,
|
|
@@ -1640,6 +2010,36 @@ const MODULES = {
|
|
|
1640
2010
|
axes: true,
|
|
1641
2011
|
defaultTooltip: true
|
|
1642
2012
|
},
|
|
2013
|
+
bubble: {
|
|
2014
|
+
render: renderBubble,
|
|
2015
|
+
hit: (r, px) => hitXY(r, px),
|
|
2016
|
+
axes: true,
|
|
2017
|
+
defaultTooltip: true
|
|
2018
|
+
},
|
|
2019
|
+
rangearea: {
|
|
2020
|
+
render: renderRangeArea,
|
|
2021
|
+
hit: (r, px) => hitXY(r, px),
|
|
2022
|
+
axes: true,
|
|
2023
|
+
defaultTooltip: true
|
|
2024
|
+
},
|
|
2025
|
+
pie: {
|
|
2026
|
+
render: renderDonut,
|
|
2027
|
+
hit: hitDonut,
|
|
2028
|
+
axes: false,
|
|
2029
|
+
defaultTooltip: true
|
|
2030
|
+
},
|
|
2031
|
+
radar: {
|
|
2032
|
+
render: renderRadar,
|
|
2033
|
+
hit: hitRadar,
|
|
2034
|
+
axes: false,
|
|
2035
|
+
defaultTooltip: true
|
|
2036
|
+
},
|
|
2037
|
+
funnel: {
|
|
2038
|
+
render: renderFunnel,
|
|
2039
|
+
hit: hitFunnel,
|
|
2040
|
+
axes: false,
|
|
2041
|
+
defaultTooltip: true
|
|
2042
|
+
},
|
|
1643
2043
|
bar: {
|
|
1644
2044
|
render: renderBar,
|
|
1645
2045
|
hit: hitBar,
|
|
@@ -1903,7 +2303,7 @@ var Chart = class {
|
|
|
1903
2303
|
bars
|
|
1904
2304
|
};
|
|
1905
2305
|
}
|
|
1906
|
-
if (t === "donut") {
|
|
2306
|
+
if (t === "donut" || t === "pie") {
|
|
1907
2307
|
const slices = data.slices.map((s, i) => ({
|
|
1908
2308
|
label: s.label,
|
|
1909
2309
|
value: s.value,
|
|
@@ -1915,7 +2315,7 @@ var Chart = class {
|
|
|
1915
2315
|
total: slices.reduce((a, s) => a + Math.max(0, s.value), 0)
|
|
1916
2316
|
};
|
|
1917
2317
|
}
|
|
1918
|
-
if (t === "bar" || t === "combo") {
|
|
2318
|
+
if (t === "bar" || t === "combo" || t === "radar" || t === "funnel") {
|
|
1919
2319
|
const d = data;
|
|
1920
2320
|
const prevSeries = prev?.kind === "category" ? prev.series : [];
|
|
1921
2321
|
return {
|
|
@@ -1947,15 +2347,22 @@ var Chart = class {
|
|
|
1947
2347
|
kind: "xy",
|
|
1948
2348
|
sortedX,
|
|
1949
2349
|
series: d.series.map((s, i) => {
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2350
|
+
const chan = (src) => {
|
|
2351
|
+
if (!src) return null;
|
|
2352
|
+
const out = new Float64Array(n);
|
|
2353
|
+
if (idx) for (let k = 0; k < n; k++) out[k] = src[idx[k]];
|
|
2354
|
+
else for (let k = 0; k < n; k++) out[k] = src[k];
|
|
2355
|
+
return out;
|
|
2356
|
+
};
|
|
2357
|
+
const y = chan(s.y) ?? new Float64Array(n);
|
|
1954
2358
|
return {
|
|
1955
2359
|
label: s.label ?? `Series ${i + 1}`,
|
|
1956
2360
|
color: seriesColor(this.theme, i, s.color),
|
|
1957
2361
|
y,
|
|
1958
2362
|
prevY: prevSeries[i]?.y ?? null,
|
|
2363
|
+
r: chan(s.r),
|
|
2364
|
+
y2: chan(s.y2),
|
|
2365
|
+
prevY2: prevSeries[i]?.y2 ?? null,
|
|
1959
2366
|
pathCache: null
|
|
1960
2367
|
};
|
|
1961
2368
|
})
|
|
@@ -2019,6 +2426,12 @@ var Chart = class {
|
|
|
2019
2426
|
if (v < min) min = v;
|
|
2020
2427
|
if (v > max) max = v;
|
|
2021
2428
|
}
|
|
2429
|
+
const y2 = s.y2;
|
|
2430
|
+
if (y2) for (let i = Math.min(x0, y2.length); i < y2.length; i++) {
|
|
2431
|
+
const v = y2[i];
|
|
2432
|
+
if (v < min) min = v;
|
|
2433
|
+
if (v > max) max = v;
|
|
2434
|
+
}
|
|
2022
2435
|
}
|
|
2023
2436
|
if (min === Infinity) {
|
|
2024
2437
|
min = 0;
|
|
@@ -2246,6 +2659,10 @@ var Chart = class {
|
|
|
2246
2659
|
const o = this.backend.overlay;
|
|
2247
2660
|
const plot = this.backend.plot;
|
|
2248
2661
|
this.backend.beginOverlay();
|
|
2662
|
+
if (info.kind === "point" && this.options.type === "radar") {
|
|
2663
|
+
drawRadarHover(this.renderContext(1), info);
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2249
2666
|
if (info.kind === "point" || info.kind === "candle") {
|
|
2250
2667
|
o.strokeStyle = this.theme.crosshair;
|
|
2251
2668
|
o.lineWidth = 1;
|
|
@@ -2276,7 +2693,7 @@ var Chart = class {
|
|
|
2276
2693
|
const cx = plot.x + plot.w / 2;
|
|
2277
2694
|
const cy = plot.y + plot.h / 2;
|
|
2278
2695
|
const rOuter = Math.min(plot.w, plot.h) / 2;
|
|
2279
|
-
const rInner = rOuter * .65;
|
|
2696
|
+
const rInner = this.options.type === "pie" ? 0 : rOuter * .65;
|
|
2280
2697
|
let a = -Math.PI / 2;
|
|
2281
2698
|
for (let i = 0; i < d.slices.length; i++) {
|
|
2282
2699
|
const s = d.slices[i];
|
|
@@ -2385,7 +2802,7 @@ var Chart = class {
|
|
|
2385
2802
|
*/
|
|
2386
2803
|
append(x, y) {
|
|
2387
2804
|
const d = this.data;
|
|
2388
|
-
if (d.kind !== "xy") throw new Error("[resolvcharts] append() requires xy data (line/area/scatter/sparkline)");
|
|
2805
|
+
if (d.kind !== "xy") throw new Error("[resolvcharts] append() requires xy data (line/area/scatter/bubble/rangearea/sparkline)");
|
|
2389
2806
|
const nowT = typeof performance === "undefined" ? Date.now() : performance.now();
|
|
2390
2807
|
if (this.lastAppendAt > 0) {
|
|
2391
2808
|
const dt = nowT - this.lastAppendAt;
|
|
@@ -2427,11 +2844,30 @@ var Chart = class {
|
|
|
2427
2844
|
prevY.set(s.y.subarray(drop));
|
|
2428
2845
|
prevY[m - 1] = m > 1 ? yNew[m - 2] : yNew[m - 1];
|
|
2429
2846
|
}
|
|
2847
|
+
let rNew = null;
|
|
2848
|
+
if (s.r) {
|
|
2849
|
+
rNew = new Float64Array(m);
|
|
2850
|
+
rNew.set(s.r.subarray(drop));
|
|
2851
|
+
rNew[m - 1] = s.r[n - 1];
|
|
2852
|
+
}
|
|
2853
|
+
let y2New = null;
|
|
2854
|
+
let prevY2 = null;
|
|
2855
|
+
if (s.y2) {
|
|
2856
|
+
y2New = new Float64Array(m);
|
|
2857
|
+
y2New.set(s.y2.subarray(drop));
|
|
2858
|
+
y2New[m - 1] = s.y2[n - 1];
|
|
2859
|
+
prevY2 = new Float64Array(m);
|
|
2860
|
+
prevY2.set(s.y2.subarray(drop));
|
|
2861
|
+
prevY2[m - 1] = m > 1 ? y2New[m - 2] : y2New[m - 1];
|
|
2862
|
+
}
|
|
2430
2863
|
return {
|
|
2431
2864
|
label: s.label,
|
|
2432
2865
|
color: s.color,
|
|
2433
2866
|
y: yNew,
|
|
2434
2867
|
prevY,
|
|
2868
|
+
r: rNew,
|
|
2869
|
+
y2: y2New,
|
|
2870
|
+
prevY2,
|
|
2435
2871
|
pathCache: null
|
|
2436
2872
|
};
|
|
2437
2873
|
});
|
|
@@ -2493,13 +2929,22 @@ function resortXY(d) {
|
|
|
2493
2929
|
for (const s of d.series) {
|
|
2494
2930
|
const ny = new Float64Array(n);
|
|
2495
2931
|
const np = s.prevY ? new Float64Array(n) : null;
|
|
2932
|
+
const nr = s.r ? new Float64Array(n) : null;
|
|
2933
|
+
const ny2 = s.y2 ? new Float64Array(n) : null;
|
|
2934
|
+
const np2 = s.prevY2 ? new Float64Array(n) : null;
|
|
2496
2935
|
for (let i = 0; i < n; i++) {
|
|
2497
2936
|
const k = idx[i];
|
|
2498
2937
|
ny[i] = s.y[k];
|
|
2499
2938
|
if (np && s.prevY) np[i] = s.prevY[k];
|
|
2939
|
+
if (nr && s.r) nr[i] = s.r[k];
|
|
2940
|
+
if (ny2 && s.y2) ny2[i] = s.y2[k];
|
|
2941
|
+
if (np2 && s.prevY2) np2[i] = s.prevY2[k];
|
|
2500
2942
|
}
|
|
2501
2943
|
s.y = ny;
|
|
2502
2944
|
s.prevY = np;
|
|
2945
|
+
s.r = nr;
|
|
2946
|
+
s.y2 = ny2;
|
|
2947
|
+
s.prevY2 = np2;
|
|
2503
2948
|
}
|
|
2504
2949
|
}
|
|
2505
2950
|
/**
|