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