@pixerate/editor 0.162.0 → 0.163.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.
Files changed (81) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/index.cjs +34 -34
  3. package/dist/agent/index.js +5 -5
  4. package/dist/animation-editor/index.cjs +57 -57
  5. package/dist/animation-editor/index.js +3 -3
  6. package/dist/{brushstrokeRender-Q4LI22QV.js → brushstrokeRender-7LXCU7PD.js} +2 -2
  7. package/dist/{brushstrokeRender-5WANQIG4.cjs → brushstrokeRender-VO4ZTWVA.cjs} +5 -5
  8. package/dist/canvas/index.cjs +14 -14
  9. package/dist/canvas/index.js +3 -3
  10. package/dist/chart/commands.d.cts +2 -1
  11. package/dist/chart/commands.d.ts +2 -1
  12. package/dist/chart/controller.d.cts +109 -1
  13. package/dist/chart/controller.d.ts +109 -1
  14. package/dist/chart/index.cjs +605 -130
  15. package/dist/chart/index.d.cts +1 -0
  16. package/dist/chart/index.d.ts +1 -0
  17. package/dist/chart/index.js +490 -15
  18. package/dist/chart/layout-annotations.d.cts +29 -0
  19. package/dist/chart/layout-annotations.d.ts +29 -0
  20. package/dist/chart/layout-families.d.cts +23 -0
  21. package/dist/chart/layout-families.d.ts +23 -0
  22. package/dist/chart/mermaid.d.cts +2 -1
  23. package/dist/chart/mermaid.d.ts +2 -1
  24. package/dist/chart/schemas.d.cts +13 -0
  25. package/dist/chart/schemas.d.ts +13 -0
  26. package/dist/chart/spec.d.cts +30 -5
  27. package/dist/chart/spec.d.ts +30 -5
  28. package/dist/chart/stats.d.cts +9 -1
  29. package/dist/chart/stats.d.ts +9 -1
  30. package/dist/chart/tools.d.cts +1 -1
  31. package/dist/chart/tools.d.ts +1 -1
  32. package/dist/chart/tree.d.cts +62 -0
  33. package/dist/chart/tree.d.ts +62 -0
  34. package/dist/chart/types.d.cts +77 -3
  35. package/dist/chart/types.d.ts +77 -3
  36. package/dist/{chunk-7YBDRJXT.cjs → chunk-22KLZZGH.cjs} +8 -8
  37. package/dist/{chunk-LQLZHP6F.js → chunk-36OKQDCF.js} +7 -4
  38. package/dist/{chunk-HLOKB7WW.js → chunk-3NEZJCBY.js} +366 -6
  39. package/dist/{chunk-GWV4T5NZ.js → chunk-3VLOOO3G.js} +1 -1
  40. package/dist/{chunk-3GJH2MGH.cjs → chunk-5AARIS3R.cjs} +35 -35
  41. package/dist/{chunk-EVPZ5IKN.js → chunk-5FNGLV2D.js} +65 -3
  42. package/dist/{chunk-EQ46F75O.cjs → chunk-D54JHQK2.cjs} +51 -51
  43. package/dist/{chunk-NO5QQDHZ.js → chunk-DAPQGA55.js} +2 -2
  44. package/dist/{chunk-3IHNXRUV.cjs → chunk-GKNEJ6EN.cjs} +399 -67
  45. package/dist/{chunk-236TKNUR.cjs → chunk-JCXMJUQO.cjs} +2 -2
  46. package/dist/{chunk-4NGVHHJB.cjs → chunk-NZD2MZJC.cjs} +80 -18
  47. package/dist/{chunk-CX6JLDFC.js → chunk-OXERWBKE.js} +3 -3
  48. package/dist/{chunk-32MMQYCQ.cjs → chunk-PA3IMPPS.cjs} +81 -81
  49. package/dist/{chunk-7CKPUUN4.js → chunk-POR54B55.js} +61 -10
  50. package/dist/{chunk-KGTF55R3.js → chunk-QY4KUDMY.js} +1 -1
  51. package/dist/{chunk-LL3H5XUH.cjs → chunk-R3P2M7HU.cjs} +367 -7
  52. package/dist/{chunk-HDGBM7EA.cjs → chunk-SBQVEBSU.cjs} +580 -53
  53. package/dist/{chunk-2TKHLX6H.js → chunk-SET6KJOP.js} +1 -1
  54. package/dist/{chunk-XDCFJEU7.js → chunk-SZA42R4N.js} +346 -14
  55. package/dist/{chunk-GLMP22FT.js → chunk-TALQONZ5.js} +3 -3
  56. package/dist/{chunk-GNK7SESQ.cjs → chunk-V7GWZIZK.cjs} +7 -4
  57. package/dist/{chunk-EYVVIZJ3.cjs → chunk-WTW7QRTS.cjs} +1 -1
  58. package/dist/{chunk-NA4UVVJ4.cjs → chunk-YGG7WDWT.cjs} +76 -25
  59. package/dist/{chunk-JGKCC5OF.cjs → chunk-YQ5C5BVK.cjs} +10 -9
  60. package/dist/{chunk-EY22AY6R.cjs → chunk-YSAGBIRW.cjs} +16 -16
  61. package/dist/{chunk-A5UCR2TO.js → chunk-YXKLVD56.js} +10 -9
  62. package/dist/{chunk-DTSTNRPH.js → chunk-Z4P2RBMJ.js} +536 -9
  63. package/dist/{chunk-45XXVHVG.js → chunk-ZZGPYNS2.js} +1 -1
  64. package/dist/export/index.cjs +4 -4
  65. package/dist/export/index.js +3 -3
  66. package/dist/extensions/index.cjs +7 -7
  67. package/dist/extensions/index.js +6 -6
  68. package/dist/image-editor/index.cjs +638 -638
  69. package/dist/image-editor/index.js +4 -4
  70. package/dist/index.cjs +13 -13
  71. package/dist/index.js +7 -7
  72. package/dist/paint/index.cjs +2 -2
  73. package/dist/paint/index.js +1 -1
  74. package/dist/paint-editor/index.cjs +129 -149
  75. package/dist/paint-editor/index.js +5 -25
  76. package/dist/vector/index.cjs +948 -948
  77. package/dist/vector/index.js +3 -3
  78. package/package.json +3 -3
  79. package/skills/pixerate-canvas/SKILL.md +16 -0
  80. package/skills/pixerate-chart/SKILL.md +92 -9
  81. package/skills/pixerate-paint-editor/SKILL.md +0 -2
@@ -7,7 +7,12 @@
7
7
 
8
8
 
9
9
 
10
- var _chunkLL3H5XUHcjs = require('./chunk-LL3H5XUH.cjs');
10
+
11
+
12
+
13
+
14
+
15
+ var _chunkR3P2M7HUcjs = require('./chunk-R3P2M7HU.cjs');
11
16
 
12
17
 
13
18
  var _chunk55AKPTLMcjs = require('./chunk-55AKPTLM.cjs');
@@ -507,6 +512,15 @@ function chartQuantile(sorted, q) {
507
512
  const b = _nullishCoalesce(sorted[i + 1], () => ( a));
508
513
  return a + (b - a) * (pos - i);
509
514
  }
515
+ var roundish = (n) => Number(n.toPrecision(3));
516
+ function suggestChartReferences(spec) {
517
+ const values = spec.series.filter((s) => !s.hidden && s.axis !== "secondary").flatMap((s) => s.values).filter((v) => v !== null && Number.isFinite(v)).sort((a, b) => a - b);
518
+ if (!values.length) return { line: { value: 0, label: "Target" }, band: { from: 0, to: 1, label: "Target range" } };
519
+ const mean = values.reduce((t, v) => t + v, 0) / values.length;
520
+ const lo = roundish(chartQuantile(values, 0.25));
521
+ const hi = roundish(chartQuantile(values, 0.75));
522
+ return { line: { value: roundish(mean), label: "Average" }, band: { from: lo, to: hi > lo ? hi : lo + 1, label: "Middle half" } };
523
+ }
510
524
  function chartBoxStats(values) {
511
525
  const v = values.filter((x) => x !== null && Number.isFinite(x)).sort((a, b) => a - b);
512
526
  if (!v.length) return null;
@@ -822,13 +836,164 @@ function chartSupportsTrendlines(type, stacking) {
822
836
  return (type === "bar" || type === "line" || type === "area" || type === "scatter" || type === "bubble") && stacking === "none";
823
837
  }
824
838
 
839
+ // src/chart/layout-annotations.ts
840
+ var axisOf = (item) => _nullishCoalesce(item.axis, () => ( "y"));
841
+ var chartAnnotationFamily = (family) => family === "cartesian" || family === "xy";
842
+ function referenceValues(spec, axis) {
843
+ const out = [];
844
+ for (const l of spec.referenceLines) if (axisOf(l) === axis) out.push(l.value);
845
+ for (const b of spec.referenceBands) if (axisOf(b) === axis) out.push(b.from, b.to);
846
+ return out;
847
+ }
848
+ function drawReferences(ctx, plot, axes, layer) {
849
+ const { spec, theme, marks, fs, measure } = ctx;
850
+ const items = layer === "bands" ? spec.referenceBands : spec.referenceLines;
851
+ const missing = /* @__PURE__ */ new Set();
852
+ items.forEach((item, i) => {
853
+ const axis = axes[axisOf(item)];
854
+ if (!axis) {
855
+ missing.add(axisOf(item));
856
+ return;
857
+ }
858
+ const { info, vertical } = axis;
859
+ const color = _nullishCoalesce(item.color, () => ( theme.text));
860
+ const lo = vertical ? plot.y : plot.x;
861
+ const hi = vertical ? plot.y + plot.height : plot.x + plot.width;
862
+ const inside = (p2) => Number.isFinite(p2) && p2 >= lo - 0.5 && p2 <= hi + 0.5;
863
+ const at = (v) => info.logBase && !(v > 0) ? NaN : info.raw(v);
864
+ if ("from" in item) {
865
+ const a = at(item.from);
866
+ const b = at(item.to);
867
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return;
868
+ const p0 = Math.max(lo, Math.min(a, b));
869
+ const p1 = Math.min(hi, Math.max(a, b));
870
+ if (p1 <= p0) return;
871
+ const rect = vertical ? { x: plot.x, y: p0, width: plot.width, height: p1 - p0 } : { x: p0, y: plot.y, width: p1 - p0, height: plot.height };
872
+ marks.push({ kind: "rect", key: `reference-band-${i}`, role: "reference-band", ...rect, fill: color, opacity: item.color ? 0.16 : 0.07 });
873
+ if (item.label) {
874
+ const text = truncate(item.label, (vertical ? plot.width : rect.width) - 8, fs - 1, measure);
875
+ marks.push({ kind: "text", key: `reference-label-band-${i}`, role: "reference-label", x: rect.x + 4, y: rect.y + 3, text, anchor: "start", baseline: "top", size: fs - 1, fill: color, opacity: 0.85 });
876
+ }
877
+ return;
878
+ }
879
+ const p = at(item.value);
880
+ if (!inside(p)) {
881
+ ctx.warn("annotations-skipped", `The reference line at ${item.value} is outside the ${axisOf(item) === "x" ? "x" : "value"} axis range and is not drawn.`);
882
+ return;
883
+ }
884
+ const line = vertical ? { x1: plot.x, y1: p, x2: plot.x + plot.width, y2: p } : { x1: p, y1: plot.y, x2: p, y2: plot.y + plot.height };
885
+ marks.push({ kind: "line", key: `reference-line-${i}`, role: "reference-line", ...line, stroke: color, strokeWidth: 1.25, dash: "5 3", opacity: 0.8 });
886
+ if (item.label) {
887
+ const text = truncate(item.label, (vertical ? plot.width : plot.width / 2) - 8, fs - 1, measure);
888
+ const label = vertical ? { x: plot.x + plot.width - 4, y: p - 3, anchor: "end", baseline: "bottom" } : { x: p + 4, y: plot.y + 2, anchor: "start", baseline: "top" };
889
+ marks.push({ kind: "text", key: `reference-label-line-${i}`, role: "reference-label", ...label, text, size: fs - 1, weight: 500, fill: color });
890
+ }
891
+ });
892
+ for (const axis of missing)
893
+ ctx.warn("annotations-skipped", axis === "x" ? "Reference lines and bands on the x axis need a scatter or bubble chart with numeric x values; they are not drawn." : "Reference lines and bands on the secondary axis need a series on that axis; they are not drawn.");
894
+ }
895
+ function anchorOf(mark, horizontal) {
896
+ if (mark.kind === "rect") return horizontal ? { x: mark.x + mark.width, y: mark.y + mark.height / 2 } : { x: mark.x + mark.width / 2, y: mark.y };
897
+ if (mark.kind === "circle") return { x: mark.cx, y: mark.cy - mark.r };
898
+ return null;
899
+ }
900
+ function drawCallouts(ctx, plot) {
901
+ const { spec, marks, theme, fs, measure } = ctx;
902
+ const horizontal = spec.orientation === "horizontal";
903
+ const placed = [];
904
+ const overlaps = (a) => placed.some((b) => a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height);
905
+ let unplaced = 0;
906
+ for (const s of spec.series) {
907
+ if (s.hidden || !s.callouts) continue;
908
+ s.callouts.forEach((label, i) => {
909
+ if (!label) return;
910
+ const mark = marks.find((m) => (m.role === "bar" || m.role === "point") && _optionalChain([m, 'access', _2 => _2.target, 'optionalAccess', _3 => _3.kind]) === "point" && m.target.seriesId === s.id && m.target.index === i);
911
+ const anchor = mark ? anchorOf(mark, horizontal && mark.kind === "rect") : null;
912
+ if (!anchor) {
913
+ unplaced++;
914
+ return;
915
+ }
916
+ const size = fs - 1;
917
+ const text = truncate(label, plot.width * 0.6, size, measure);
918
+ const w = measure(text, size, 600) + 10;
919
+ const h = size + 8;
920
+ const sideways = horizontal && mark.kind === "rect";
921
+ let bx = sideways ? anchor.x + 14 : anchor.x - w / 2;
922
+ let by = sideways ? anchor.y - h / 2 : anchor.y - h - 14;
923
+ const clamp = () => {
924
+ bx = Math.max(plot.x, Math.min(plot.x + plot.width - w, bx));
925
+ by = Math.max(plot.y - fs / 2, Math.min(plot.y + plot.height - h, by));
926
+ };
927
+ clamp();
928
+ const covers = anchor.x >= bx - 2 && anchor.x <= bx + w + 2 && anchor.y >= by - 2 && anchor.y <= by + h + 2;
929
+ if (covers) {
930
+ if (sideways) bx = anchor.x - w - 14;
931
+ else by = anchor.y + 14;
932
+ clamp();
933
+ }
934
+ for (let k = 0; k < 6 && overlaps({ x: bx, y: by, width: w, height: h }); k++) {
935
+ if (sideways) by += h + 2;
936
+ else by -= h + 2;
937
+ clamp();
938
+ }
939
+ const box = { x: bx, y: by, width: w, height: h };
940
+ placed.push(box);
941
+ const target = { kind: "point", seriesId: s.id, index: i };
942
+ const ex = Math.max(bx, Math.min(bx + w, anchor.x));
943
+ const ey = Math.max(by, Math.min(by + h, anchor.y));
944
+ if (Math.hypot(ex - anchor.x, ey - anchor.y) > 2) marks.push({ kind: "line", key: `callout-leader-${s.id}-${i}`, role: "callout", x1: anchor.x, y1: anchor.y, x2: ex, y2: ey, stroke: theme.axis, strokeWidth: 1 });
945
+ marks.push({ kind: "rect", key: `callout-box-${s.id}-${i}`, role: "callout", ...box, fill: theme.surface, stroke: theme.axis, strokeWidth: 1, radius: 3, target });
946
+ marks.push({ kind: "text", key: `callout-${s.id}-${i}`, role: "callout", x: bx + w / 2, y: by + h / 2, text, anchor: "middle", baseline: "middle", size, weight: 600, fill: theme.text, target });
947
+ });
948
+ }
949
+ if (unplaced) ctx.warn("annotations-skipped", `${unplaced} callout${unplaced === 1 ? " points" : "s point"} at a gap or a hidden value and ${unplaced === 1 ? "is" : "are"} not drawn.`);
950
+ }
951
+ var MUTED = /* @__PURE__ */ new Set(["bar", "line", "area", "point", "slice", "cell", "box", "whisker", "outlier", "connector", "trendline", "trendline-label", "error-bar", "data-label", "legend-swatch", "legend-label"]);
952
+ function applyHighlight(ctx) {
953
+ const { spec, marks } = ctx;
954
+ if (!spec.highlight.length) return;
955
+ const names = new Set(spec.highlight);
956
+ const categories = /* @__PURE__ */ new Set();
957
+ spec.categories.forEach((c, i) => names.has(c) && categories.add(i));
958
+ const series = new Set(spec.series.filter((s) => names.has(s.id) || names.has(s.name)).map((s) => s.id));
959
+ const unmatched = spec.highlight.filter((h) => !spec.categories.includes(h) && !spec.series.some((s) => s.id === h || s.name === h));
960
+ if (unmatched.length) ctx.warn("highlight-unmatched", `Highlight names ${unmatched.map((u) => `"${u}"`).join(", ")}, which ${unmatched.length === 1 ? "is" : "are"} neither a category nor a series.`);
961
+ if (!categories.size && !series.size) return;
962
+ const bright = (t) => {
963
+ switch (t.kind) {
964
+ case "point":
965
+ return series.has(t.seriesId) || categories.has(t.index);
966
+ case "series":
967
+ case "trendline":
968
+ return !series.size || series.has(t.seriesId);
969
+ case "category":
970
+ return !categories.size || categories.has(t.index);
971
+ default:
972
+ return true;
973
+ }
974
+ };
975
+ for (let k = 0; k < marks.length; k++) {
976
+ const m = marks[k];
977
+ if (!m.target || !MUTED.has(m.role) || bright(m.target)) continue;
978
+ marks[k] = { ...m, opacity: (_nullishCoalesce(m.opacity, () => ( 1))) * 0.25 };
979
+ }
980
+ }
981
+ var noteHeight = (spec, fs) => spec.note ? fs * 1.5 : 0;
982
+ function drawNote(ctx, pad) {
983
+ const { spec, marks, theme, fs, measure, width, height } = ctx;
984
+ if (!spec.note) return;
985
+ const text = truncate(spec.note, width - pad * 2, fs - 1, measure);
986
+ if (text !== spec.note) ctx.warn("labels-truncated", "The note was shortened to fit the width.");
987
+ marks.push({ kind: "text", key: "note", role: "note", x: pad, y: height - pad + 2, text, anchor: "start", baseline: "bottom", size: fs - 1, fill: theme.mutedText });
988
+ }
989
+
825
990
  // src/chart/layout-cartesian.ts
826
991
  var MAX_CHART_DATA_LABELS = 200;
827
992
  var MAX_CHART_LINE_POINTS = 2e3;
828
993
  var MAX_CHART_MARKERS = 1e3;
829
994
  function scaleInfo(scale, range, format, axis) {
830
- const logBase = _optionalChain([axis, 'optionalAccess', _2 => _2.scale]) === "log" ? _nullishCoalesce(axis.logBase, () => ( 10)) : void 0;
831
- const r = _optionalChain([axis, 'optionalAccess', _3 => _3.reverse]) ? [range[1], range[0]] : range;
995
+ const logBase = _optionalChain([axis, 'optionalAccess', _4 => _4.scale]) === "log" ? _nullishCoalesce(axis.logBase, () => ( 10)) : void 0;
996
+ const r = _optionalChain([axis, 'optionalAccess', _5 => _5.reverse]) ? [range[1], range[0]] : range;
832
997
  const vs = { domain: [scale.min, scale.max], range: r, logBase };
833
998
  const raw = (v) => chartScaleToPixel(v, vs);
834
999
  const px = (v) => logBase && !(v > 0) ? r[0] : raw(Math.min(scale.max, Math.max(scale.min, v)));
@@ -849,7 +1014,7 @@ function categoryFrame(ctx, area, input) {
849
1014
  };
850
1015
  const valueLen0 = horizontal ? area.width : area.height;
851
1016
  const valueLabelWidth = primary.axis.show ? estimate(primary, valueLen0) : 0;
852
- const valueLabelWidth2 = _optionalChain([secondary, 'optionalAccess', _4 => _4.axis, 'access', _5 => _5.show]) ? estimate(secondary, valueLen0) : 0;
1017
+ const valueLabelWidth2 = _optionalChain([secondary, 'optionalAccess', _6 => _6.axis, 'access', _7 => _7.show]) ? estimate(secondary, valueLen0) : 0;
853
1018
  const catLabelWidth = Math.max(0, ...labels.map((c) => measure(c, fs)));
854
1019
  let left;
855
1020
  let bottom;
@@ -973,7 +1138,7 @@ function categoryFrame(ctx, area, input) {
973
1138
  marks.push({ kind: "text", key: `axis-title-${horizontal ? "y" : "x"}`, role: "axis-title", x: plot.x + plot.width / 2, y: area.y + area.height, text: bottomTitle, anchor: "middle", baseline: "bottom", size: fs + 1, weight: 500, fill: theme.text, target: horizontal ? yTarget : xTarget });
974
1139
  if (leftTitle)
975
1140
  marks.push({ kind: "text", key: `axis-title-${horizontal ? "x" : "y"}`, role: "axis-title", x: area.x, y: plot.y + plot.height / 2, text: leftTitle, anchor: "middle", baseline: "top", size: fs + 1, weight: 500, fill: theme.text, rotate: -90, target: horizontal ? xTarget : yTarget });
976
- if (_optionalChain([secondary, 'optionalAccess', _6 => _6.axis, 'access', _7 => _7.title])) {
1141
+ if (_optionalChain([secondary, 'optionalAccess', _8 => _8.axis, 'access', _9 => _9.title])) {
977
1142
  const target = { kind: "axis", axis: "y2" };
978
1143
  if (horizontal) marks.push({ kind: "text", key: "axis-title-y2", role: "axis-title", x: plot.x + plot.width / 2, y: area.y, text: secondary.axis.title, anchor: "middle", baseline: "top", size: fs + 1, weight: 500, fill: theme.text, target });
979
1144
  else marks.push({ kind: "text", key: "axis-title-y2", role: "axis-title", x: area.x + area.width, y: plot.y + plot.height / 2, text: secondary.axis.title, anchor: "middle", baseline: "top", size: fs + 1, weight: 500, fill: theme.text, rotate: 90, target });
@@ -1074,7 +1239,7 @@ function layoutCartesian(ctx, area, visible) {
1074
1239
  const { spec, theme, marks, measure, fs } = ctx;
1075
1240
  const horizontal = spec.orientation === "horizontal";
1076
1241
  const n = spec.categories.length;
1077
- const types = new Map(visible.map((s) => [s.id, _chunkLL3H5XUHcjs.chartSeriesType.call(void 0, spec, s)]));
1242
+ const types = new Map(visible.map((s) => [s.id, _chunkR3P2M7HUcjs.chartSeriesType.call(void 0, spec, s)]));
1078
1243
  const stacked = spec.stacking !== "none";
1079
1244
  const percent = spec.stacking === "percent";
1080
1245
  const onSecondary = (s) => s.axis === "secondary";
@@ -1111,9 +1276,9 @@ function layoutCartesian(ctx, area, visible) {
1111
1276
  const stack = stacks.get(s.id);
1112
1277
  errors.set(
1113
1278
  s.id,
1114
- _chunkLL3H5XUHcjs.chartErrorAmounts.call(void 0, s.errorBars, s.values).map((e, i) => {
1279
+ _chunkR3P2M7HUcjs.chartErrorAmounts.call(void 0, s.errorBars, s.values).map((e, i) => {
1115
1280
  const raw = s.values[i];
1116
- const seg = _optionalChain([stack, 'optionalAccess', _8 => _8.segments, 'access', _9 => _9[i]]);
1281
+ const seg = _optionalChain([stack, 'optionalAccess', _10 => _10.segments, 'access', _11 => _11[i]]);
1117
1282
  if (!e || raw === null || stack && !seg) return null;
1118
1283
  const k = seg && percent ? raw ? (seg[1] - seg[0]) / raw : 0 : 1;
1119
1284
  const center = (seg ? seg[1] : raw) + (e.center - raw) * k;
@@ -1135,8 +1300,9 @@ function layoutCartesian(ctx, area, visible) {
1135
1300
  const stack = stacks.get(s.id);
1136
1301
  if (stack) stack.segments.forEach((seg) => seg && (see(seg[0]), see(seg[1])));
1137
1302
  else s.values.forEach((v) => v !== null && see(v));
1138
- _optionalChain([errors, 'access', _10 => _10.get, 'call', _11 => _11(s.id), 'optionalAccess', _12 => _12.forEach, 'call', _13 => _13((e) => e && (see(e.lo), see(e.hi)))]);
1303
+ _optionalChain([errors, 'access', _12 => _12.get, 'call', _13 => _13(s.id), 'optionalAccess', _14 => _14.forEach, 'call', _15 => _15((e) => e && (see(e.lo), see(e.hi)))]);
1139
1304
  }
1305
+ for (const v of referenceValues(spec, secondary ? "y2" : "y")) see(v);
1140
1306
  if (log) {
1141
1307
  const skipped = group.reduce((t, s) => t + s.values.filter((v) => v !== null && Number.isFinite(v) && v <= 0).length, 0);
1142
1308
  if (skipped) ctx.warn("values-skipped", `${skipped} value${skipped === 1 ? "" : "s"} of 0 or less cannot be drawn on the logarithmic ${secondary ? "secondary " : ""}value axis and ${skipped === 1 ? "was" : "were"} skipped.`);
@@ -1163,6 +1329,8 @@ function layoutCartesian(ctx, area, visible) {
1163
1329
  });
1164
1330
  const { plot, band, catCenter, map, catStart } = frame;
1165
1331
  const infoOf = (s) => onSecondary(s) && frame.value2 ? frame.value2 : frame.value;
1332
+ const refAxes = { y: { info: frame.value, vertical: !horizontal }, ...frame.value2 ? { y2: { info: frame.value2, vertical: !horizontal } } : {} };
1333
+ drawReferences(ctx, plot, refAxes, "bands");
1166
1334
  const bars = visible.filter((s) => types.get(s.id) === "bar");
1167
1335
  const barAxes = [...new Set(bars.map((s) => onSecondary(s)))];
1168
1336
  const slots = stacked ? barAxes.length : bars.length;
@@ -1178,7 +1346,7 @@ function layoutCartesian(ctx, area, visible) {
1178
1346
  const drawOrder = [...visible].sort((a, b) => order(types.get(a.id)) - order(types.get(b.id)));
1179
1347
  for (const s of drawOrder) {
1180
1348
  const si = spec.series.indexOf(s);
1181
- const color = _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, si);
1349
+ const color = _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, si);
1182
1350
  const type = types.get(s.id);
1183
1351
  const stack = stacks.get(s.id);
1184
1352
  const selSeries = ctx.seriesSelected(s.id);
@@ -1204,7 +1372,7 @@ function layoutCartesian(ctx, area, visible) {
1204
1372
  const selected = ctx.isSelected(target);
1205
1373
  const [x, y] = map({ c: c0, v: Math.min(a, b) });
1206
1374
  const rect = horizontal ? { x: Math.min(a, b), y: c0, width: Math.abs(b - a), height: cw } : { x, y, width: cw, height: Math.abs(b - a) };
1207
- const fill = _chunkLL3H5XUHcjs.chartPointColor.call(void 0, spec, si, i);
1375
+ const fill = _chunkR3P2M7HUcjs.chartPointColor.call(void 0, spec, si, i);
1208
1376
  marks.push({
1209
1377
  kind: "rect",
1210
1378
  key: `bar-${s.id}-${i}`,
@@ -1232,7 +1400,7 @@ function layoutCartesian(ctx, area, visible) {
1232
1400
  } else {
1233
1401
  const curve = _nullishCoalesce(s.curve, () => ( spec.curve));
1234
1402
  const pointValue = (i) => {
1235
- const v = stack ? _nullishCoalesce(_optionalChain([stack, 'access', _14 => _14.segments, 'access', _15 => _15[i], 'optionalAccess', _16 => _16[1]]), () => ( null)) : s.values[i];
1403
+ const v = stack ? _nullishCoalesce(_optionalChain([stack, 'access', _16 => _16.segments, 'access', _17 => _17[i], 'optionalAccess', _18 => _18[1]]), () => ( null)) : s.values[i];
1236
1404
  return v !== null && Number.isFinite(v) && !(log && (v <= 0 || (_nullishCoalesce(s.values[i], () => ( 0))) <= 0)) ? v : null;
1237
1405
  };
1238
1406
  const runs = [];
@@ -1256,7 +1424,7 @@ function layoutCartesian(ctx, area, visible) {
1256
1424
  const drawn = runs.map(thin);
1257
1425
  if (type === "area") {
1258
1426
  const parts = drawn.map((r) => {
1259
- const baseline = stack ? r.map((pt) => ({ c: pt.c, v: valuePx(_nullishCoalesce(_optionalChain([stack, 'access', _17 => _17.segments, 'access', _18 => _18[pt.i], 'optionalAccess', _19 => _19[0]]), () => ( 0))) })).reverse() : [
1427
+ const baseline = stack ? r.map((pt) => ({ c: pt.c, v: valuePx(_nullishCoalesce(_optionalChain([stack, 'access', _19 => _19.segments, 'access', _20 => _20[pt.i], 'optionalAccess', _21 => _21[0]]), () => ( 0))) })).reverse() : [
1260
1428
  { c: r[r.length - 1].c, v: info.zero },
1261
1429
  { c: r[0].c, v: info.zero }
1262
1430
  ];
@@ -1283,7 +1451,7 @@ function layoutCartesian(ctx, area, visible) {
1283
1451
  const target = { kind: "point", seriesId: s.id, index: i };
1284
1452
  const selected = ctx.isSelected(target);
1285
1453
  const r = type === "scatter" ? 4.5 : 3.5;
1286
- const pointColor = _chunkLL3H5XUHcjs.chartPointColor.call(void 0, spec, si, i);
1454
+ const pointColor = _chunkR3P2M7HUcjs.chartPointColor.call(void 0, spec, si, i);
1287
1455
  const fill = showDots ? type === "scatter" ? pointColor : theme.surface : "transparent";
1288
1456
  marks.push(
1289
1457
  pointMark({ key: `point-${s.id}-${i}`, target, selected: selected || selSeries, opacity: type === "scatter" ? 0.85 : void 0 }, showDots ? marker : "hit", cx, cy, showDots ? r : 6, fill, showDots ? pointColor : void 0, 2)
@@ -1320,13 +1488,14 @@ function layoutCartesian(ctx, area, visible) {
1320
1488
  if (d) errorMarks.push({ kind: "path", key: `error-bar-${s.id}-${i}`, role: "error-bar", d, fill: "none", stroke: type === "bar" ? theme.text : color, strokeWidth: selSeries ? 2 : 1.25, target: seriesTarget, selected: selSeries });
1321
1489
  });
1322
1490
  }
1323
- if (trendsAllowed && _optionalChain([s, 'access', _20 => _20.trendlines, 'optionalAccess', _21 => _21.length])) {
1491
+ if (trendsAllowed && _optionalChain([s, 'access', _22 => _22.trendlines, 'optionalAccess', _23 => _23.length])) {
1324
1492
  const points = [];
1325
1493
  s.values.forEach((v, i) => v !== null && Number.isFinite(v) && points.push([i + 1, v]));
1326
1494
  const toPx = (x, y) => map({ c: catCenter(x - 1), v: info.raw(y) });
1327
1495
  drawTrendlines(ctx, plot, s, color, points, toPx, trendLabels, !!info.logBase);
1328
1496
  }
1329
1497
  }
1498
+ drawReferences(ctx, plot, refAxes, "lines");
1330
1499
  marks.push(...errorMarks, ...labelMarks);
1331
1500
  drawTrendLabels(ctx, plot, trendLabels);
1332
1501
  if (hiddenLabels) ctx.warn("data-labels-hidden", `${hiddenLabels} data label${hiddenLabels === 1 ? "" : "s"} did not fit and ${hiddenLabels === 1 ? "is" : "are"} hidden.`);
@@ -1345,7 +1514,7 @@ function layoutXy(ctx, area, visible) {
1345
1514
  const xLog = xAxis.scale === "log";
1346
1515
  const yLog = yAxis.scale === "log";
1347
1516
  let skipped = 0;
1348
- const pointsOf = (s) => s.values.map((y, i) => ({ i, x: _nullishCoalesce(_optionalChain([s, 'access', _22 => _22.x, 'optionalAccess', _23 => _23[i]]), () => ( (bubble || !s.x ? i + 1 : null))), y, size: _nullishCoalesce(_optionalChain([s, 'access', _24 => _24.sizes, 'optionalAccess', _25 => _25[i]]), () => ( null)) })).filter((p) => {
1517
+ const pointsOf = (s) => s.values.map((y, i) => ({ i, x: _nullishCoalesce(_optionalChain([s, 'access', _24 => _24.x, 'optionalAccess', _25 => _25[i]]), () => ( (bubble || !s.x ? i + 1 : null))), y, size: _nullishCoalesce(_optionalChain([s, 'access', _26 => _26.sizes, 'optionalAccess', _27 => _27[i]]), () => ( null)) })).filter((p) => {
1349
1518
  if (p.x === null || p.y === null || !Number.isFinite(p.x) || !Number.isFinite(p.y)) return false;
1350
1519
  if (xLog && p.x <= 0 || yLog && p.y <= 0) return skipped++, false;
1351
1520
  return true;
@@ -1355,8 +1524,8 @@ function layoutXy(ctx, area, visible) {
1355
1524
  const yErrors = /* @__PURE__ */ new Map();
1356
1525
  const xErrors = /* @__PURE__ */ new Map();
1357
1526
  for (const { s } of all) {
1358
- if (s.errorBars) yErrors.set(s.id, _chunkLL3H5XUHcjs.chartErrorAmounts.call(void 0, s.errorBars, s.values));
1359
- if (s.xErrorBars) xErrors.set(s.id, _chunkLL3H5XUHcjs.chartErrorAmounts.call(void 0, s.xErrorBars, s.values.map((_, i) => _nullishCoalesce(_optionalChain([s, 'access', _26 => _26.x, 'optionalAccess', _27 => _27[i]]), () => ( (bubble || !s.x ? i + 1 : null))))));
1527
+ if (s.errorBars) yErrors.set(s.id, _chunkR3P2M7HUcjs.chartErrorAmounts.call(void 0, s.errorBars, s.values));
1528
+ if (s.xErrorBars) xErrors.set(s.id, _chunkR3P2M7HUcjs.chartErrorAmounts.call(void 0, s.xErrorBars, s.values.map((_, i) => _nullishCoalesce(_optionalChain([s, 'access', _28 => _28.x, 'optionalAccess', _29 => _29[i]]), () => ( (bubble || !s.x ? i + 1 : null))))));
1360
1529
  }
1361
1530
  let xlo = Infinity;
1362
1531
  let xhi = -Infinity;
@@ -1378,9 +1547,9 @@ function layoutXy(ctx, area, visible) {
1378
1547
  seeX(p.x);
1379
1548
  seeY(p.y);
1380
1549
  if (p.size !== null && p.size > maxSize) maxSize = p.size;
1381
- const ey = _optionalChain([yErrors, 'access', _28 => _28.get, 'call', _29 => _29(s.id), 'optionalAccess', _30 => _30[p.i]]);
1550
+ const ey = _optionalChain([yErrors, 'access', _30 => _30.get, 'call', _31 => _31(s.id), 'optionalAccess', _32 => _32[p.i]]);
1382
1551
  if (ey) seeY(ey.center - ey.minus), seeY(ey.center + ey.plus);
1383
- const ex = _optionalChain([xErrors, 'access', _31 => _31.get, 'call', _32 => _32(s.id), 'optionalAccess', _33 => _33[p.i]]);
1552
+ const ex = _optionalChain([xErrors, 'access', _33 => _33.get, 'call', _34 => _34(s.id), 'optionalAccess', _35 => _35[p.i]]);
1384
1553
  if (ex) seeX(ex.center - ex.minus), seeX(ex.center + ex.plus);
1385
1554
  }
1386
1555
  for (const t of _nullishCoalesce(s.trendlines, () => ( []))) {
@@ -1389,6 +1558,8 @@ function layoutXy(ctx, area, visible) {
1389
1558
  seeX(Math.max(...pts.map((p) => p.x)) + (_nullishCoalesce(t.forward, () => ( 0))));
1390
1559
  }
1391
1560
  }
1561
+ for (const v of referenceValues(spec, "y")) seeY(v);
1562
+ for (const v of referenceValues(spec, "x")) seeX(v);
1392
1563
  if (!Number.isFinite(xlo)) [xlo, xhi] = xLog ? [1, 10] : [0, 1];
1393
1564
  if (!Number.isFinite(ylo)) [ylo, yhi] = yLog ? [1, 10] : [0, 1];
1394
1565
  const titleBand = fs * 1.3 + 6;
@@ -1430,13 +1601,15 @@ function layoutXy(ctx, area, visible) {
1430
1601
  if (!ctx.isSelected({ kind: "axis", axis })) continue;
1431
1602
  ctx.outline(`axis-${axis}-selection`, axis === "y" ? { x: area.x, y: plot.y - fs / 2, width: left - 2, height: plot.height + fs } : { x: plot.x - 4, y: plot.y + plot.height + 2, width: plot.width + 8, height: area.y + area.height - plot.y - plot.height - 2 });
1432
1603
  }
1604
+ const refAxes = { y: { info: yInfo, vertical: true }, x: { info: xInfo, vertical: false } };
1605
+ drawReferences(ctx, plot, refAxes, "bands");
1433
1606
  const labelMarks = [];
1434
1607
  const errorMarks = [];
1435
1608
  const trendLabels = [];
1436
1609
  for (const { s, pts } of all) {
1437
1610
  const si = spec.series.indexOf(s);
1438
- const color = _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, si);
1439
- const type = _chunkLL3H5XUHcjs.chartSeriesType.call(void 0, spec, s);
1611
+ const color = _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, si);
1612
+ const type = _chunkR3P2M7HUcjs.chartSeriesType.call(void 0, spec, s);
1440
1613
  const selSeries = ctx.seriesSelected(s.id);
1441
1614
  const seriesTarget = { kind: "series", seriesId: s.id };
1442
1615
  if (type === "line" || type === "area") {
@@ -1461,7 +1634,7 @@ function layoutXy(ctx, area, visible) {
1461
1634
  }
1462
1635
  const target = { kind: "point", seriesId: s.id, index: p.i };
1463
1636
  const selected = ctx.isSelected(target);
1464
- const pointColor = _chunkLL3H5XUHcjs.chartPointColor.call(void 0, spec, si, p.i);
1637
+ const pointColor = _chunkR3P2M7HUcjs.chartPointColor.call(void 0, spec, si, p.i);
1465
1638
  const r = bubble ? maxSize > 0 && p.size !== null && p.size > 0 ? Math.max(2, Math.sqrt(p.size / maxSize) * maxR) : 2 : 4.5;
1466
1639
  if (bubble) marks.push({ kind: "circle", key: `point-${s.id}-${p.i}`, role: "point", cx, cy, r, fill: pointColor, stroke: pointColor, strokeWidth: 1, opacity: 0.6, target, selected: selected || selSeries });
1467
1640
  else marks.push(pointMark({ key: `point-${s.id}-${p.i}`, target, selected: selected || selSeries, opacity: 0.85 }, marker === "none" ? "hit" : marker, cx, cy, marker === "none" ? 6 : r, marker === "none" ? "transparent" : pointColor, marker === "none" ? void 0 : pointColor, 2));
@@ -1471,14 +1644,14 @@ function layoutXy(ctx, area, visible) {
1471
1644
  const stroke = color;
1472
1645
  const errorBar = (key, d) => d && errorMarks.push({ kind: "path", key, role: "error-bar", d, fill: "none", stroke, strokeWidth: selSeries ? 2 : 1.25, target: seriesTarget, selected: selSeries });
1473
1646
  for (const p of pts) {
1474
- const ey = _optionalChain([yErrors, 'access', _34 => _34.get, 'call', _35 => _35(s.id), 'optionalAccess', _36 => _36[p.i]]);
1647
+ const ey = _optionalChain([yErrors, 'access', _36 => _36.get, 'call', _37 => _37(s.id), 'optionalAccess', _38 => _38[p.i]]);
1475
1648
  if (ey) {
1476
1649
  const lo = ey.center - ey.minus;
1477
1650
  const hi = ey.center + ey.plus;
1478
1651
  const cap = s.errorBars.cap !== false;
1479
1652
  errorBar(`error-bar-${s.id}-${p.i}`, errorBarPath((q) => [q.c, q.v], xPx(p.x), yPx(lo), yPx(hi), 8, cap && lo < ey.center, cap && hi > ey.center));
1480
1653
  }
1481
- const ex = _optionalChain([xErrors, 'access', _37 => _37.get, 'call', _38 => _38(s.id), 'optionalAccess', _39 => _39[p.i]]);
1654
+ const ex = _optionalChain([xErrors, 'access', _39 => _39.get, 'call', _40 => _40(s.id), 'optionalAccess', _41 => _41[p.i]]);
1482
1655
  if (ex) {
1483
1656
  const lo = ex.center - ex.minus;
1484
1657
  const hi = ex.center + ex.plus;
@@ -1486,8 +1659,9 @@ function layoutXy(ctx, area, visible) {
1486
1659
  errorBar(`error-bar-x-${s.id}-${p.i}`, errorBarPath((q) => [q.v, q.c], yPx(p.y), xPx(lo), xPx(hi), 8, cap && lo < ex.center, cap && hi > ex.center));
1487
1660
  }
1488
1661
  }
1489
- if (_optionalChain([s, 'access', _40 => _40.trendlines, 'optionalAccess', _41 => _41.length])) drawTrendlines(ctx, plot, s, color, pts.map((p) => [p.x, p.y]), (x, y) => [xRaw(x), yRaw(y)], trendLabels, xLog || yLog);
1662
+ if (_optionalChain([s, 'access', _42 => _42.trendlines, 'optionalAccess', _43 => _43.length])) drawTrendlines(ctx, plot, s, color, pts.map((p) => [p.x, p.y]), (x, y) => [xRaw(x), yRaw(y)], trendLabels, xLog || yLog);
1490
1663
  }
1664
+ drawReferences(ctx, plot, refAxes, "lines");
1491
1665
  marks.push(...errorMarks, ...labelMarks);
1492
1666
  drawTrendLabels(ctx, plot, trendLabels);
1493
1667
  return {
@@ -1504,7 +1678,7 @@ function trendlineLegendItems(spec, color) {
1504
1678
  }
1505
1679
 
1506
1680
  // src/chart/layout-families.ts
1507
- var sliceColor = (ctx, series, i) => _optionalChain([series, 'access', _42 => _42.pointColors, 'optionalAccess', _43 => _43[i]]) || _chunkLL3H5XUHcjs.chartSliceColor.call(void 0, ctx.spec, i);
1681
+ var sliceColor = (ctx, series, i) => _optionalChain([series, 'access', _44 => _44.pointColors, 'optionalAccess', _45 => _45[i]]) || _chunkR3P2M7HUcjs.chartSliceColor.call(void 0, ctx.spec, i);
1508
1682
  function layoutPie(ctx, area, series) {
1509
1683
  const { spec, theme, marks, fs, selection } = ctx;
1510
1684
  const values = spec.categories.map((_, i) => {
@@ -1534,7 +1708,7 @@ function layoutPie(ctx, area, series) {
1534
1708
  angle = end;
1535
1709
  const target = { kind: "point", seriesId: series.id, index: i };
1536
1710
  const selected = ctx.isSelected(target) || ctx.isSelected({ kind: "category", index: i });
1537
- const seriesSel = _optionalChain([selection, 'optionalAccess', _44 => _44.kind]) === "series" && selection.seriesId === series.id;
1711
+ const seriesSel = _optionalChain([selection, 'optionalAccess', _46 => _46.kind]) === "series" && selection.seriesId === series.id;
1538
1712
  const mid = (start + end) / 2;
1539
1713
  const off = selected ? 6 : 0;
1540
1714
  const ox = cx + off * Math.sin(mid);
@@ -1620,7 +1794,7 @@ function layoutRadar(ctx, area, visible) {
1620
1794
  });
1621
1795
  for (const s of visible) {
1622
1796
  const si = spec.series.indexOf(s);
1623
- const color = _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, si);
1797
+ const color = _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, si);
1624
1798
  const selSeries = ctx.seriesSelected(s.id);
1625
1799
  const pts = s.values.map((v, i) => v !== null && Number.isFinite(v) ? at(i, rOf(v)) : null);
1626
1800
  const present = pts.filter((p) => p !== null);
@@ -1670,7 +1844,7 @@ function layoutWaterfall(ctx, area, series) {
1670
1844
  });
1671
1845
  const { band, catCenter, map, value } = frame;
1672
1846
  const w = band * (1 - spec.gapWidth);
1673
- const color = _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(series));
1847
+ const color = _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(series));
1674
1848
  const format = _nullishCoalesce(series.format, () => ( spec.yAxis.format));
1675
1849
  const labelsOn = _nullishCoalesce(series.dataLabels, () => ( spec.dataLabels));
1676
1850
  let prevEnd = null;
@@ -1729,7 +1903,7 @@ function layoutHistogram(ctx, area, visible) {
1729
1903
  const gap = k > 1 ? band * spec.gapWidth * 0.5 : 0;
1730
1904
  const w = (band - gap) / k;
1731
1905
  visible.forEach((s, si) => {
1732
- const color = _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s));
1906
+ const color = _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s));
1733
1907
  const target = { kind: "series", seriesId: s.id };
1734
1908
  const selected = ctx.seriesSelected(s.id);
1735
1909
  counts[si].forEach((c, b) => {
@@ -1760,7 +1934,7 @@ function layoutBox(ctx, area, visible) {
1760
1934
  visible.forEach((s, i) => {
1761
1935
  const st = stats[i];
1762
1936
  if (!st) return;
1763
- const color = _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s));
1937
+ const color = _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s));
1764
1938
  const target = { kind: "series", seriesId: s.id };
1765
1939
  const selected = ctx.seriesSelected(s.id);
1766
1940
  const c = catCenter(i);
@@ -1843,16 +2017,349 @@ function layoutTreemap(ctx, area, series) {
1843
2017
  if (unlabelled) ctx.warn("data-labels-hidden", `${unlabelled} treemap cell${unlabelled === 1 ? " is" : "s are"} too small for a label.`);
1844
2018
  return { plot: area };
1845
2019
  }
2020
+ function dodge(ys, gap, top, bottom) {
2021
+ const out = [...ys];
2022
+ for (let k = 1; k < out.length; k++) out[k] = Math.max(out[k], out[k - 1] + gap);
2023
+ const over = out.length ? out[out.length - 1] - bottom : 0;
2024
+ if (over > 0) for (let k = out.length - 1; k >= 0; k--) out[k] = Math.max(top, Math.min(out[k] - over, k < out.length - 1 ? out[k + 1] - gap : Infinity));
2025
+ return out;
2026
+ }
2027
+ function layoutSlope(ctx, area, visible) {
2028
+ const { spec, theme, marks, fs, measure } = ctx;
2029
+ const [a, b] = visible;
2030
+ if (visible.length > 2) ctx.warn("values-skipped", `A slope chart joins two series; ${visible.length - 2} more ${visible.length === 3 ? "is" : "are"} not drawn.`);
2031
+ const at = (s, i) => {
2032
+ const v = s.values[i];
2033
+ return v !== null && Number.isFinite(v) ? v : null;
2034
+ };
2035
+ const all = spec.categories.flatMap((_, i) => [at(a, i), at(b, i)]).filter((v) => v !== null);
2036
+ const lo = Math.min(...all);
2037
+ const hi = Math.max(...all);
2038
+ const labelsOn = _nullishCoalesce(_nullishCoalesce(a.dataLabels, () => ( b.dataLabels)), () => ( spec.dataLabels));
2039
+ const fmtA = (v) => formatChartNumber(v, _nullishCoalesce(a.format, () => ( spec.yAxis.format)));
2040
+ const fmtB = (v) => formatChartNumber(v, _nullishCoalesce(b.format, () => ( spec.yAxis.format)));
2041
+ const leftText = (i) => {
2042
+ const v = at(a, i);
2043
+ return labelsOn && v !== null ? `${spec.categories[i]} ${fmtA(v)}` : spec.categories[i];
2044
+ };
2045
+ const rightText = (i) => {
2046
+ const v = at(b, i);
2047
+ return labelsOn && v !== null ? `${fmtB(v)} ${spec.categories[i]}` : spec.categories[i];
2048
+ };
2049
+ const maxW = area.width * 0.3;
2050
+ const leftW = Math.min(maxW, Math.max(0, ...spec.categories.map((_, i) => measure(leftText(i), fs))));
2051
+ const rightW = Math.min(maxW, Math.max(0, ...spec.categories.map((_, i) => measure(rightText(i), fs))));
2052
+ const headerH = fs * 1.8;
2053
+ const plot = { x: area.x + leftW + 10, y: area.y + headerH + fs / 2, width: Math.max(1, area.width - leftW - rightW - 20), height: Math.max(1, area.height - headerH - fs) };
2054
+ const scale = axisScale(lo, hi, plot.height, spec.yAxis, ctx.options.valueDomain, true);
2055
+ const yOf = (v) => linearMap(Math.min(scale.max, Math.max(scale.min, v)), [scale.min, scale.max], [plot.y + plot.height, plot.y]);
2056
+ const xL = plot.x;
2057
+ const xR = plot.x + plot.width;
2058
+ for (const [s, x, anchor] of [[a, xL, "end"], [b, xR, "start"]]) {
2059
+ const target = { kind: "series", seriesId: s.id };
2060
+ const selected = ctx.seriesSelected(s.id);
2061
+ marks.push({ kind: "line", key: `axis-slope-${s.id}`, role: "axis", x1: x, y1: plot.y, x2: x, y2: plot.y + plot.height, stroke: theme.axis, strokeWidth: 1, target });
2062
+ marks.push({ kind: "text", key: `axis-title-${s.id}`, role: "axis-title", x, y: area.y, text: truncate(s.name, maxW + plot.width / 2, fs + 1, measure), anchor: anchor === "end" ? "end" : "start", baseline: "top", size: fs + 1, weight: 600, fill: selected ? theme.selection : theme.text, target, selected });
2063
+ }
2064
+ const leftLabels = [];
2065
+ const rightLabels = [];
2066
+ spec.categories.forEach((_, i) => {
2067
+ const va = at(a, i);
2068
+ const vb = at(b, i);
2069
+ const color = sliceColor(ctx, a, i);
2070
+ const catTarget = { kind: "category", index: i };
2071
+ const catSelected = ctx.isSelected(catTarget);
2072
+ if (va !== null && vb !== null)
2073
+ marks.push({ kind: "line", key: `line-slope-${i}`, role: "line", x1: xL, y1: yOf(va), x2: xR, y2: yOf(vb), stroke: color, strokeWidth: catSelected ? 3.5 : 2, target: catTarget, selected: catSelected });
2074
+ for (const [s, v, x, labels] of [[a, va, xL, leftLabels], [b, vb, xR, rightLabels]]) {
2075
+ if (v === null) continue;
2076
+ const target = { kind: "point", seriesId: s.id, index: i };
2077
+ const selected = ctx.isSelected(target);
2078
+ marks.push({ kind: "circle", key: `point-${s.id}-${i}`, role: "point", cx: x, cy: yOf(v), r: 4, fill: color, stroke: theme.surface, strokeWidth: 1.5, target, selected: selected || catSelected || ctx.seriesSelected(s.id) });
2079
+ if (selected) marks.push({ kind: "circle", key: `point-${s.id}-${i}-selection`, role: "selection", cx: x, cy: yOf(v), r: 7.5, fill: "none", stroke: theme.selection, strokeWidth: 1.5 });
2080
+ labels.push({ i, y: yOf(v) });
2081
+ }
2082
+ });
2083
+ let moved = 0;
2084
+ for (const [labels, x, anchor, text, width] of [[leftLabels, xL - 8, "end", leftText, leftW], [rightLabels, xR + 8, "start", rightText, rightW]]) {
2085
+ labels.sort((p, q) => p.y - q.y);
2086
+ const ys = dodge(labels.map((l) => l.y), fs * 1.15, plot.y - fs / 2, plot.y + plot.height + fs / 2);
2087
+ labels.forEach((l, k) => {
2088
+ if (Math.abs(ys[k] - l.y) > 0.5) moved++;
2089
+ const target = { kind: "category", index: l.i };
2090
+ const selected = ctx.isSelected(target);
2091
+ marks.push({ kind: "text", key: `label-slope-${anchor === "end" ? "left" : "right"}-${l.i}`, role: "data-label", x, y: ys[k], text: truncate(text(l.i), width, fs, measure), anchor, baseline: "middle", size: fs, fill: selected ? theme.selection : theme.text, weight: selected ? 600 : void 0, target, selected });
2092
+ });
2093
+ }
2094
+ if (moved) ctx.warn("labels-truncated", `${moved} slope label${moved === 1 ? " was" : "s were"} moved off its point so the labels do not overlap.`);
2095
+ return { plot, valueScale: { axis: "y", domain: [scale.min, scale.max], range: [plot.y + plot.height, plot.y], ticks: scale.ticks } };
2096
+ }
2097
+ function layoutLikert(ctx, area, visible) {
2098
+ const { spec, theme, marks, fs, measure } = ctx;
2099
+ const n = visible.length;
2100
+ const side = Math.floor(n / 2);
2101
+ const neutral = n % 2 === 1 ? side : -1;
2102
+ const separate = neutral >= 0 && spec.likertNeutral === "separate";
2103
+ if (spec.likertNeutral === "separate" && neutral < 0) ctx.warn("type-fallback", "A separate neutral column needs an odd number of answers; the middle answer is not set apart.");
2104
+ const shares = spec.categories.map((_, i) => {
2105
+ const raw = visible.map((s) => {
2106
+ const v = s.values[i];
2107
+ return v !== null && Number.isFinite(v) ? Math.max(0, v) : 0;
2108
+ });
2109
+ const total = raw.reduce((t, v) => t + v, 0);
2110
+ return raw.map((v) => total ? v / total : 0);
2111
+ });
2112
+ const sum = (p, from, to) => p.slice(from, to).reduce((t, v) => t + v, 0);
2113
+ const negOf = (p) => sum(p, 0, side) + (neutral >= 0 && !separate ? p[neutral] / 2 : 0);
2114
+ const posOf = (p) => sum(p, n - side, n) + (neutral >= 0 && !separate ? p[neutral] / 2 : 0);
2115
+ const rows = spec.categories.map((_, i) => i);
2116
+ if (spec.likertSort === "positive") rows.sort((p, q) => sum(shares[q], n - side, n) - sum(shares[p], n - side, n));
2117
+ else if (spec.likertSort === "negative") rows.sort((p, q) => sum(shares[q], 0, side) - sum(shares[p], 0, side));
2118
+ const extent = Math.max(0.01, ...shares.map((p) => Math.max(negOf(p), posOf(p))));
2119
+ const labelW = spec.xAxis.show ? Math.min(area.width * 0.35, Math.max(0, ...spec.categories.map((c) => measure(c, fs)))) + 10 : 4;
2120
+ const neutralW = separate ? Math.max(40, area.width * 0.18) : 0;
2121
+ const axisH = spec.yAxis.show ? fs * 1.6 : 2;
2122
+ const headH = separate ? fs * 1.5 : 0;
2123
+ const plot = { x: area.x + labelW, y: area.y + headH, width: Math.max(1, area.width - labelW - (separate ? neutralW + 14 : 0)), height: Math.max(1, area.height - axisH - headH) };
2124
+ const half = axisScale(0, extent, plot.width / 2, { ...spec.yAxis, min: 0, max: null, scale: void 0, reverse: void 0 });
2125
+ const m = half.max;
2126
+ const xOf = (v) => plot.x + (v + m) / (2 * m) * plot.width;
2127
+ const percent = { style: "percent", decimals: spec.yAxis.format.decimals };
2128
+ const ticks = [...half.ticks.slice(1).map((t) => -t).reverse(), ...half.ticks];
2129
+ const axisTarget = { kind: "axis", axis: "y" };
2130
+ ticks.forEach((t, k) => {
2131
+ const x = xOf(t);
2132
+ if (spec.yAxis.gridlines && t !== 0) marks.push({ kind: "line", key: `grid-y-${k}`, role: "grid", x1: x, y1: plot.y, x2: x, y2: plot.y + plot.height, stroke: theme.grid, strokeWidth: 1 });
2133
+ if (spec.yAxis.show) marks.push({ kind: "text", key: `tick-y-${k}`, role: "tick-label", x, y: plot.y + plot.height + 6, text: formatChartNumber(Math.abs(t), percent, _nullishCoalesce(percent.decimals, () => ( stepDecimals(half.step * 100)))), anchor: "middle", baseline: "top", size: fs, fill: theme.mutedText, target: axisTarget });
2134
+ });
2135
+ const band = plot.height / Math.max(1, rows.length);
2136
+ const barH = Math.max(2, band * (1 - Math.min(0.9, spec.gapWidth)));
2137
+ const neutralMax = separate ? Math.max(0.01, ...shares.map((p) => p[neutral])) : 1;
2138
+ const neutralX = plot.x + plot.width + 14;
2139
+ if (separate) {
2140
+ const s = visible[neutral];
2141
+ marks.push({ kind: "text", key: "likert-neutral-title", role: "axis-title", x: neutralX, y: area.y, text: truncate(s.name, neutralW, fs, measure), anchor: "start", baseline: "top", size: fs, weight: 600, fill: theme.mutedText, target: { kind: "series", seriesId: s.id } });
2142
+ }
2143
+ let hidden = 0;
2144
+ rows.forEach((i, r) => {
2145
+ const p = shares[i];
2146
+ const y = plot.y + r * band + (band - barH) / 2;
2147
+ const catTarget = { kind: "category", index: i };
2148
+ const catSelected = ctx.isSelected(catTarget);
2149
+ if (spec.xAxis.show)
2150
+ marks.push({ kind: "text", key: `tick-x-${i}`, role: "tick-label", x: plot.x - 8, y: y + barH / 2, text: truncate(spec.categories[i], labelW - 10, fs, measure), anchor: "end", baseline: "middle", size: fs, fill: catSelected ? theme.selection : theme.mutedText, target: catTarget, selected: catSelected });
2151
+ const segment = (j, x0, x1, key) => {
2152
+ const s = visible[j];
2153
+ if (x1 - x0 <= 0) return;
2154
+ const target = { kind: "point", seriesId: s.id, index: i };
2155
+ const selected = ctx.isSelected(target);
2156
+ const fill = _optionalChain([s, 'access', _47 => _47.pointColors, 'optionalAccess', _48 => _48[i]]) || _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s));
2157
+ const rect = { x: x0, y, width: x1 - x0, height: barH };
2158
+ marks.push({ kind: "rect", key, role: "bar", ...rect, fill, target, selected: selected || ctx.seriesSelected(s.id) || catSelected });
2159
+ if (selected) ctx.outline(`${key}-selection`, { x: rect.x - 2, y: rect.y - 2, width: rect.width + 4, height: rect.height + 4 });
2160
+ if (!(_nullishCoalesce(s.dataLabels, () => ( spec.dataLabels)))) return;
2161
+ const text = formatChartNumber(p[j], percent, _nullishCoalesce(percent.decimals, () => ( 0)));
2162
+ if (measure(text, fs - 1) + 6 > rect.width || barH < fs) {
2163
+ hidden++;
2164
+ return;
2165
+ }
2166
+ marks.push({ kind: "text", key: `label-${s.id}-${i}`, role: "data-label", x: rect.x + rect.width / 2, y: y + barH / 2, text, anchor: "middle", baseline: "middle", size: fs - 1, fill: contrastingTextColor(fill), target });
2167
+ };
2168
+ let cursor = -negOf(p);
2169
+ visible.forEach((s, j) => {
2170
+ if (separate && j === neutral) return;
2171
+ segment(j, xOf(cursor), xOf(cursor + p[j]), `bar-${s.id}-${i}`);
2172
+ cursor += p[j];
2173
+ });
2174
+ if (separate) segment(neutral, neutralX, neutralX + p[neutral] / neutralMax * neutralW, `bar-${visible[neutral].id}-${i}`);
2175
+ });
2176
+ marks.push({ kind: "line", key: "axis-likert-centre", role: "axis", x1: xOf(0), y1: plot.y, x2: xOf(0), y2: plot.y + plot.height, stroke: theme.axis, strokeWidth: 1.25, target: axisTarget });
2177
+ if (hidden) ctx.warn("data-labels-hidden", `${hidden} data label${hidden === 1 ? "" : "s"} did not fit and ${hidden === 1 ? "is" : "are"} hidden.`);
2178
+ return { plot, percent: true };
2179
+ }
2180
+ function layoutParliament(ctx, area, series) {
2181
+ const { spec, theme, marks, fs } = ctx;
2182
+ const seats = spec.categories.map((_, i) => {
2183
+ const v = series.values[i];
2184
+ return v !== null && Number.isFinite(v) ? Math.max(0, Math.round(v)) : 0;
2185
+ });
2186
+ if (series.values.some((v) => v !== null && Number.isFinite(v) && (v < 0 || !Number.isInteger(v)))) ctx.warn("values-skipped", "Seats are whole numbers of 0 or more; other values were rounded.");
2187
+ const total = seats.reduce((t, v) => t + v, 0);
2188
+ const empty = (text) => {
2189
+ marks.push({ kind: "text", key: "empty", role: "empty", x: area.x + area.width / 2, y: area.y + area.height / 2, text, anchor: "middle", baseline: "middle", size: fs + 1, fill: theme.mutedText });
2190
+ return { plot: area };
2191
+ };
2192
+ if (!total) return empty("No seats");
2193
+ if (total > 2e3) return empty("Too many seats to draw (more than 2,000)");
2194
+ const arc = spec.parliamentArc * Math.PI / 180;
2195
+ const start = Math.PI / 2 + arc / 2;
2196
+ const angles = [start, start - arc, ...[0, 0.5, 1, 1.5, 2, 2.5].map((k) => k * Math.PI).filter((t) => t < start && t > start - arc)];
2197
+ const xs = [0, ...angles.map(Math.cos)];
2198
+ const ys = [0, ...angles.map(Math.sin)];
2199
+ const pad = fs * 1.2;
2200
+ const radius = Math.max(8, Math.min((area.width - pad * 2) / (Math.max(...xs) - Math.min(...xs)), (area.height - pad * 2) / (Math.max(...ys) - Math.min(...ys))));
2201
+ const cx = area.x + area.width / 2 - (Math.max(...xs) + Math.min(...xs)) / 2 * radius;
2202
+ const cy = area.y + area.height / 2 + (Math.max(...ys) + Math.min(...ys)) / 2 * radius;
2203
+ const inner = radius * 0.4;
2204
+ let rows = 1;
2205
+ for (; rows < 60; rows++) {
2206
+ const gap2 = (radius - inner) / rows;
2207
+ const length = Array.from({ length: rows }, (_, k) => (inner + gap2 * (k + 0.5)) * arc).reduce((t, l) => t + l, 0);
2208
+ if (length / total >= gap2 * 0.9) break;
2209
+ }
2210
+ const gap = (radius - inner) / rows;
2211
+ const radii = Array.from({ length: rows }, (_, k) => inner + gap * (k + 0.5));
2212
+ const lengths = radii.map((r) => r * arc);
2213
+ const all = lengths.reduce((t, l) => t + l, 0);
2214
+ const exact = lengths.map((l) => l / all * total);
2215
+ const counts = exact.map(Math.floor);
2216
+ exact.map((e, k) => ({ k, rest: e - counts[k] })).sort((p, q) => q.rest - p.rest).slice(0, total - counts.reduce((t, c) => t + c, 0)).forEach(({ k }) => counts[k]++);
2217
+ const places = [];
2218
+ radii.forEach((r, k) => {
2219
+ for (let j = 0; j < counts[k]; j++) places.push({ angle: counts[k] === 1 ? Math.PI / 2 : start - arc * (j + 0.5) / counts[k], r });
2220
+ });
2221
+ places.sort((p, q) => q.angle - p.angle || p.r - q.r);
2222
+ const dot = Math.max(1, Math.min(gap, all / total) * 0.42);
2223
+ let next = 0;
2224
+ seats.forEach((count, i) => {
2225
+ const target = { kind: "point", seriesId: series.id, index: i };
2226
+ const selected = ctx.isSelected(target) || ctx.isSelected({ kind: "category", index: i }) || ctx.seriesSelected(series.id);
2227
+ const fill = sliceColor(ctx, series, i);
2228
+ for (let k = 0; k < count; k++, next++) {
2229
+ const { angle, r } = places[next];
2230
+ marks.push({ kind: "circle", key: `seat-${i}-${k}`, role: "point", cx: cx + r * Math.cos(angle), cy: cy - r * Math.sin(angle), r: dot, fill, ...selected ? { stroke: theme.selection, strokeWidth: 1.5 } : {}, target, selected });
2231
+ }
2232
+ });
2233
+ const majority = _nullishCoalesce(spec.parliamentMajority, () => ( Math.floor(total / 2) + 1));
2234
+ if (majority <= total) {
2235
+ const angle = start - arc * (majority / total);
2236
+ const r0 = inner - gap * 0.3;
2237
+ const r1 = radius + gap * 0.3;
2238
+ marks.push({ kind: "line", key: "majority-line", role: "reference-line", x1: cx + r0 * Math.cos(angle), y1: cy - r0 * Math.sin(angle), x2: cx + r1 * Math.cos(angle), y2: cy - r1 * Math.sin(angle), stroke: theme.text, strokeWidth: 1.25, dash: "4 3", opacity: 0.8 });
2239
+ const lr = r1 + fs * 0.4;
2240
+ const lx = cx + lr * Math.cos(angle);
2241
+ const ly = cy - lr * Math.sin(angle);
2242
+ marks.push({ kind: "text", key: "majority-label", role: "reference-label", x: lx, y: ly, text: `Majority ${majority}`, anchor: Math.abs(Math.cos(angle)) < 0.25 ? "middle" : Math.cos(angle) > 0 ? "start" : "end", baseline: Math.sin(angle) >= 0 ? "bottom" : "top", size: fs - 1, weight: 500, fill: theme.text });
2243
+ } else ctx.warn("values-skipped", `The majority of ${majority} is more than the ${total} seats; the majority line is not drawn.`);
2244
+ if (inner > fs * 2.2) {
2245
+ const size = Math.min(theme.titleSize + 8, inner * 0.6);
2246
+ const ty = spec.parliamentArc <= 200 ? cy - size * 0.2 : cy;
2247
+ const totalTarget = { kind: "series", seriesId: series.id };
2248
+ marks.push({ kind: "text", key: "parliament-total", role: "data-label", x: cx, y: ty, text: formatChartNumber(total, {}), anchor: "middle", baseline: spec.parliamentArc <= 200 ? "bottom" : "middle", size, weight: 700, fill: theme.text, target: totalTarget });
2249
+ marks.push({ kind: "text", key: "parliament-caption", role: "data-label", x: cx, y: spec.parliamentArc <= 200 ? ty + 2 : ty + size * 0.6, text: spec.parliamentLabel || "seats", anchor: "middle", baseline: "top", size: fs, fill: theme.mutedText, target: totalTarget });
2250
+ }
2251
+ return { plot: { x: cx - radius, y: cy - radius, width: radius * 2, height: radius * 2 } };
2252
+ }
2253
+ function treeColor(ctx, series, node, group, base, shade) {
2254
+ if (node.color) return node.color;
2255
+ if (base === null) return _optionalChain([series, 'optionalAccess', _49 => _49.pointColors, 'optionalAccess', _50 => _50[group]]) || _chunkR3P2M7HUcjs.chartSliceColor.call(void 0, ctx.spec, group);
2256
+ return _chunkR3P2M7HUcjs.shadeChartColor.call(void 0, base, shade);
2257
+ }
2258
+ function layoutTreemapTree(ctx, area, series) {
2259
+ const { spec, marks, theme, fs, measure } = ctx;
2260
+ const format = _nullishCoalesce(_optionalChain([series, 'optionalAccess', _51 => _51.format]), () => ( spec.yAxis.format));
2261
+ const labelsOn = _nullishCoalesce(_optionalChain([series, 'optionalAccess', _52 => _52.dataLabels]), () => ( true));
2262
+ let unlabelled = 0;
2263
+ const draw = (nodes, rect, key, group, base) => {
2264
+ const rects = chartSquarify(nodes.map(_chunkR3P2M7HUcjs.chartTreeTotal), rect);
2265
+ nodes.forEach((node, k) => {
2266
+ const r = rects[k];
2267
+ if (r.width <= 0.5 || r.height <= 0.5) return;
2268
+ const g = _nullishCoalesce(group, () => ( k));
2269
+ const target = { kind: "category", index: g };
2270
+ const selected = ctx.isSelected(target) || ctx.isSelected({ kind: "point", seriesId: _nullishCoalesce(_optionalChain([series, 'optionalAccess', _53 => _53.id]), () => ( "")), index: g });
2271
+ const id = `${key}-${k}`;
2272
+ const fill = treeColor(ctx, series, node, g, base, base === null ? 0 : (k % 4 - 1.5) * 0.14);
2273
+ if (node.children) {
2274
+ const header = r.height > fs * 3.2 && r.width > fs * 3 ? fs * 1.5 : 0;
2275
+ marks.push({ kind: "rect", key: `group-${id}`, role: "cell", ...r, fill, stroke: selected ? theme.selection : theme.surface, strokeWidth: selected ? 2.5 : 2, target, selected });
2276
+ if (header) marks.push({ kind: "text", key: `label-group-${id}`, role: "data-label", x: r.x + 5, y: r.y + 4, text: truncate(node.name, r.width - 10, fs, measure), anchor: "start", baseline: "top", size: fs, weight: 700, fill: contrastingTextColor(fill), target });
2277
+ const inner = { x: r.x + 2, y: r.y + (header || 2), width: Math.max(0, r.width - 4), height: Math.max(0, r.height - (header || 2) - 2) };
2278
+ draw(node.children, inner, id, g, _nullishCoalesce(base, () => ( fill)));
2279
+ return;
2280
+ }
2281
+ marks.push({ kind: "rect", key: `cell-${id}`, role: "cell", ...r, fill, stroke: selected ? theme.selection : theme.surface, strokeWidth: selected ? 2.5 : 1, target, selected });
2282
+ if (!labelsOn) return;
2283
+ const room = r.width - 8;
2284
+ if (room < fs * 2 || r.height < fs * 1.5) {
2285
+ unlabelled++;
2286
+ return;
2287
+ }
2288
+ const color = contrastingTextColor(fill);
2289
+ marks.push({ kind: "text", key: `label-cell-${id}`, role: "data-label", x: r.x + 4, y: r.y + 4, text: truncate(node.name, room, fs, measure), anchor: "start", baseline: "top", size: fs, weight: 600, fill: color, target });
2290
+ if (r.height >= fs * 3) marks.push({ kind: "text", key: `label-value-${id}`, role: "data-label", x: r.x + 4, y: r.y + 6 + fs * 1.2, text: truncate(formatChartNumber(_nullishCoalesce(node.value, () => ( 0)), format), room, fs - 1, measure), anchor: "start", baseline: "top", size: fs - 1, fill: color, target });
2291
+ });
2292
+ };
2293
+ draw(spec.tree, area, "n", null, null);
2294
+ if (unlabelled) ctx.warn("data-labels-hidden", `${unlabelled} treemap cell${unlabelled === 1 ? " is" : "s are"} too small for a label.`);
2295
+ return { plot: area };
2296
+ }
2297
+ function layoutSunburst(ctx, area, series) {
2298
+ const { spec, marks, theme, fs, measure } = ctx;
2299
+ const tree = spec.tree.length ? spec.tree : _chunkR3P2M7HUcjs.chartTreeFromSeries.call(void 0, spec.categories, _nullishCoalesce(_optionalChain([series, 'optionalAccess', _54 => _54.values]), () => ( [])));
2300
+ const indexOf = new Map(spec.categories.map((c, i) => [c.trim() || `${i + 1}`, i]));
2301
+ const depth = _chunkR3P2M7HUcjs.chartTreeDepth.call(void 0, tree);
2302
+ const total = tree.reduce((t, n) => t + _chunkR3P2M7HUcjs.chartTreeTotal.call(void 0, n), 0);
2303
+ const cx = area.x + area.width / 2;
2304
+ const cy = area.y + area.height / 2;
2305
+ const outer = Math.max(8, Math.min(area.width, area.height) / 2 - 4);
2306
+ const plot = { x: cx - outer, y: cy - outer, width: outer * 2, height: outer * 2 };
2307
+ if (!(total > 0)) {
2308
+ marks.push({ kind: "text", key: "empty", role: "empty", x: cx, y: cy, text: "No positive values", anchor: "middle", baseline: "middle", size: fs + 1, fill: theme.mutedText });
2309
+ return { plot };
2310
+ }
2311
+ const hole = outer * 0.28;
2312
+ const ring = (outer - hole) / Math.max(1, depth);
2313
+ const format = _nullishCoalesce(_optionalChain([series, 'optionalAccess', _55 => _55.format]), () => ( spec.yAxis.format));
2314
+ let unlabelled = 0;
2315
+ const walk = (nodes, start, sweep, level, key, group, base) => {
2316
+ const sum = nodes.reduce((t, n) => t + _chunkR3P2M7HUcjs.chartTreeTotal.call(void 0, n), 0);
2317
+ let at = start;
2318
+ nodes.forEach((node, k) => {
2319
+ const value = _chunkR3P2M7HUcjs.chartTreeTotal.call(void 0, node);
2320
+ if (!(value > 0) || !sum) return;
2321
+ const a0 = at;
2322
+ const a1 = at + sweep * value / sum;
2323
+ at = a1;
2324
+ const g = _nullishCoalesce(_nullishCoalesce(group, () => ( indexOf.get(node.name))), () => ( k));
2325
+ const target = { kind: "category", index: g };
2326
+ const selected = ctx.isSelected(target) || ctx.isSelected({ kind: "point", seriesId: _nullishCoalesce(_optionalChain([series, 'optionalAccess', _56 => _56.id]), () => ( "")), index: g });
2327
+ const fill = treeColor(ctx, series, node, g, base, 0.22 * level);
2328
+ const id = `${key}-${k}`;
2329
+ const r0 = hole + ring * level;
2330
+ const r1 = r0 + ring;
2331
+ marks.push({ kind: "path", key: `slice-${id}`, role: "slice", d: arcPath(cx, cy, r1, r0, a0, a1), fill, stroke: selected ? theme.selection : theme.surface, strokeWidth: selected ? 2 : 1, target, selected });
2332
+ const mid = (a0 + a1) / 2;
2333
+ const rm = (r0 + r1) / 2;
2334
+ const text = node.name;
2335
+ const width = measure(text, fs - 1);
2336
+ if (ring >= fs * 1.3 && 2 * rm * Math.sin(Math.min(Math.PI / 2, (a1 - a0) / 2)) >= width + 6) marks.push({ kind: "text", key: `label-slice-${id}`, role: "data-label", x: cx + rm * Math.sin(mid), y: cy - rm * Math.cos(mid), text, anchor: "middle", baseline: "middle", size: fs - 1, weight: level === 0 ? 600 : void 0, fill: contrastingTextColor(fill), target });
2337
+ else unlabelled++;
2338
+ if (node.children) walk(node.children, a0, a1 - a0, level + 1, id, g, _nullishCoalesce(base, () => ( fill)));
2339
+ });
2340
+ };
2341
+ walk(tree, 0, Math.PI * 2, 0, "n", null, null);
2342
+ if (unlabelled) ctx.warn("data-labels-hidden", `${unlabelled} sunburst arc${unlabelled === 1 ? " is" : "s are"} too narrow for a label.`);
2343
+ if (hole > fs * 2) {
2344
+ const size = Math.min(theme.titleSize + 4, hole * 0.45);
2345
+ const centre = series ? { kind: "series", seriesId: series.id } : void 0;
2346
+ const caption = spec.sunburstLabel;
2347
+ marks.push({ kind: "text", key: "sunburst-total", role: "data-label", x: cx, y: caption ? cy - 2 : cy, text: formatChartNumber(total, format), anchor: "middle", baseline: caption ? "bottom" : "middle", size, weight: 700, fill: theme.text, ...centre ? { target: centre } : {} });
2348
+ if (caption) marks.push({ kind: "text", key: "sunburst-caption", role: "data-label", x: cx, y: cy + 2, text: truncate(caption, hole * 1.8, fs - 1, measure), anchor: "middle", baseline: "top", size: fs - 1, fill: theme.mutedText, ...centre ? { target: centre } : {} });
2349
+ }
2350
+ return { plot };
2351
+ }
1846
2352
 
1847
2353
  // src/chart/layout.ts
1848
2354
  function legendItems(spec, ctx) {
1849
- const family = _chunkLL3H5XUHcjs.chartFamily.call(void 0, spec);
1850
- if (spec.type === "funnel") return [];
1851
- if (_chunkLL3H5XUHcjs.chartColorsByCategory.call(void 0, spec.type)) {
2355
+ const family = _chunkR3P2M7HUcjs.chartFamily.call(void 0, spec);
2356
+ if (spec.type === "funnel" || spec.type === "slope") return [];
2357
+ if (_chunkR3P2M7HUcjs.chartSpecColorsByCategory.call(void 0, spec)) {
1852
2358
  const first = spec.series.find((s) => !s.hidden);
2359
+ const seats = (i) => Math.max(0, Math.round(_nullishCoalesce(_optionalChain([first, 'optionalAccess', _57 => _57.values, 'access', _58 => _58[i]]), () => ( 0))));
1853
2360
  return spec.categories.map((c, i) => ({
1854
- label: c,
1855
- color: _optionalChain([first, 'optionalAccess', _45 => _45.pointColors, 'optionalAccess', _46 => _46[i]]) || _chunkLL3H5XUHcjs.chartSliceColor.call(void 0, spec, i),
2361
+ label: spec.type === "parliament" ? `${c} ${seats(i)}` : c,
2362
+ color: _optionalChain([first, 'optionalAccess', _59 => _59.pointColors, 'optionalAccess', _60 => _60[i]]) || _chunkR3P2M7HUcjs.chartSliceColor.call(void 0, spec, i),
1856
2363
  target: { kind: "category", index: i },
1857
2364
  faded: false,
1858
2365
  selected: ctx.isSelected({ kind: "category", index: i }),
@@ -1866,17 +2373,17 @@ function legendItems(spec, ctx) {
1866
2373
  return [
1867
2374
  { label: "Increase", color: ctx.theme.positive, target, faded: false, selected: false, shape: "square" },
1868
2375
  { label: "Decrease", color: ctx.theme.negative, target, faded: false, selected: false, shape: "square" },
1869
- { label: "Total", color: _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(first)), target, faded: false, selected: false, shape: "square" }
2376
+ { label: "Total", color: _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(first)), target, faded: false, selected: false, shape: "square" }
1870
2377
  ];
1871
2378
  }
1872
2379
  if (family === "box") return [];
1873
- const trends = trendlineLegendItems(spec, (i) => _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, i));
2380
+ const trends = trendlineLegendItems(spec, (i) => _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, i));
1874
2381
  const items = [];
1875
2382
  spec.series.forEach((s, i) => {
1876
- const type = _chunkLL3H5XUHcjs.chartSeriesType.call(void 0, spec, s);
2383
+ const type = _chunkR3P2M7HUcjs.chartSeriesType.call(void 0, spec, s);
1877
2384
  items.push({
1878
2385
  label: s.name,
1879
- color: _chunkLL3H5XUHcjs.chartSeriesColor.call(void 0, spec, i),
2386
+ color: _chunkR3P2M7HUcjs.chartSeriesColor.call(void 0, spec, i),
1880
2387
  target: { kind: "series", seriesId: s.id },
1881
2388
  faded: !!s.hidden,
1882
2389
  selected: ctx.seriesSelected(s.id),
@@ -1889,7 +2396,7 @@ function legendItems(spec, ctx) {
1889
2396
  function layoutChart(spec, options) {
1890
2397
  const width = Math.max(1, Number.isFinite(options.width) ? options.width : 1);
1891
2398
  const height = Math.max(1, Number.isFinite(options.height) ? options.height : 1);
1892
- const theme = { ..._chunkLL3H5XUHcjs.DEFAULT_CHART_THEME, ...options.theme };
2399
+ const theme = { ..._chunkR3P2M7HUcjs.DEFAULT_CHART_THEME, ...options.theme };
1893
2400
  const selection = _nullishCoalesce(options.selection, () => ( null));
1894
2401
  const marks = [];
1895
2402
  const warnings = [];
@@ -1905,7 +2412,7 @@ function layoutChart(spec, options) {
1905
2412
  warnings,
1906
2413
  selection,
1907
2414
  isSelected: (target) => sameChartSelection(selection, target),
1908
- seriesSelected: (id) => _optionalChain([selection, 'optionalAccess', _47 => _47.kind]) === "series" && selection.seriesId === id,
2415
+ seriesSelected: (id) => _optionalChain([selection, 'optionalAccess', _61 => _61.kind]) === "series" && selection.seriesId === id,
1909
2416
  outline: (key, box) => marks.push({ kind: "rect", key, role: "selection", ...box, fill: "none", stroke: theme.selection, strokeWidth: 1, radius: 3 }),
1910
2417
  warn: (code, message) => {
1911
2418
  if (!warnings.some((w) => w.code === code && w.message === message)) warnings.push({ code, message });
@@ -1928,15 +2435,22 @@ function layoutChart(spec, options) {
1928
2435
  if (spec.title) titleMark("title", spec.title, theme.titleSize, 600, theme.text);
1929
2436
  if (spec.subtitle) titleMark("subtitle", spec.subtitle, fs + 1, 400, theme.mutedText);
1930
2437
  if (spec.title || spec.subtitle) top += 6;
1931
- const family = _chunkLL3H5XUHcjs.chartFamily.call(void 0, spec);
2438
+ const family = _chunkR3P2M7HUcjs.chartFamily.call(void 0, spec);
1932
2439
  const visible = spec.series.filter((s) => !s.hidden);
1933
2440
  const items = legendItems(spec, ctx);
1934
- let area = { x: pad, y: top, width: width - pad * 2, height: height - top - pad };
2441
+ let area = { x: pad, y: top, width: width - pad * 2, height: height - top - pad - noteHeight(spec, fs) };
1935
2442
  const seriesLegend = family === "cartesian" || family === "xy" || family === "radar" || family === "histogram";
1936
2443
  const hasTrend = items.some((i) => i.shape === "dash");
1937
- const showLegend = spec.legend.show && items.length > 0 && (!seriesLegend || spec.series.length > 1 || spec.series.some((s) => s.hidden) || hasTrend);
2444
+ const showLegend = spec.legend.show && items.length > 0 && (!seriesLegend || _chunkR3P2M7HUcjs.chartVariesColors.call(void 0, spec) || spec.series.length > 1 || spec.series.some((s) => s.hidden) || hasTrend);
1938
2445
  if (showLegend) area = layoutLegend(items, spec.legend.position, area, ctx, ctx.isSelected({ kind: "legend" }));
1939
2446
  const finish = (result) => {
2447
+ if (result.plot.width > 0) {
2448
+ if (chartAnnotationFamily(_chunkR3P2M7HUcjs.chartFamily.call(void 0, spec))) drawCallouts(ctx, result.plot);
2449
+ else if (spec.referenceLines.length || spec.referenceBands.length || spec.series.some((s) => s.callouts))
2450
+ ctx.warn("annotations-skipped", `Reference lines, bands and callouts are drawn on bar, line, area, scatter and bubble charts; this ${spec.type} chart leaves them out.`);
2451
+ }
2452
+ applyHighlight(ctx);
2453
+ drawNote(ctx, pad);
1940
2454
  if (options.readout) addReadout(ctx, options.readout, result.plot);
1941
2455
  const kept = finalizeMarks(marks, width, height);
1942
2456
  if (kept.length < marks.length) ctx.warn("values-skipped", `${marks.length - kept.length} mark${marks.length - kept.length === 1 ? " was" : "s were"} left out: their values are not finite numbers.`);
@@ -1978,7 +2492,19 @@ function layoutChart(spec, options) {
1978
2492
  case "funnel":
1979
2493
  return finish(layoutFunnel(ctx, area, visible[0]));
1980
2494
  case "treemap":
1981
- return finish(layoutTreemap(ctx, area, visible[0]));
2495
+ return finish(spec.tree.length ? layoutTreemapTree(ctx, area, spec.series[0]) : layoutTreemap(ctx, area, visible[0]));
2496
+ case "sunburst":
2497
+ return finish(layoutSunburst(ctx, area, spec.tree.length ? spec.series[0] : visible[0]));
2498
+ case "slope":
2499
+ if (visible.length < 2) {
2500
+ ctx.warn("type-fallback", "A slope chart needs two series (before and after); it is drawn as a line chart until then.");
2501
+ return finish(layoutCartesian(ctx, area, visible));
2502
+ }
2503
+ return finish(layoutSlope(ctx, area, visible));
2504
+ case "likert":
2505
+ return finish(layoutLikert(ctx, area, visible));
2506
+ case "parliament":
2507
+ return finish(layoutParliament(ctx, area, visible[0]));
1982
2508
  case "xy":
1983
2509
  return finish(layoutXy(ctx, area, visible));
1984
2510
  default:
@@ -2062,9 +2588,9 @@ function layoutLegend(items, position, area, ctx, legendSelected) {
2062
2588
  function addReadout(ctx, readout, plot) {
2063
2589
  const { spec, marks, theme, fs, measure } = ctx;
2064
2590
  const series = spec.series.find((s) => s.id === readout.seriesId);
2065
- const value = _optionalChain([series, 'optionalAccess', _48 => _48.values, 'access', _49 => _49[readout.index]]);
2591
+ const value = _optionalChain([series, 'optionalAccess', _62 => _62.values, 'access', _63 => _63[readout.index]]);
2066
2592
  if (!series || value === null || value === void 0) return;
2067
- const mark = marks.find((m) => (m.role === "point" || m.role === "bar") && _optionalChain([m, 'access', _50 => _50.target, 'optionalAccess', _51 => _51.kind]) === "point" && m.target.seriesId === readout.seriesId && m.target.index === readout.index);
2593
+ const mark = marks.find((m) => (m.role === "point" || m.role === "bar") && _optionalChain([m, 'access', _64 => _64.target, 'optionalAccess', _65 => _65.kind]) === "point" && m.target.seriesId === readout.seriesId && m.target.index === readout.index);
2068
2594
  if (!mark) return;
2069
2595
  let ax;
2070
2596
  let ay;
@@ -2076,8 +2602,8 @@ function addReadout(ctx, readout, plot) {
2076
2602
  ay = mark.cy - mark.r;
2077
2603
  } else return;
2078
2604
  const axis = series.axis === "secondary" ? spec.yAxis2 : spec.yAxis;
2079
- const x = _optionalChain([series, 'access', _52 => _52.x, 'optionalAccess', _53 => _53[readout.index]]);
2080
- const text = x !== null && x !== void 0 && _chunkLL3H5XUHcjs.chartFamily.call(void 0, spec) === "xy" ? `${formatChartNumber(x, spec.xAxis.format)}, ${formatChartNumber(value, axis.format)}` : `${_nullishCoalesce(spec.categories[readout.index], () => ( ""))}: ${formatChartNumber(value, _nullishCoalesce(series.format, () => ( axis.format)))}`;
2605
+ const x = _optionalChain([series, 'access', _66 => _66.x, 'optionalAccess', _67 => _67[readout.index]]);
2606
+ const text = x !== null && x !== void 0 && _chunkR3P2M7HUcjs.chartFamily.call(void 0, spec) === "xy" ? `${formatChartNumber(x, spec.xAxis.format)}, ${formatChartNumber(value, axis.format)}` : `${_nullishCoalesce(spec.categories[readout.index], () => ( ""))}: ${formatChartNumber(value, _nullishCoalesce(series.format, () => ( axis.format)))}`;
2081
2607
  const w = measure(text, fs, 600) + 10;
2082
2608
  const h = fs + 8;
2083
2609
  const bx = Math.max(plot.x, Math.min(plot.x + plot.width - w, ax - w / 2));
@@ -2126,7 +2652,7 @@ function chartXAtPoint(scene, point) {
2126
2652
  if (!scale) return void 0;
2127
2653
  return chartScaleFromPixel(point.x, scale);
2128
2654
  }
2129
- function snapChartValue(scene, value, step, ticks = _nullishCoalesce(_optionalChain([scene, 'access', _54 => _54.valueScale, 'optionalAccess', _55 => _55.ticks]), () => ( [])), logBase) {
2655
+ function snapChartValue(scene, value, step, ticks = _nullishCoalesce(_optionalChain([scene, 'access', _68 => _68.valueScale, 'optionalAccess', _69 => _69.ticks]), () => ( [])), logBase) {
2130
2656
  if (logBase && step === void 0) return value > 0 && Number.isFinite(value) ? Number(value.toPrecision(2)) : value;
2131
2657
  const unit = _nullishCoalesce(step, () => ( (ticks.length > 1 ? (ticks[1] - ticks[0]) / 10 : 1)));
2132
2658
  if (!(unit > 0)) return value;
@@ -2173,4 +2699,5 @@ function snapChartValue(scene, value, step, ticks = _nullishCoalesce(_optionalCh
2173
2699
 
2174
2700
 
2175
2701
 
2176
- exports.parseChartDate = parseChartDate; exports.CHART_DATE_STEPS = CHART_DATE_STEPS; exports.formatChartDate = formatChartDate; exports.chartDateLabels = chartDateLabels; exports.compileChartFormatCode = compileChartFormatCode; exports.formatChartValueWithCode = formatChartValueWithCode; exports.chartSerialToDate = chartSerialToDate; exports.chartDateToSerial = chartDateToSerial; exports.niceStep = niceStep; exports.niceScale = niceScale; exports.stepDecimals = stepDecimals; exports.formatChartNumber = formatChartNumber; exports.linearMap = linearMap; exports.niceLogScale = niceLogScale; exports.chartScaleToPixel = chartScaleToPixel; exports.chartScaleFromPixel = chartScaleFromPixel; exports.estimateChartTextWidth = estimateChartTextWidth; exports.sameChartSelection = sameChartSelection; exports.contrastingTextColor = contrastingTextColor; exports.r2 = r2; exports.chartHistogram = chartHistogram; exports.chartQuantile = chartQuantile; exports.chartBoxStats = chartBoxStats; exports.chartSquarify = chartSquarify; exports.decimateChartPoints = decimateChartPoints; exports.summarizeChartSeries = summarizeChartSeries; exports.fitChartTrendline = fitChartTrendline; exports.trendNumberText = trendNumberText; exports.chartMovingAverage = chartMovingAverage; exports.chartTrendlineName = chartTrendlineName; exports.chartSupportsTrendlines = chartSupportsTrendlines; exports.MAX_CHART_DATA_LABELS = MAX_CHART_DATA_LABELS; exports.MAX_CHART_LINE_POINTS = MAX_CHART_LINE_POINTS; exports.MAX_CHART_MARKERS = MAX_CHART_MARKERS; exports.layoutChart = layoutChart; exports.chartValueAtPoint = chartValueAtPoint; exports.chartXAtPoint = chartXAtPoint; exports.snapChartValue = snapChartValue;
2702
+
2703
+ exports.parseChartDate = parseChartDate; exports.CHART_DATE_STEPS = CHART_DATE_STEPS; exports.formatChartDate = formatChartDate; exports.chartDateLabels = chartDateLabels; exports.compileChartFormatCode = compileChartFormatCode; exports.formatChartValueWithCode = formatChartValueWithCode; exports.chartSerialToDate = chartSerialToDate; exports.chartDateToSerial = chartDateToSerial; exports.niceStep = niceStep; exports.niceScale = niceScale; exports.stepDecimals = stepDecimals; exports.formatChartNumber = formatChartNumber; exports.linearMap = linearMap; exports.niceLogScale = niceLogScale; exports.chartScaleToPixel = chartScaleToPixel; exports.chartScaleFromPixel = chartScaleFromPixel; exports.estimateChartTextWidth = estimateChartTextWidth; exports.sameChartSelection = sameChartSelection; exports.contrastingTextColor = contrastingTextColor; exports.r2 = r2; exports.chartHistogram = chartHistogram; exports.chartQuantile = chartQuantile; exports.suggestChartReferences = suggestChartReferences; exports.chartBoxStats = chartBoxStats; exports.chartSquarify = chartSquarify; exports.decimateChartPoints = decimateChartPoints; exports.summarizeChartSeries = summarizeChartSeries; exports.fitChartTrendline = fitChartTrendline; exports.trendNumberText = trendNumberText; exports.chartMovingAverage = chartMovingAverage; exports.chartTrendlineName = chartTrendlineName; exports.chartSupportsTrendlines = chartSupportsTrendlines; exports.MAX_CHART_DATA_LABELS = MAX_CHART_DATA_LABELS; exports.MAX_CHART_LINE_POINTS = MAX_CHART_LINE_POINTS; exports.MAX_CHART_MARKERS = MAX_CHART_MARKERS; exports.layoutChart = layoutChart; exports.chartValueAtPoint = chartValueAtPoint; exports.chartXAtPoint = chartXAtPoint; exports.snapChartValue = snapChartValue;