resolvcharts 0.2.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/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.moveTo(pts[0], pts[1]);
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
- const curve = r.opts.curve ?? "linear";
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
- if (n === 0) return path;
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 * INNER;
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;
@@ -1381,6 +1830,11 @@ function padDomain(min, max, includeZero = false) {
1381
1830
  }
1382
1831
  return [lo, hi];
1383
1832
  }
1833
+ /** @category Utilities
1834
+ * Pin domain ends — each defined bound overrides the fitted value. */
1835
+ function boundDomain(dom, min, max) {
1836
+ return [min ?? dom[0], max ?? dom[1]];
1837
+ }
1384
1838
  //#endregion
1385
1839
  //#region src/core/scheduler.ts
1386
1840
  const raf = typeof requestAnimationFrame === "function" ? (cb) => requestAnimationFrame(cb) : (cb) => setTimeout(() => cb(Date.now()), 16);
@@ -1418,85 +1872,6 @@ var Scheduler = class {
1418
1872
  const sharedScheduler = new Scheduler();
1419
1873
  const easeOutCubic = (t) => 1 - (1 - t) ** 3;
1420
1874
  //#endregion
1421
- //#region src/core/theme.ts
1422
- const FONT = "Inter, system-ui, -apple-system, \"Segoe UI\", sans-serif";
1423
- /** Built-in light theme.
1424
- * @category Theming
1425
- */
1426
- const lightTheme = {
1427
- bg: "#ffffff",
1428
- text: "#1f1f1f",
1429
- textMuted: "#8a8a8a",
1430
- grid: "rgba(0,0,0,0.07)",
1431
- axisBaseline: "rgba(0,0,0,0.15)",
1432
- crosshair: "rgba(0,0,0,0.25)",
1433
- series: ["#0070f3"],
1434
- contextGray: "#b0b0b0",
1435
- up: "#0e9f6e",
1436
- down: "#e5484d",
1437
- tooltipBg: "#ffffff",
1438
- tooltipText: "#1f1f1f",
1439
- tooltipMuted: "#8a8a8a",
1440
- tooltipBorder: "rgba(0,0,0,0.08)",
1441
- tooltipShadow: "0 4px 14px rgba(0,0,0,0.08)",
1442
- fontFamily: FONT
1443
- };
1444
- /** Built-in dark theme.
1445
- * @category Theming
1446
- */
1447
- const darkTheme = {
1448
- bg: "#0a0a0a",
1449
- text: "#ededed",
1450
- textMuted: "#8a8a8a",
1451
- grid: "rgba(255,255,255,0.07)",
1452
- axisBaseline: "rgba(255,255,255,0.18)",
1453
- crosshair: "rgba(255,255,255,0.3)",
1454
- series: ["#3b9dff"],
1455
- contextGray: "#5c5c5c",
1456
- up: "#34c98e",
1457
- down: "#f2555a",
1458
- tooltipBg: "#161616",
1459
- tooltipText: "#ededed",
1460
- tooltipMuted: "#8a8a8a",
1461
- tooltipBorder: "rgba(255,255,255,0.1)",
1462
- tooltipShadow: "0 4px 14px rgba(0,0,0,0.4)",
1463
- fontFamily: FONT
1464
- };
1465
- const categoricalLight = [
1466
- "#0070f3",
1467
- "#0e9f6e",
1468
- "#8e4ec6",
1469
- "#f5a623"
1470
- ];
1471
- const categoricalDark = [
1472
- "#3b9dff",
1473
- "#34c98e",
1474
- "#a97ef0",
1475
- "#f5b84d"
1476
- ];
1477
- /** Merge a theme option (`"light" | "dark" | partial tokens`) into full {@link ThemeTokens}.
1478
- * @category Theming
1479
- */
1480
- function resolveTheme(opt, accent) {
1481
- const base = opt === "dark" ? {
1482
- ...darkTheme,
1483
- series: [...categoricalDark]
1484
- } : opt === "light" || opt === void 0 ? {
1485
- ...lightTheme,
1486
- series: [...categoricalLight]
1487
- } : {
1488
- ...lightTheme,
1489
- series: [...categoricalLight],
1490
- ...opt
1491
- };
1492
- if (accent) base.series[0] = accent;
1493
- return base;
1494
- }
1495
- function seriesColor(theme, index, explicit) {
1496
- if (explicit) return explicit;
1497
- return theme.series[index % theme.series.length];
1498
- }
1499
- //#endregion
1500
1875
  //#region src/core/ticks.ts
1501
1876
  const STEPS = [
1502
1877
  1,
@@ -1636,6 +2011,36 @@ const MODULES = {
1636
2011
  axes: true,
1637
2012
  defaultTooltip: true
1638
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
+ },
1639
2044
  bar: {
1640
2045
  render: renderBar,
1641
2046
  hit: hitBar,
@@ -1696,6 +2101,8 @@ const GPU_KIND = {
1696
2101
  };
1697
2102
  /** points retained beyond the window's left edge so they slide off-screen instead of vanishing mid-plot */
1698
2103
  const EDGE_KEEP = 3;
2104
+ /** default point count at which `renderer: "auto"` engages the GPU path */
2105
+ const GPU_THRESHOLD = 1e5;
1699
2106
  /** Chart instance. Create via {@link createChart}.
1700
2107
  * @category Charts
1701
2108
  */
@@ -1721,6 +2128,8 @@ var Chart = class {
1721
2128
  this.destroyed = false;
1722
2129
  this.pendingView = false;
1723
2130
  this.gpu = null;
2131
+ this.gpuInit = false;
2132
+ this.dataRev = 0;
1724
2133
  this.lastAppendAt = 0;
1725
2134
  this.estInterval = 400;
1726
2135
  this.onScroll = () => {
@@ -1783,7 +2192,35 @@ var Chart = class {
1783
2192
  this.reduced = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
1784
2193
  if (options.tooltip ?? this.mod.defaultTooltip) this.tooltip = new Tooltip(el, this.theme);
1785
2194
  this.data = this.normalize(options.data, null);
1786
- if (GPU_KIND[options.type] && (options.renderer === "webgpu" || options.renderer === "auto")) Promise.resolve().then(() => require("./series-J_5Z6dah.cjs")).then(({ GpuSeries }) => GpuSeries.create(options.gpu).then((g) => {
2195
+ this.maybeInitGpu();
2196
+ this.bindEvents();
2197
+ const dom = this.layout();
2198
+ this.view = {
2199
+ x: [...dom.x],
2200
+ y: [...dom.y]
2201
+ };
2202
+ this.viewTo = {
2203
+ x: [...dom.x],
2204
+ y: [...dom.y]
2205
+ };
2206
+ this.startAnimation(dom);
2207
+ }
2208
+ /**
2209
+ * Lazily acquire the WebGPU backend. `"webgpu"` attaches unconditionally;
2210
+ * `"auto"` waits until the xy point count crosses `gpuThreshold` — small
2211
+ * charts never pay adapter/device acquisition.
2212
+ */
2213
+ maybeInitGpu() {
2214
+ if (this.gpu || this.gpuInit || this.destroyed) return;
2215
+ if (!GPU_KIND[this.options.type]) return;
2216
+ const r = this.options.renderer ?? "auto";
2217
+ if (r !== "auto" && r !== "webgpu") return;
2218
+ if (r === "auto") {
2219
+ const d = this.data;
2220
+ if ((d.kind === "xy" ? d.sortedX.length * Math.max(1, d.series.length) : 0) < (this.options.gpuThreshold ?? GPU_THRESHOLD)) return;
2221
+ }
2222
+ this.gpuInit = true;
2223
+ Promise.resolve().then(() => require("./series-PmAjztEh.cjs")).then(({ GpuSeries }) => GpuSeries.create(this.options.gpu).then((g) => {
1787
2224
  if (!g || this.destroyed) {
1788
2225
  g?.destroy();
1789
2226
  return;
@@ -1796,17 +2233,6 @@ var Chart = class {
1796
2233
  return false;
1797
2234
  });
1798
2235
  }));
1799
- this.bindEvents();
1800
- const dom = this.layout();
1801
- this.view = {
1802
- x: [...dom.x],
1803
- y: [...dom.y]
1804
- };
1805
- this.viewTo = {
1806
- x: [...dom.x],
1807
- y: [...dom.y]
1808
- };
1809
- this.startAnimation(dom);
1810
2236
  }
1811
2237
  normalize(data, prev) {
1812
2238
  const t = this.options.type;
@@ -1878,7 +2304,7 @@ var Chart = class {
1878
2304
  bars
1879
2305
  };
1880
2306
  }
1881
- if (t === "donut") {
2307
+ if (t === "donut" || t === "pie") {
1882
2308
  const slices = data.slices.map((s, i) => ({
1883
2309
  label: s.label,
1884
2310
  value: s.value,
@@ -1890,7 +2316,7 @@ var Chart = class {
1890
2316
  total: slices.reduce((a, s) => a + Math.max(0, s.value), 0)
1891
2317
  };
1892
2318
  }
1893
- if (t === "bar" || t === "combo") {
2319
+ if (t === "bar" || t === "combo" || t === "radar" || t === "funnel") {
1894
2320
  const d = data;
1895
2321
  const prevSeries = prev?.kind === "category" ? prev.series : [];
1896
2322
  return {
@@ -1922,15 +2348,22 @@ var Chart = class {
1922
2348
  kind: "xy",
1923
2349
  sortedX,
1924
2350
  series: d.series.map((s, i) => {
1925
- const y = new Float64Array(n);
1926
- const src = s.y;
1927
- if (idx) for (let k = 0; k < n; k++) y[k] = src[idx[k]];
1928
- else for (let k = 0; k < n; k++) y[k] = src[k];
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);
1929
2359
  return {
1930
2360
  label: s.label ?? `Series ${i + 1}`,
1931
2361
  color: seriesColor(this.theme, i, s.color),
1932
2362
  y,
1933
2363
  prevY: prevSeries[i]?.y ?? null,
2364
+ r: chan(s.r),
2365
+ y2: chan(s.y2),
2366
+ prevY2: prevSeries[i]?.y2 ?? null,
1934
2367
  pathCache: null
1935
2368
  };
1936
2369
  })
@@ -1994,6 +2427,12 @@ var Chart = class {
1994
2427
  if (v < min) min = v;
1995
2428
  if (v > max) max = v;
1996
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
+ }
1997
2436
  }
1998
2437
  if (min === Infinity) {
1999
2438
  min = 0;
@@ -2002,7 +2441,7 @@ var Chart = class {
2002
2441
  const includeZero = this.options.type === "area" || this.options.type === "sparkline";
2003
2442
  return {
2004
2443
  x,
2005
- y: padDomain(min, max, includeZero)
2444
+ y: this.yDomain(padDomain(min, max, includeZero))
2006
2445
  };
2007
2446
  }
2008
2447
  if (d.kind === "category") {
@@ -2038,7 +2477,7 @@ var Chart = class {
2038
2477
  }
2039
2478
  return {
2040
2479
  x: [0, d.categories.length],
2041
- y: padDomain(min, max, true)
2480
+ y: this.yDomain(padDomain(min, max, true))
2042
2481
  };
2043
2482
  }
2044
2483
  if (d.kind === "ohlc") {
@@ -2057,7 +2496,7 @@ var Chart = class {
2057
2496
  }
2058
2497
  return {
2059
2498
  x,
2060
- y: padDomain(min, max)
2499
+ y: this.yDomain(padDomain(min, max))
2061
2500
  };
2062
2501
  }
2063
2502
  if (d.kind === "waterfall") {
@@ -2073,14 +2512,17 @@ var Chart = class {
2073
2512
  }
2074
2513
  return {
2075
2514
  x: [0, d.bars.length],
2076
- y: padDomain(min, max, true)
2515
+ y: this.yDomain(padDomain(min, max, true))
2077
2516
  };
2078
2517
  }
2079
2518
  return {
2080
2519
  x: [0, 1],
2081
- y: [0, 1]
2520
+ y: this.yDomain([0, 1])
2082
2521
  };
2083
2522
  }
2523
+ yDomain(y) {
2524
+ return boundDomain(y, this.options.yMin, this.options.yMax);
2525
+ }
2084
2526
  lerpView(eased) {
2085
2527
  const f = this.viewFrom;
2086
2528
  const to = this.viewTo;
@@ -2182,30 +2624,35 @@ var Chart = class {
2182
2624
  else this.mod.render(this.renderContext(t, scales));
2183
2625
  if (this.hover) this.drawHover(this.hover, scales.yScale);
2184
2626
  }
2185
- /** series rasterization on the GPU; grid/labels/hover stay on Canvas2D */
2627
+ /**
2628
+ * Series rasterization on the GPU; grid/labels/hover stay on Canvas2D.
2629
+ * Raw data lives in GPU buffers — the per-frame cost is uniform writes +
2630
+ * instanced draws, with compute decimation above the density threshold.
2631
+ */
2186
2632
  gpuRender(t, scales) {
2187
2633
  const g = this.gpu;
2188
2634
  const d = this.data;
2189
2635
  const kind = GPU_KIND[this.options.type];
2190
- if (!g || !kind || d.kind !== "xy") {
2636
+ if (!g || g.unavailable || !kind || d.kind !== "xy") {
2191
2637
  this.mod.render(this.renderContext(t, scales));
2192
2638
  return;
2193
2639
  }
2194
- const r = this.renderContext(t, scales);
2640
+ g.syncData(d, this.dataRev);
2641
+ const view = {
2642
+ x: scales.xScale.domain,
2643
+ y: scales.yScale.domain
2644
+ };
2195
2645
  const flow = this.options.flow ? FLOW_RIPPLE : 0;
2196
- g.begin(this.backend.plot);
2197
- for (const s of d.series) {
2646
+ g.begin(this.backend.plot, t, flow);
2647
+ for (let i = 0; i < d.series.length; i++) {
2648
+ const s = d.series[i];
2649
+ if (!s) continue;
2198
2650
  if (kind === "points") {
2199
- const pts = mapSeriesPx(d.sortedX, s.y, s.prevY, t, scales.yScale.domain[0], scales.xScale, scales.yScale, flow);
2200
- g.points(pts, s.color, 2.5, .65);
2651
+ g.points(i, s.color, 2.5, .65, view);
2201
2652
  continue;
2202
2653
  }
2203
- const pts = seriesPoints(r, s);
2204
- if (kind === "area") {
2205
- const base = scales.yScale(Math.max(0, scales.yScale.domain[0]));
2206
- g.area(pts, s.color, base, this.backend.plot);
2207
- }
2208
- g.line(pts, s.color, 2);
2654
+ if (kind === "area") g.area(i, s.color, Math.max(0, scales.yScale.domain[0]), view);
2655
+ g.line(i, s.color, 2, view);
2209
2656
  }
2210
2657
  g.end();
2211
2658
  }
@@ -2213,6 +2660,10 @@ var Chart = class {
2213
2660
  const o = this.backend.overlay;
2214
2661
  const plot = this.backend.plot;
2215
2662
  this.backend.beginOverlay();
2663
+ if (info.kind === "point" && this.options.type === "radar") {
2664
+ drawRadarHover(this.renderContext(1), info);
2665
+ return;
2666
+ }
2216
2667
  if (info.kind === "point" || info.kind === "candle") {
2217
2668
  o.strokeStyle = this.theme.crosshair;
2218
2669
  o.lineWidth = 1;
@@ -2243,7 +2694,7 @@ var Chart = class {
2243
2694
  const cx = plot.x + plot.w / 2;
2244
2695
  const cy = plot.y + plot.h / 2;
2245
2696
  const rOuter = Math.min(plot.w, plot.h) / 2;
2246
- const rInner = rOuter * .65;
2697
+ const rInner = this.options.type === "pie" ? 0 : rOuter * .65;
2247
2698
  let a = -Math.PI / 2;
2248
2699
  for (let i = 0; i < d.slices.length; i++) {
2249
2700
  const s = d.slices[i];
@@ -2340,6 +2791,8 @@ var Chart = class {
2340
2791
  setData(data) {
2341
2792
  const prev = this.data;
2342
2793
  this.data = this.normalize(data, prev);
2794
+ this.dataRev++;
2795
+ this.maybeInitGpu();
2343
2796
  const dom = this.layout();
2344
2797
  this.startAnimation(dom);
2345
2798
  }
@@ -2350,7 +2803,7 @@ var Chart = class {
2350
2803
  */
2351
2804
  append(x, y) {
2352
2805
  const d = this.data;
2353
- 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)");
2354
2807
  const nowT = typeof performance === "undefined" ? Date.now() : performance.now();
2355
2808
  if (this.lastAppendAt > 0) {
2356
2809
  const dt = nowT - this.lastAppendAt;
@@ -2392,11 +2845,30 @@ var Chart = class {
2392
2845
  prevY.set(s.y.subarray(drop));
2393
2846
  prevY[m - 1] = m > 1 ? yNew[m - 2] : yNew[m - 1];
2394
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
+ }
2395
2864
  return {
2396
2865
  label: s.label,
2397
2866
  color: s.color,
2398
2867
  y: yNew,
2399
2868
  prevY,
2869
+ r: rNew,
2870
+ y2: y2New,
2871
+ prevY2,
2400
2872
  pathCache: null
2401
2873
  };
2402
2874
  });
@@ -2405,8 +2877,14 @@ var Chart = class {
2405
2877
  sortedX,
2406
2878
  series
2407
2879
  };
2408
- if (m > 1 && x < sortedX[m - 2]) resortXY(nd);
2880
+ const resort = m > 1 && x < sortedX[m - 2];
2881
+ if (resort) resortXY(nd);
2409
2882
  this.data = nd;
2883
+ if (resort) this.dataRev++;
2884
+ else if (this.gpu && !this.gpu.unavailable) {
2885
+ if (!this.gpu.appended(x, d.series.map((s, i) => ys[i] ?? s.y[n - 1]), d.series.map((s) => s.y[n - 1]), drop)) this.dataRev++;
2886
+ }
2887
+ this.maybeInitGpu();
2410
2888
  this.pendingView = true;
2411
2889
  sharedScheduler.add(this.frame);
2412
2890
  }
@@ -2452,13 +2930,22 @@ function resortXY(d) {
2452
2930
  for (const s of d.series) {
2453
2931
  const ny = new Float64Array(n);
2454
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;
2455
2936
  for (let i = 0; i < n; i++) {
2456
2937
  const k = idx[i];
2457
2938
  ny[i] = s.y[k];
2458
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];
2459
2943
  }
2460
2944
  s.y = ny;
2461
2945
  s.prevY = np;
2946
+ s.r = nr;
2947
+ s.y2 = ny2;
2948
+ s.prevY2 = np2;
2462
2949
  }
2463
2950
  }
2464
2951
  /**
@@ -2482,6 +2969,7 @@ function createChart(el, options) {
2482
2969
  }
2483
2970
  //#endregion
2484
2971
  exports.Chart = Chart;
2972
+ exports.boundDomain = boundDomain;
2485
2973
  exports.computeTicks = computeTicks;
2486
2974
  exports.createChart = createChart;
2487
2975
  exports.darkTheme = darkTheme;