@sliiu/present-renderer 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/COPYRIGHT +5 -0
  2. package/LICENSE +661 -0
  3. package/README.md +8 -2
  4. package/THIRD-PARTY-NOTICES.md +221 -0
  5. package/dist/cjs/arrow-geom.js +38 -0
  6. package/dist/cjs/canvas-backend.js +1368 -0
  7. package/dist/cjs/cell-borders.js +25 -0
  8. package/dist/cjs/chart-canvas.js +1059 -0
  9. package/dist/cjs/chart-ex-render.js +275 -0
  10. package/dist/cjs/chart-geom.js +313 -0
  11. package/dist/cjs/chart-svg.js +1110 -0
  12. package/dist/cjs/compose.js +74 -0
  13. package/dist/cjs/effect-clip.js +652 -0
  14. package/dist/cjs/entrance-hide.js +63 -0
  15. package/dist/cjs/font-stack.js +103 -0
  16. package/dist/cjs/hit-geom.js +50 -0
  17. package/dist/cjs/hybrid-backend.js +93 -0
  18. package/dist/cjs/hybrid-segments.js +54 -0
  19. package/dist/cjs/index.js +70 -0
  20. package/dist/cjs/linked-src.js +116 -0
  21. package/dist/cjs/metafile-preview.js +142 -0
  22. package/dist/cjs/model3d-frame.js +199 -0
  23. package/dist/cjs/model3d-render.js +78 -0
  24. package/dist/cjs/morph-transition.js +57 -0
  25. package/dist/cjs/node-affine.js +79 -0
  26. package/dist/cjs/ole-render.js +291 -0
  27. package/dist/cjs/package.json +1 -0
  28. package/dist/cjs/paint-canvas.js +701 -0
  29. package/dist/cjs/paint-svg.js +674 -0
  30. package/dist/cjs/scene-node.js +762 -0
  31. package/dist/cjs/selection.js +130 -0
  32. package/dist/cjs/shape-effects.js +66 -0
  33. package/dist/cjs/shape-front.js +38 -0
  34. package/dist/cjs/stage-chrome.js +15 -0
  35. package/dist/cjs/svg-backend.js +2172 -0
  36. package/dist/cjs/text-autofit.js +48 -0
  37. package/dist/cjs/text-hit.js +284 -0
  38. package/dist/cjs/text-layout.js +408 -0
  39. package/dist/cjs/text-shared.js +238 -0
  40. package/dist/cjs/text-stacked.js +82 -0
  41. package/dist/cjs/text-warp.js +377 -0
  42. package/dist/cjs/text-wordart.js +33 -0
  43. package/dist/cjs/video-frame.js +113 -0
  44. package/dist/cjs/webgl-model3d-backend.js +285 -0
  45. package/dist/cjs/webgl-shape-backend.js +360 -0
  46. package/dist/esm/arrow-geom.js +33 -0
  47. package/dist/esm/canvas-backend.js +1363 -0
  48. package/dist/esm/cell-borders.js +21 -0
  49. package/dist/esm/chart-canvas.js +1056 -0
  50. package/dist/esm/chart-ex-render.js +261 -0
  51. package/dist/esm/chart-geom.js +296 -0
  52. package/dist/esm/chart-svg.js +1107 -0
  53. package/dist/esm/compose.js +71 -0
  54. package/dist/esm/effect-clip.js +648 -0
  55. package/dist/esm/entrance-hide.js +59 -0
  56. package/dist/esm/font-stack.js +100 -0
  57. package/dist/esm/hit-geom.js +45 -0
  58. package/dist/esm/hybrid-backend.js +89 -0
  59. package/dist/esm/hybrid-segments.js +51 -0
  60. package/dist/esm/index.js +24 -0
  61. package/dist/esm/linked-src.js +109 -0
  62. package/dist/esm/metafile-preview.js +135 -0
  63. package/dist/esm/model3d-frame.js +158 -0
  64. package/dist/esm/model3d-render.js +73 -0
  65. package/dist/esm/morph-transition.js +54 -0
  66. package/dist/esm/node-affine.js +71 -0
  67. package/dist/esm/ole-render.js +286 -0
  68. package/dist/esm/paint-canvas.js +693 -0
  69. package/dist/esm/paint-svg.js +670 -0
  70. package/dist/esm/scene-node.js +750 -0
  71. package/dist/esm/selection.js +122 -0
  72. package/dist/esm/shape-effects.js +60 -0
  73. package/dist/esm/shape-front.js +35 -0
  74. package/dist/esm/stage-chrome.js +12 -0
  75. package/dist/esm/svg-backend.js +2168 -0
  76. package/dist/esm/text-autofit.js +44 -0
  77. package/dist/esm/text-hit.js +280 -0
  78. package/dist/esm/text-layout.js +400 -0
  79. package/dist/esm/text-shared.js +221 -0
  80. package/dist/esm/text-stacked.js +78 -0
  81. package/dist/esm/text-warp.js +371 -0
  82. package/dist/esm/text-wordart.js +30 -0
  83. package/dist/esm/video-frame.js +109 -0
  84. package/dist/esm/webgl-model3d-backend.js +248 -0
  85. package/dist/esm/webgl-shape-backend.js +323 -0
  86. package/dist/types/arrow-geom.d.ts +10 -0
  87. package/dist/types/canvas-backend.d.ts +55 -0
  88. package/dist/types/cell-borders.d.ts +9 -0
  89. package/dist/types/chart-canvas.d.ts +2 -0
  90. package/dist/types/chart-ex-render.d.ts +79 -0
  91. package/dist/types/chart-geom.d.ts +112 -0
  92. package/dist/types/chart-svg.d.ts +2 -0
  93. package/dist/types/compose.d.ts +2 -0
  94. package/dist/types/effect-clip.d.ts +10 -0
  95. package/dist/types/entrance-hide.d.ts +16 -0
  96. package/dist/types/font-stack.d.ts +9 -0
  97. package/dist/types/hit-geom.d.ts +15 -0
  98. package/dist/types/hybrid-backend.d.ts +21 -0
  99. package/dist/types/hybrid-segments.d.ts +11 -0
  100. package/dist/types/index.d.ts +24 -0
  101. package/dist/types/linked-src.d.ts +13 -0
  102. package/dist/types/metafile-preview.d.ts +11 -0
  103. package/dist/types/model3d-frame.d.ts +8 -0
  104. package/dist/types/model3d-render.d.ts +4 -0
  105. package/dist/types/morph-transition.d.ts +15 -0
  106. package/dist/types/node-affine.d.ts +22 -0
  107. package/dist/types/ole-render.d.ts +7 -0
  108. package/dist/types/paint-canvas.d.ts +22 -0
  109. package/dist/types/paint-svg.d.ts +19 -0
  110. package/dist/types/scene-node.d.ts +128 -0
  111. package/dist/types/selection.d.ts +22 -0
  112. package/dist/types/shape-effects.d.ts +13 -0
  113. package/dist/types/shape-front.d.ts +4 -0
  114. package/dist/types/stage-chrome.d.ts +8 -0
  115. package/dist/types/svg-backend.d.ts +88 -0
  116. package/dist/types/text-autofit.d.ts +7 -0
  117. package/dist/types/text-hit.d.ts +7 -0
  118. package/dist/types/text-layout.d.ts +35 -0
  119. package/dist/types/text-shared.d.ts +86 -0
  120. package/dist/types/text-stacked.d.ts +11 -0
  121. package/dist/types/text-warp.d.ts +37 -0
  122. package/dist/types/text-wordart.d.ts +18 -0
  123. package/dist/types/video-frame.d.ts +2 -0
  124. package/dist/types/webgl-model3d-backend.d.ts +25 -0
  125. package/dist/types/webgl-shape-backend.d.ts +33 -0
  126. package/package.json +43 -4
@@ -0,0 +1,1110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderChart = renderChart;
4
+ const chart_ex_render_1 = require("./chart-ex-render");
5
+ const chart_geom_1 = require("./chart-geom");
6
+ /** Text / line colours of the chart being drawn; set once per `renderChart` (drawing is synchronous). */
7
+ let ts = (0, chart_geom_1.chartTextStyle)({});
8
+ const SVG_NS = "http://www.w3.org/2000/svg";
9
+ function poly(g, pts, fill) {
10
+ const p = document.createElementNS(SVG_NS, "polygon");
11
+ p.setAttribute("points", pts);
12
+ p.setAttribute("fill", fill);
13
+ g.appendChild(p);
14
+ }
15
+ function appendExtrudedRect(g, x, y, w, h, color, off) {
16
+ if (off && (off.dx || off.dy) && w > 0 && h > 0) {
17
+ const { dx, dy } = off;
18
+ poly(g, `${x},${y} ${x + dx},${y + dy} ${x + w + dx},${y + dy} ${x + w},${y}`, (0, chart_geom_1.shadeHex)(color, 1.18));
19
+ poly(g, `${x + w},${y} ${x + w + dx},${y + dy} ${x + w + dx},${y + h + dy} ${x + w},${y + h}`, (0, chart_geom_1.shadeHex)(color, 0.72));
20
+ }
21
+ const rect = document.createElementNS(SVG_NS, "rect");
22
+ rect.setAttribute("x", String(x));
23
+ rect.setAttribute("y", String(y));
24
+ rect.setAttribute("width", String(w));
25
+ rect.setAttribute("height", String(h));
26
+ rect.setAttribute("fill", color);
27
+ g.appendChild(rect);
28
+ }
29
+ function renderChart(g, shape) {
30
+ while (g.firstChild)
31
+ g.removeChild(g.firstChild);
32
+ ts = (0, chart_geom_1.chartTextStyle)(shape.chart);
33
+ const { x, y, w, h } = shape.bbox;
34
+ const padTop = shape.chart.title ? 28 : 8;
35
+ const pads = (0, chart_geom_1.chartPadding)(shape.chart, ts.size);
36
+ const padBottom = pads.bottom;
37
+ const padLeft = 40;
38
+ const padRight = 12;
39
+ if (shape.chart.title) {
40
+ const t = document.createElementNS(SVG_NS, "text");
41
+ t.setAttribute("x", String(x + w / 2));
42
+ t.setAttribute("y", String(y + 18));
43
+ t.setAttribute("text-anchor", "middle");
44
+ t.setAttribute("font-size", String(ts.size + 1));
45
+ t.setAttribute("font-weight", "600");
46
+ t.setAttribute("fill", ts.text);
47
+ t.textContent = shape.chart.title;
48
+ g.appendChild(t);
49
+ }
50
+ const plot = {
51
+ x: x + padLeft,
52
+ y: y + padTop,
53
+ w: w - padLeft - padRight,
54
+ h: h - padTop - padBottom,
55
+ };
56
+ const def = shape.chart;
57
+ if (hasCombo(def)) {
58
+ drawCombo(g, def, plot);
59
+ }
60
+ else {
61
+ switch (def.type) {
62
+ case "bar":
63
+ case "column":
64
+ drawBar(g, def, plot);
65
+ break;
66
+ case "line":
67
+ drawLine(g, def, plot);
68
+ break;
69
+ case "pie":
70
+ case "doughnut":
71
+ drawPie(g, def, plot);
72
+ break;
73
+ case "area":
74
+ drawArea(g, def, plot);
75
+ break;
76
+ case "scatter":
77
+ drawScatter(g, def, plot);
78
+ break;
79
+ case "bubble":
80
+ drawBubble(g, def, plot);
81
+ break;
82
+ case "radar":
83
+ drawRadar(g, def, plot);
84
+ break;
85
+ case "stock":
86
+ drawStock(g, def, plot);
87
+ break;
88
+ case "surface":
89
+ drawSurface(g, def, plot);
90
+ break;
91
+ case "sunburst":
92
+ drawSunburst(g, def, plot);
93
+ break;
94
+ case "treemap":
95
+ drawTreemap(g, def, plot);
96
+ break;
97
+ case "waterfall":
98
+ drawWaterfall(g, def, plot);
99
+ break;
100
+ case "funnel":
101
+ drawFunnel(g, def, plot);
102
+ break;
103
+ case "pareto":
104
+ drawPareto(g, def, plot);
105
+ break;
106
+ case "histogram":
107
+ drawHistogram(g, def, plot);
108
+ break;
109
+ case "boxWhisker":
110
+ drawBoxWhisker(g, def, plot);
111
+ break;
112
+ case "region":
113
+ drawRegion(g, def, plot);
114
+ break;
115
+ default:
116
+ drawChartExFallback(g, def, plot);
117
+ break;
118
+ }
119
+ }
120
+ if (!hasCombo(def))
121
+ overlayGuides(g, def, plot);
122
+ overlayDataLabels(g, def, plot);
123
+ if (def.legend)
124
+ drawLegend(g, def, { x, y: y + h - pads.legend, w, h: pads.legend });
125
+ }
126
+ function drawAxes(g, plot, ticks, categories, isBar) {
127
+ const min = ticks[0];
128
+ const max = ticks[ticks.length - 1];
129
+ const xAxis = document.createElementNS(SVG_NS, "line");
130
+ xAxis.setAttribute("x1", String(plot.x));
131
+ xAxis.setAttribute("y1", String(plot.y + plot.h));
132
+ xAxis.setAttribute("x2", String(plot.x + plot.w));
133
+ xAxis.setAttribute("y2", String(plot.y + plot.h));
134
+ xAxis.setAttribute("stroke", ts.axis);
135
+ g.appendChild(xAxis);
136
+ const yAxis = document.createElementNS(SVG_NS, "line");
137
+ yAxis.setAttribute("x1", String(plot.x));
138
+ yAxis.setAttribute("y1", String(plot.y));
139
+ yAxis.setAttribute("x2", String(plot.x));
140
+ yAxis.setAttribute("y2", String(plot.y + plot.h));
141
+ yAxis.setAttribute("stroke", ts.axis);
142
+ g.appendChild(yAxis);
143
+ // barDir="bar" (§21.2.2.17) turns the value axis along the bottom: ticks run left→right with
144
+ // vertical gridlines, and the left edge belongs to the category labels.
145
+ for (const t of ticks) {
146
+ const f = (t - min) / (max - min);
147
+ const grid = document.createElementNS(SVG_NS, "line");
148
+ const lab = document.createElementNS(SVG_NS, "text");
149
+ if (isBar) {
150
+ const nx = plot.x + f * plot.w;
151
+ grid.setAttribute("x1", String(nx));
152
+ grid.setAttribute("y1", String(plot.y));
153
+ grid.setAttribute("x2", String(nx));
154
+ grid.setAttribute("y2", String(plot.y + plot.h));
155
+ lab.setAttribute("x", String(nx));
156
+ lab.setAttribute("y", String(plot.y + plot.h + 14));
157
+ lab.setAttribute("text-anchor", "middle");
158
+ }
159
+ else {
160
+ const ny = plot.y + plot.h - f * plot.h;
161
+ grid.setAttribute("x1", String(plot.x));
162
+ grid.setAttribute("y1", String(ny));
163
+ grid.setAttribute("x2", String(plot.x + plot.w));
164
+ grid.setAttribute("y2", String(ny));
165
+ lab.setAttribute("x", String(plot.x - 6));
166
+ lab.setAttribute("y", String(ny + 3));
167
+ lab.setAttribute("text-anchor", "end");
168
+ }
169
+ grid.setAttribute("stroke", ts.grid);
170
+ g.appendChild(grid);
171
+ lab.setAttribute("font-size", String(ts.size));
172
+ lab.setAttribute("fill", ts.muted);
173
+ lab.textContent = String(t);
174
+ g.appendChild(lab);
175
+ }
176
+ if (!isBar) {
177
+ const step = categories.length > 0 ? plot.w / categories.length : 0;
178
+ categories.forEach((c, i) => {
179
+ const lab = document.createElementNS(SVG_NS, "text");
180
+ lab.setAttribute("x", String(plot.x + step * (i + 0.5)));
181
+ lab.setAttribute("y", String(plot.y + plot.h + 14));
182
+ lab.setAttribute("text-anchor", "middle");
183
+ lab.setAttribute("font-size", String(ts.size));
184
+ lab.setAttribute("fill", ts.muted);
185
+ lab.textContent = c;
186
+ g.appendChild(lab);
187
+ });
188
+ }
189
+ else {
190
+ const step = categories.length > 0 ? plot.h / categories.length : 0;
191
+ categories.forEach((c, i) => {
192
+ const lab = document.createElementNS(SVG_NS, "text");
193
+ lab.setAttribute("x", String(plot.x - 6));
194
+ lab.setAttribute("y", String(plot.y + step * (i + 0.5) + 3));
195
+ lab.setAttribute("text-anchor", "end");
196
+ lab.setAttribute("font-size", String(ts.size));
197
+ lab.setAttribute("fill", ts.muted);
198
+ lab.textContent = c;
199
+ g.appendChild(lab);
200
+ });
201
+ }
202
+ }
203
+ function drawChartExFallback(g, def, plot) {
204
+ const categories = def.categories?.length
205
+ ? def.categories
206
+ : Array.from({ length: Math.max(1, def.series[0]?.values.length ?? 1) }, (_, i) => `Cat ${i + 1}`);
207
+ const series = def.series.length > 0
208
+ ? def.series
209
+ : [{ name: "Series 1", values: categories.map(() => 0) }];
210
+ const fallback = {
211
+ type: "column",
212
+ title: def.title,
213
+ categories,
214
+ series,
215
+ legend: def.legend,
216
+ colors: def.colors,
217
+ varyColors: def.varyColors ?? true,
218
+ };
219
+ drawBar(g, fallback, plot);
220
+ }
221
+ function drawTreemap(g, def, plot) {
222
+ const slices = (0, chart_ex_render_1.normalizeChartExSlices)(def);
223
+ for (const cell of (0, chart_ex_render_1.layoutTreemap)(slices, plot)) {
224
+ const rect = document.createElementNS(SVG_NS, "rect");
225
+ rect.setAttribute("x", String(cell.x));
226
+ rect.setAttribute("y", String(cell.y));
227
+ rect.setAttribute("width", String(cell.w));
228
+ rect.setAttribute("height", String(cell.h));
229
+ rect.setAttribute("fill", cell.color);
230
+ rect.setAttribute("stroke", "#ffffff");
231
+ rect.setAttribute("stroke-width", "1.5");
232
+ g.appendChild(rect);
233
+ if (cell.w > 36 && cell.h > 18) {
234
+ const t = document.createElementNS(SVG_NS, "text");
235
+ t.setAttribute("x", String(cell.x + 6));
236
+ t.setAttribute("y", String(cell.y + 14));
237
+ t.setAttribute("font-size", String(ts.size));
238
+ t.setAttribute("fill", "#f8fafc");
239
+ t.textContent = cell.label;
240
+ g.appendChild(t);
241
+ }
242
+ }
243
+ }
244
+ function drawSunburst(g, def, plot) {
245
+ const slices = (0, chart_ex_render_1.normalizeChartExSlices)(def);
246
+ for (const arc of (0, chart_ex_render_1.layoutSunburst)(slices, plot)) {
247
+ const path = document.createElementNS(SVG_NS, "path");
248
+ path.setAttribute("d", (0, chart_ex_render_1.sunburstArcPath)(arc));
249
+ path.setAttribute("fill", arc.color);
250
+ path.setAttribute("stroke", "#ffffff");
251
+ path.setAttribute("stroke-width", "1");
252
+ g.appendChild(path);
253
+ }
254
+ }
255
+ function drawWaterfall(g, def, plot) {
256
+ const slices = (0, chart_ex_render_1.normalizeChartExSlices)(def, { preserveSign: true });
257
+ const bars = (0, chart_ex_render_1.layoutWaterfall)(slices, plot);
258
+ for (const bar of bars) {
259
+ const rect = document.createElementNS(SVG_NS, "rect");
260
+ rect.setAttribute("x", String(bar.x));
261
+ rect.setAttribute("y", String(bar.y));
262
+ rect.setAttribute("width", String(bar.w));
263
+ rect.setAttribute("height", String(bar.h));
264
+ rect.setAttribute("fill", bar.kind === "decrease" ? "#E15759" : bar.color);
265
+ rect.setAttribute("rx", "2");
266
+ g.appendChild(rect);
267
+ }
268
+ for (let i = 0; i < bars.length - 1; i++) {
269
+ const a = bars[i];
270
+ const b = bars[i + 1];
271
+ const line = document.createElementNS(SVG_NS, "line");
272
+ line.setAttribute("x1", String(a.x + a.w));
273
+ line.setAttribute("y1", String(a.y));
274
+ line.setAttribute("x2", String(b.x));
275
+ line.setAttribute("y2", String(b.y));
276
+ line.setAttribute("stroke", ts.axis);
277
+ line.setAttribute("stroke-dasharray", "3 2");
278
+ g.appendChild(line);
279
+ }
280
+ }
281
+ function drawFunnel(g, def, plot) {
282
+ const slices = (0, chart_ex_render_1.normalizeChartExSlices)(def);
283
+ for (const seg of (0, chart_ex_render_1.layoutFunnel)(slices, plot)) {
284
+ const path = document.createElementNS(SVG_NS, "path");
285
+ path.setAttribute("d", (0, chart_ex_render_1.polygonPath)(seg.points));
286
+ path.setAttribute("fill", seg.color);
287
+ path.setAttribute("stroke", "#ffffff");
288
+ path.setAttribute("stroke-width", "1.5");
289
+ g.appendChild(path);
290
+ const cx = seg.points.reduce((s, p) => s + p[0], 0) / seg.points.length;
291
+ const cy = seg.points.reduce((s, p) => s + p[1], 0) / seg.points.length;
292
+ const t = document.createElementNS(SVG_NS, "text");
293
+ t.setAttribute("x", String(cx));
294
+ t.setAttribute("y", String(cy + 4));
295
+ t.setAttribute("text-anchor", "middle");
296
+ t.setAttribute("font-size", String(ts.size));
297
+ t.setAttribute("fill", "#f8fafc");
298
+ t.textContent = seg.label;
299
+ g.appendChild(t);
300
+ }
301
+ }
302
+ function drawColumnBars(g, bars) {
303
+ for (const bar of bars) {
304
+ const rect = document.createElementNS(SVG_NS, "rect");
305
+ rect.setAttribute("x", String(bar.x));
306
+ rect.setAttribute("y", String(bar.y));
307
+ rect.setAttribute("width", String(bar.w));
308
+ rect.setAttribute("height", String(bar.h));
309
+ rect.setAttribute("fill", bar.color);
310
+ rect.setAttribute("rx", "2");
311
+ g.appendChild(rect);
312
+ }
313
+ }
314
+ function drawPareto(g, def, plot) {
315
+ const { bars, line } = (0, chart_ex_render_1.layoutPareto)((0, chart_ex_render_1.normalizeChartExSlices)(def), plot);
316
+ drawColumnBars(g, bars);
317
+ if (line.length >= 2) {
318
+ const path = document.createElementNS(SVG_NS, "polyline");
319
+ path.setAttribute("points", line.map((p) => `${p[0]},${p[1]}`).join(" "));
320
+ path.setAttribute("fill", "none");
321
+ path.setAttribute("stroke", "#E15759");
322
+ path.setAttribute("stroke-width", "2");
323
+ g.appendChild(path);
324
+ for (const [x, y] of line) {
325
+ const dot = document.createElementNS(SVG_NS, "circle");
326
+ dot.setAttribute("cx", String(x));
327
+ dot.setAttribute("cy", String(y));
328
+ dot.setAttribute("r", "3");
329
+ dot.setAttribute("fill", "#E15759");
330
+ g.appendChild(dot);
331
+ }
332
+ }
333
+ }
334
+ function drawHistogram(g, def, plot) {
335
+ drawColumnBars(g, (0, chart_ex_render_1.layoutHistogram)((0, chart_ex_render_1.normalizeChartExSlices)(def), plot));
336
+ }
337
+ function drawBoxWhisker(g, def, plot) {
338
+ for (const box of (0, chart_ex_render_1.layoutBoxWhisker)((0, chart_ex_render_1.normalizeChartExSlices)(def), plot)) {
339
+ const whisker = document.createElementNS(SVG_NS, "line");
340
+ whisker.setAttribute("x1", String(box.cx));
341
+ whisker.setAttribute("y1", String(box.whiskerLow));
342
+ whisker.setAttribute("x2", String(box.cx));
343
+ whisker.setAttribute("y2", String(box.whiskerHigh));
344
+ whisker.setAttribute("stroke", "#374151");
345
+ whisker.setAttribute("stroke-width", "1");
346
+ g.appendChild(whisker);
347
+ const capW = box.boxW * 0.5;
348
+ for (const y of [box.whiskerLow, box.whiskerHigh]) {
349
+ const cap = document.createElementNS(SVG_NS, "line");
350
+ cap.setAttribute("x1", String(box.cx - capW / 2));
351
+ cap.setAttribute("x2", String(box.cx + capW / 2));
352
+ cap.setAttribute("y1", String(y));
353
+ cap.setAttribute("y2", String(y));
354
+ cap.setAttribute("stroke", "#374151");
355
+ g.appendChild(cap);
356
+ }
357
+ const rect = document.createElementNS(SVG_NS, "rect");
358
+ rect.setAttribute("x", String(box.cx - box.boxW / 2));
359
+ rect.setAttribute("y", String(Math.min(box.q1, box.q3)));
360
+ rect.setAttribute("width", String(box.boxW));
361
+ rect.setAttribute("height", String(Math.abs(box.q3 - box.q1)));
362
+ rect.setAttribute("fill", box.color);
363
+ rect.setAttribute("fill-opacity", "0.75");
364
+ g.appendChild(rect);
365
+ const med = document.createElementNS(SVG_NS, "line");
366
+ med.setAttribute("x1", String(box.cx - box.boxW / 2));
367
+ med.setAttribute("x2", String(box.cx + box.boxW / 2));
368
+ med.setAttribute("y1", String(box.median));
369
+ med.setAttribute("y2", String(box.median));
370
+ med.setAttribute("stroke", "#1f2937");
371
+ med.setAttribute("stroke-width", "2");
372
+ g.appendChild(med);
373
+ }
374
+ }
375
+ function drawRegion(g, def, plot) {
376
+ for (const row of (0, chart_ex_render_1.layoutRegion)((0, chart_ex_render_1.normalizeChartExSlices)(def), plot)) {
377
+ const rect = document.createElementNS(SVG_NS, "rect");
378
+ rect.setAttribute("x", String(row.x));
379
+ rect.setAttribute("y", String(row.y));
380
+ rect.setAttribute("width", String(row.w));
381
+ rect.setAttribute("height", String(row.h));
382
+ rect.setAttribute("fill", row.color);
383
+ rect.setAttribute("rx", "2");
384
+ g.appendChild(rect);
385
+ if (row.h > 14) {
386
+ const t = document.createElementNS(SVG_NS, "text");
387
+ t.setAttribute("x", String(row.x + 6));
388
+ t.setAttribute("y", String(row.y + row.h / 2 + 4));
389
+ t.setAttribute("font-size", String(ts.size));
390
+ t.setAttribute("fill", "#f8fafc");
391
+ t.textContent = row.label;
392
+ g.appendChild(t);
393
+ }
394
+ }
395
+ }
396
+ function drawBar(g, def, plot, shared) {
397
+ const cats = def.categories ?? [];
398
+ const series = def.series;
399
+ const { isHoriz, stacked, ticks, tmin, tmax, groupStep, inner, barW, totals } = (0, chart_geom_1.barGeometry)(def, plot, shared?.ticks);
400
+ if (!shared?.skipAxes)
401
+ drawAxes(g, plot, ticks, cats, isHoriz);
402
+ const extrude = (0, chart_geom_1.chart3DOffset)(def);
403
+ for (let ci = 0; ci < cats.length; ci++) {
404
+ let stackPos = 0;
405
+ let stackNeg = 0;
406
+ for (let si = 0; si < series.length; si++) {
407
+ let v = series[si].values[ci] ?? 0;
408
+ if (def.percentStacked)
409
+ v = (totals[ci] ?? 0) > 0 ? v / totals[ci] : 0;
410
+ const color = series[si].color ?? "#3b82f6";
411
+ if (isHoriz) {
412
+ const x0 = plot.x + ((Math.min(0, tmin) - tmin) / (tmax - tmin)) * plot.w;
413
+ const len = (Math.abs(v) / (tmax - tmin)) * plot.w;
414
+ const yCat = plot.y + groupStep * ci + (groupStep - inner) / 2;
415
+ const ySub = stacked ? yCat : yCat + si * barW;
416
+ const left = stacked
417
+ ? plot.x + ((v >= 0 ? stackPos : stackNeg) - tmin) / (tmax - tmin) * plot.w
418
+ : x0 - (v < 0 ? len : 0);
419
+ appendExtrudedRect(g, v >= 0 ? left : left - len, ySub, len, barW, color, extrude);
420
+ }
421
+ else {
422
+ const xCat = plot.x + groupStep * ci + (groupStep - inner) / 2;
423
+ const xSub = stacked ? xCat : xCat + si * barW;
424
+ const baseY = plot.y + plot.h - ((0 - tmin) / (tmax - tmin)) * plot.h;
425
+ const h = (Math.abs(v) / (tmax - tmin)) * plot.h;
426
+ const yBar = stacked
427
+ ? plot.y + plot.h - (((v >= 0 ? stackPos : stackNeg) + (v >= 0 ? v : 0) - tmin) / (tmax - tmin)) * plot.h
428
+ : v >= 0
429
+ ? baseY - h
430
+ : baseY;
431
+ appendExtrudedRect(g, xSub, yBar, barW, h, color, extrude);
432
+ }
433
+ if (v >= 0)
434
+ stackPos += v;
435
+ else
436
+ stackNeg += v;
437
+ }
438
+ }
439
+ }
440
+ function drawLine(g, def, plot, shared) {
441
+ const cats = def.categories ?? [];
442
+ let yMin = 0;
443
+ let yMax = 1;
444
+ for (const s of def.series) {
445
+ for (const v of s.values) {
446
+ if (v > yMax)
447
+ yMax = v;
448
+ if (v < yMin)
449
+ yMin = v;
450
+ }
451
+ }
452
+ ({ yMin, yMax } = (0, chart_geom_1.applyValueAxisScale)(yMin, yMax, def));
453
+ const ticks = shared?.ticks ?? (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
454
+ if (!shared?.skipAxes)
455
+ drawAxes(g, plot, ticks, cats, false);
456
+ const tmin = ticks[0];
457
+ const tmax = ticks[ticks.length - 1];
458
+ const step = (cats.length > 1 ? plot.w / (cats.length - 1) : plot.w) || plot.w;
459
+ const extrude = (0, chart_geom_1.chart3DOffset)(def);
460
+ const layers = extrude && (extrude.dx || extrude.dy) ? 5 : 1;
461
+ for (const s of def.series) {
462
+ const pts = s.values.map((v, i) => {
463
+ const xx = plot.x + step * (cats.length > 1 ? i : 0.5);
464
+ const yy = plot.y + plot.h - ((v - tmin) / (tmax - tmin)) * plot.h;
465
+ return { x: xx, y: yy };
466
+ });
467
+ if (pts.length === 0)
468
+ continue;
469
+ const color = s.color ?? "#3b82f6";
470
+ for (let layer = layers - 1; layer >= 0; layer--) {
471
+ const t = layers === 1 ? 0 : layer / (layers - 1);
472
+ const ox = extrude ? extrude.dx * t : 0;
473
+ const oy = extrude ? extrude.dy * t : 0;
474
+ const path = document.createElementNS(SVG_NS, "polyline");
475
+ path.setAttribute("points", pts.map((p) => `${p.x + ox},${p.y + oy}`).join(" "));
476
+ path.setAttribute("fill", "none");
477
+ path.setAttribute("stroke", layer === 0 ? color : (0, chart_geom_1.shadeHex)(color, 0.55 + 0.08 * (1 - t)));
478
+ path.setAttribute("stroke-width", layer === 0 ? "2" : "3");
479
+ g.appendChild(path);
480
+ }
481
+ if (def.markers) {
482
+ for (const p of pts) {
483
+ const c = document.createElementNS(SVG_NS, "circle");
484
+ c.setAttribute("cx", String(p.x));
485
+ c.setAttribute("cy", String(p.y));
486
+ c.setAttribute("r", "3");
487
+ c.setAttribute("fill", color);
488
+ g.appendChild(c);
489
+ }
490
+ }
491
+ }
492
+ }
493
+ function drawPie(g, def, plot) {
494
+ const s0 = def.series[0];
495
+ if (!s0)
496
+ return;
497
+ const total = s0.values.reduce((a, b) => a + Math.abs(b), 0);
498
+ if (total === 0)
499
+ return;
500
+ const cx0 = plot.x + plot.w / 2;
501
+ const cy0 = plot.y + plot.h / 2;
502
+ const r = Math.min(plot.w, plot.h) / 2 - 4;
503
+ const innerR = def.type === "doughnut" ? r * (def.holeRatio ?? 0.5) : 0;
504
+ const palette = s0.perSliceColors ?? def.colors ?? [];
505
+ const explode = ((s0.explosion ?? 0) / 100) * r;
506
+ const off = (0, chart_geom_1.chart3DOffset)(def);
507
+ const layers = off && (off.dx || off.dy) ? 6 : 1;
508
+ for (let layer = layers - 1; layer >= 0; layer--) {
509
+ const t = layers === 1 ? 0 : layer / (layers - 1);
510
+ const ox = off ? off.dx * t : 0;
511
+ const oy = off ? off.dy * t : 0;
512
+ const shade = layer === 0 ? 1 : 0.55 + 0.08 * (1 - t);
513
+ let start = -Math.PI / 2 + ((def.startAngle ?? 0) * Math.PI) / 180;
514
+ for (let i = 0; i < s0.values.length; i++) {
515
+ const v = Math.abs(s0.values[i]);
516
+ const frac = v / total;
517
+ if (frac <= 0)
518
+ continue;
519
+ const end = start + frac * Math.PI * 2;
520
+ const mid = (start + end) / 2;
521
+ const cx = cx0 + ox + Math.cos(mid) * explode;
522
+ const cy = cy0 + oy + Math.sin(mid) * explode;
523
+ const color = palette[i] ?? s0.color ?? "#3b82f6";
524
+ const path = document.createElementNS(SVG_NS, "path");
525
+ path.setAttribute("d", pieSlicePath(cx, cy, r, innerR, start, end));
526
+ path.setAttribute("fill", shade === 1 ? color : (0, chart_geom_1.shadeHex)(color, shade));
527
+ g.appendChild(path);
528
+ start = end;
529
+ }
530
+ }
531
+ }
532
+ function pieSlicePath(cx, cy, r, ir, a0, a1) {
533
+ const large = a1 - a0 > Math.PI ? 1 : 0;
534
+ const x0 = cx + Math.cos(a0) * r;
535
+ const y0 = cy + Math.sin(a0) * r;
536
+ const x1 = cx + Math.cos(a1) * r;
537
+ const y1 = cy + Math.sin(a1) * r;
538
+ if (ir <= 0) {
539
+ return `M ${cx} ${cy} L ${x0} ${y0} A ${r} ${r} 0 ${large} 1 ${x1} ${y1} Z`;
540
+ }
541
+ const ix0 = cx + Math.cos(a0) * ir;
542
+ const iy0 = cy + Math.sin(a0) * ir;
543
+ const ix1 = cx + Math.cos(a1) * ir;
544
+ const iy1 = cy + Math.sin(a1) * ir;
545
+ return `M ${x0} ${y0} A ${r} ${r} 0 ${large} 1 ${x1} ${y1} L ${ix1} ${iy1} A ${ir} ${ir} 0 ${large} 0 ${ix0} ${iy0} Z`;
546
+ }
547
+ function drawArea(g, def, plot, shared) {
548
+ const cats = def.categories ?? [];
549
+ let yMin = 0;
550
+ let yMax = 1;
551
+ for (const s of def.series)
552
+ for (const v of s.values) {
553
+ if (v > yMax)
554
+ yMax = v;
555
+ if (v < yMin)
556
+ yMin = v;
557
+ }
558
+ ({ yMin, yMax } = (0, chart_geom_1.applyValueAxisScale)(yMin, yMax, def));
559
+ const ticks = shared?.ticks ?? (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
560
+ if (!shared?.skipAxes)
561
+ drawAxes(g, plot, ticks, cats, false);
562
+ const tmin = ticks[0];
563
+ const tmax = ticks[ticks.length - 1];
564
+ const step = (cats.length > 1 ? plot.w / (cats.length - 1) : plot.w);
565
+ const baseY = plot.y + plot.h - ((0 - tmin) / (tmax - tmin)) * plot.h;
566
+ const extrude = (0, chart_geom_1.chart3DOffset)(def);
567
+ const layers = extrude && (extrude.dx || extrude.dy) ? 5 : 1;
568
+ for (const s of def.series) {
569
+ const pts = s.values.map((v, i) => {
570
+ const xx = plot.x + step * i;
571
+ const yy = plot.y + plot.h - ((v - tmin) / (tmax - tmin)) * plot.h;
572
+ return [xx, yy];
573
+ });
574
+ if (pts.length < 2)
575
+ continue;
576
+ const color = s.color ?? "#3b82f6";
577
+ for (let layer = layers - 1; layer >= 0; layer--) {
578
+ const t = layers === 1 ? 0 : layer / (layers - 1);
579
+ const ox = extrude ? extrude.dx * t : 0;
580
+ const oy = extrude ? extrude.dy * t : 0;
581
+ const d = [
582
+ `M ${pts[0][0] + ox} ${baseY + oy}`,
583
+ ...pts.map((p) => `L ${p[0] + ox} ${p[1] + oy}`),
584
+ `L ${pts[pts.length - 1][0] + ox} ${baseY + oy}`,
585
+ "Z",
586
+ ].join(" ");
587
+ const path = document.createElementNS(SVG_NS, "path");
588
+ path.setAttribute("d", d);
589
+ path.setAttribute("fill", layer === 0 ? color : (0, chart_geom_1.shadeHex)(color, 0.55 + 0.08 * (1 - t)));
590
+ path.setAttribute("fill-opacity", layer === 0 ? "0.5" : "0.35");
591
+ if (layer === 0) {
592
+ path.setAttribute("stroke", color);
593
+ path.setAttribute("stroke-width", "1.5");
594
+ }
595
+ g.appendChild(path);
596
+ }
597
+ }
598
+ }
599
+ function drawScatter(g, def, plot) {
600
+ let xMin = Infinity;
601
+ let xMax = -Infinity;
602
+ let yMin = Infinity;
603
+ let yMax = -Infinity;
604
+ for (const series of def.points) {
605
+ for (const p of series) {
606
+ if (p.x < xMin)
607
+ xMin = p.x;
608
+ if (p.x > xMax)
609
+ xMax = p.x;
610
+ if (p.y < yMin)
611
+ yMin = p.y;
612
+ if (p.y > yMax)
613
+ yMax = p.y;
614
+ }
615
+ }
616
+ if (!Number.isFinite(xMin))
617
+ return;
618
+ const ticks = (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
619
+ drawAxes(g, plot, ticks, [], false);
620
+ const tmin = ticks[0];
621
+ const tmax = ticks[ticks.length - 1];
622
+ for (let si = 0; si < def.points.length; si++) {
623
+ const pts = def.points[si];
624
+ const color = def.series[si]?.color ?? "#3b82f6";
625
+ for (const p of pts) {
626
+ const cx = plot.x + ((p.x - xMin) / (xMax - xMin || 1)) * plot.w;
627
+ const cy = plot.y + plot.h - ((p.y - tmin) / (tmax - tmin)) * plot.h;
628
+ const c = document.createElementNS(SVG_NS, "circle");
629
+ c.setAttribute("cx", String(cx));
630
+ c.setAttribute("cy", String(cy));
631
+ c.setAttribute("r", "3");
632
+ c.setAttribute("fill", color);
633
+ g.appendChild(c);
634
+ }
635
+ }
636
+ }
637
+ function hasCombo(def) {
638
+ return def.series.some((s) => s.chartType && s.chartType !== def.type);
639
+ }
640
+ function drawCombo(g, def, plot) {
641
+ const cats = def.categories ?? [];
642
+ let yMin = 0;
643
+ let yMax = 1;
644
+ for (const s of def.series) {
645
+ for (const v of s.values) {
646
+ if (v > yMax)
647
+ yMax = v;
648
+ if (v < yMin)
649
+ yMin = v;
650
+ }
651
+ if (s.chartType === "scatter" || s.chartType === "bubble") {
652
+ for (const p of (0, chart_geom_1.seriesXyPoints)(def, s)) {
653
+ if (p.y > yMax)
654
+ yMax = p.y;
655
+ if (p.y < yMin)
656
+ yMin = p.y;
657
+ }
658
+ }
659
+ }
660
+ ({ yMin, yMax } = (0, chart_geom_1.applyValueAxisScale)(yMin, yMax, def));
661
+ const ticks = (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
662
+ drawAxes(g, plot, ticks, cats, def.type === "bar");
663
+ const groups = new Map();
664
+ for (const s of def.series) {
665
+ const ty = s.chartType ?? def.type;
666
+ const list = groups.get(ty) ?? [];
667
+ list.push(s);
668
+ groups.set(ty, list);
669
+ }
670
+ const order = ["area", "bar", "column", "stock", "radar", "scatter", "bubble", "line"];
671
+ const keys = [...groups.keys()].sort((a, b) => order.indexOf(a) - order.indexOf(b));
672
+ const shared = { ticks, skipAxes: true };
673
+ for (const ty of keys) {
674
+ const series = groups.get(ty);
675
+ if (ty === "bar" || ty === "column") {
676
+ drawBar(g, { ...def, type: ty, series }, plot, shared);
677
+ }
678
+ else if (ty === "area" || ty === "surface") {
679
+ drawArea(g, { ...def, type: "area", series }, plot, shared);
680
+ }
681
+ else if (ty === "scatter" || ty === "bubble") {
682
+ drawComboXy(g, def, series, plot, ticks, ty === "bubble");
683
+ }
684
+ else {
685
+ drawLine(g, { ...def, type: "line", series }, plot, shared);
686
+ }
687
+ }
688
+ overlayGuides(g, def, plot, ticks);
689
+ }
690
+ function svgLine(g, x1, y1, x2, y2, stroke, width = 1) {
691
+ const el = document.createElementNS(SVG_NS, "line");
692
+ el.setAttribute("x1", String(x1));
693
+ el.setAttribute("y1", String(y1));
694
+ el.setAttribute("x2", String(x2));
695
+ el.setAttribute("y2", String(y2));
696
+ el.setAttribute("stroke", stroke);
697
+ el.setAttribute("stroke-width", String(width));
698
+ g.appendChild(el);
699
+ }
700
+ function drawRadar(g, def, plot) {
701
+ const cats = def.categories ?? [];
702
+ const n = Math.max(cats.length, 1);
703
+ const cx = plot.x + plot.w / 2;
704
+ const cy = plot.y + plot.h / 2;
705
+ const r = Math.min(plot.w, plot.h) / 2 - 8;
706
+ let max = 1;
707
+ for (const s of def.series)
708
+ for (const v of s.values)
709
+ if (v > max)
710
+ max = v;
711
+ for (let ring = 1; ring <= 4; ring++) {
712
+ const pts = [];
713
+ for (let i = 0; i < n; i++) {
714
+ const a = -Math.PI / 2 + (i / n) * Math.PI * 2;
715
+ const rr = (r * ring) / 4;
716
+ pts.push(`${cx + Math.cos(a) * rr},${cy + Math.sin(a) * rr}`);
717
+ }
718
+ const poly = document.createElementNS(SVG_NS, "polygon");
719
+ poly.setAttribute("points", pts.join(" "));
720
+ poly.setAttribute("fill", "none");
721
+ poly.setAttribute("stroke", ts.grid);
722
+ g.appendChild(poly);
723
+ }
724
+ for (let i = 0; i < n; i++) {
725
+ const a = -Math.PI / 2 + (i / n) * Math.PI * 2;
726
+ svgLine(g, cx, cy, cx + Math.cos(a) * r, cy + Math.sin(a) * r, ts.grid);
727
+ if (cats[i]) {
728
+ const t = document.createElementNS(SVG_NS, "text");
729
+ t.setAttribute("x", String(cx + Math.cos(a) * (r + 12)));
730
+ t.setAttribute("y", String(cy + Math.sin(a) * (r + 12) + 3));
731
+ t.setAttribute("text-anchor", "middle");
732
+ t.setAttribute("font-size", String(ts.size));
733
+ t.setAttribute("fill", ts.muted);
734
+ t.textContent = cats[i];
735
+ g.appendChild(t);
736
+ }
737
+ }
738
+ const filled = def.radarStyle === "filled";
739
+ for (const s of def.series) {
740
+ const pts = [];
741
+ for (let i = 0; i < n; i++) {
742
+ const a = -Math.PI / 2 + (i / n) * Math.PI * 2;
743
+ const rr = r * ((s.values[i] ?? 0) / max);
744
+ pts.push(`${cx + Math.cos(a) * rr},${cy + Math.sin(a) * rr}`);
745
+ }
746
+ const poly = document.createElementNS(SVG_NS, "polygon");
747
+ poly.setAttribute("points", pts.join(" "));
748
+ poly.setAttribute("fill", filled ? (s.color ?? "#3b82f6") : "none");
749
+ if (filled)
750
+ poly.setAttribute("fill-opacity", "0.25");
751
+ poly.setAttribute("stroke", s.color ?? "#3b82f6");
752
+ poly.setAttribute("stroke-width", "2");
753
+ g.appendChild(poly);
754
+ if (def.radarStyle !== "standard") {
755
+ for (const pt of pts) {
756
+ const [px, py] = pt.split(",").map(Number);
757
+ const c = document.createElementNS(SVG_NS, "circle");
758
+ c.setAttribute("cx", String(px));
759
+ c.setAttribute("cy", String(py));
760
+ c.setAttribute("r", "3");
761
+ c.setAttribute("fill", s.color ?? "#3b82f6");
762
+ g.appendChild(c);
763
+ }
764
+ }
765
+ }
766
+ }
767
+ function drawBubble(g, def, plot) {
768
+ let xMin = Infinity;
769
+ let xMax = -Infinity;
770
+ let yMin = Infinity;
771
+ let yMax = -Infinity;
772
+ let zMax = 1;
773
+ for (const series of def.points) {
774
+ for (const p of series) {
775
+ if (p.x < xMin)
776
+ xMin = p.x;
777
+ if (p.x > xMax)
778
+ xMax = p.x;
779
+ if (p.y < yMin)
780
+ yMin = p.y;
781
+ if (p.y > yMax)
782
+ yMax = p.y;
783
+ if ((p.z ?? 1) > zMax)
784
+ zMax = p.z ?? 1;
785
+ }
786
+ }
787
+ if (!Number.isFinite(xMin))
788
+ return;
789
+ const ticks = (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
790
+ drawAxes(g, plot, ticks, [], false);
791
+ const tmin = ticks[0];
792
+ const tmax = ticks[ticks.length - 1];
793
+ const rMax = Math.min(plot.w, plot.h) * 0.08;
794
+ for (let si = 0; si < def.points.length; si++) {
795
+ const color = def.series[si]?.color ?? "#3b82f6";
796
+ for (const p of def.points[si]) {
797
+ const cx = plot.x + ((p.x - xMin) / (xMax - xMin || 1)) * plot.w;
798
+ const cy = plot.y + plot.h - ((p.y - tmin) / (tmax - tmin)) * plot.h;
799
+ const rr = Math.max(3, rMax * Math.sqrt((p.z ?? 1) / zMax));
800
+ const c = document.createElementNS(SVG_NS, "circle");
801
+ c.setAttribute("cx", String(cx));
802
+ c.setAttribute("cy", String(cy));
803
+ c.setAttribute("r", String(rr));
804
+ c.setAttribute("fill", color);
805
+ c.setAttribute("fill-opacity", "0.55");
806
+ g.appendChild(c);
807
+ }
808
+ }
809
+ }
810
+ function drawStock(g, def, plot) {
811
+ const cats = def.categories ?? [];
812
+ const n = def.series.length;
813
+ if (n < 3) {
814
+ drawLine(g, { ...def, type: "line" }, plot);
815
+ return;
816
+ }
817
+ const high = def.series[n >= 4 ? n - 3 : 0];
818
+ const low = def.series[n >= 4 ? n - 2 : 1];
819
+ const close = def.series[n >= 4 ? n - 1 : 2];
820
+ const open = n >= 4 ? def.series[n - 4] : undefined;
821
+ let yMin = 0;
822
+ let yMax = 1;
823
+ for (const s of [high, low, close, open]) {
824
+ if (!s)
825
+ continue;
826
+ for (const v of s.values) {
827
+ if (v > yMax)
828
+ yMax = v;
829
+ if (v < yMin)
830
+ yMin = v;
831
+ }
832
+ }
833
+ const ticks = (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
834
+ drawAxes(g, plot, ticks, cats, false);
835
+ const tmin = ticks[0];
836
+ const tmax = ticks[ticks.length - 1];
837
+ const step = plot.w / Math.max(cats.length, 1);
838
+ const yOf = (v) => plot.y + plot.h - ((v - tmin) / (tmax - tmin)) * plot.h;
839
+ for (let i = 0; i < cats.length; i++) {
840
+ const x = plot.x + step * (i + 0.5);
841
+ svgLine(g, x, yOf(high.values[i] ?? 0), x, yOf(low.values[i] ?? 0), ts.text, 1.25);
842
+ svgLine(g, x, yOf(close.values[i] ?? 0), x + step * 0.18, yOf(close.values[i] ?? 0), ts.text, 1.25);
843
+ if (open) {
844
+ const ov = open.values[i] ?? 0;
845
+ svgLine(g, x - step * 0.18, yOf(ov), x, yOf(ov), ts.text, 1.25);
846
+ }
847
+ }
848
+ }
849
+ function drawLegend(g, def, area) {
850
+ const items = [];
851
+ if (def.type === "pie" || def.type === "doughnut") {
852
+ const s0 = def.series[0];
853
+ if (s0) {
854
+ const palette = s0.perSliceColors ?? def.colors ?? [];
855
+ (def.categories ?? []).forEach((c, i) => {
856
+ items.push({ name: c || `Item ${i + 1}`, color: palette[i] ?? s0.color ?? "#3b82f6" });
857
+ });
858
+ }
859
+ }
860
+ else {
861
+ for (const s of def.series)
862
+ items.push({ name: s.name ?? "Series", color: s.color ?? "#3b82f6" });
863
+ }
864
+ let x = area.x + 12;
865
+ const y = area.y + 6;
866
+ for (const it of items) {
867
+ const sw = document.createElementNS(SVG_NS, "rect");
868
+ sw.setAttribute("x", String(x));
869
+ sw.setAttribute("y", String(y));
870
+ sw.setAttribute("width", "10");
871
+ sw.setAttribute("height", "10");
872
+ sw.setAttribute("fill", it.color);
873
+ g.appendChild(sw);
874
+ const t = document.createElementNS(SVG_NS, "text");
875
+ t.setAttribute("x", String(x + 14));
876
+ t.setAttribute("y", String(y + 9));
877
+ t.setAttribute("font-size", String(ts.size));
878
+ t.setAttribute("fill", ts.muted);
879
+ t.textContent = it.name;
880
+ g.appendChild(t);
881
+ x += (0, chart_geom_1.estimateTextWidth)(it.name, ts.size) + 22 + 8;
882
+ }
883
+ }
884
+ function drawSurface(g, def, plot) {
885
+ for (const q of (0, chart_geom_1.surfaceIsoQuads)(def, plot)) {
886
+ if (q.pts.length < 3)
887
+ continue;
888
+ const poly = document.createElementNS(SVG_NS, "polygon");
889
+ poly.setAttribute("points", q.pts.map((p) => `${p.x},${p.y}`).join(" "));
890
+ if (q.stroke) {
891
+ poly.setAttribute("fill", "none");
892
+ poly.setAttribute("stroke", q.color);
893
+ poly.setAttribute("stroke-width", "1");
894
+ }
895
+ else {
896
+ poly.setAttribute("fill", q.color);
897
+ poly.setAttribute("fill-opacity", "0.72");
898
+ poly.setAttribute("stroke", "rgba(255,255,255,0.45)");
899
+ poly.setAttribute("stroke-width", "0.6");
900
+ }
901
+ g.appendChild(poly);
902
+ }
903
+ }
904
+ function drawComboXy(g, def, series, plot, ticks, bubble) {
905
+ const cats = def.categories ?? [];
906
+ const tmin = ticks[0];
907
+ const tmax = ticks[ticks.length - 1];
908
+ const xsAll = series.flatMap((s) => (0, chart_geom_1.seriesXyPoints)(def, s).map((p) => p.x));
909
+ for (const s of series) {
910
+ const pts = (0, chart_geom_1.seriesXyPoints)(def, s);
911
+ for (const p of pts) {
912
+ const cx = (0, chart_geom_1.comboXyPlotX)(p.x, cats, plot, xsAll);
913
+ const cy = plot.y + plot.h - ((p.y - tmin) / (tmax - tmin || 1)) * plot.h;
914
+ const r = bubble ? Math.max(3, Math.min(16, (p.z ?? 1) * 1.8)) : 3.5;
915
+ const c = document.createElementNS(SVG_NS, "circle");
916
+ c.setAttribute("cx", String(cx));
917
+ c.setAttribute("cy", String(cy));
918
+ c.setAttribute("r", String(r));
919
+ c.setAttribute("fill", s.color ?? "#3b82f6");
920
+ if (bubble)
921
+ c.setAttribute("fill-opacity", "0.7");
922
+ g.appendChild(c);
923
+ }
924
+ }
925
+ }
926
+ function overlayGuides(g, def, plot, ticksIn) {
927
+ if (def.type === "pie" ||
928
+ def.type === "doughnut" ||
929
+ def.type === "surface" ||
930
+ def.type === "radar" ||
931
+ def.type === "sunburst" ||
932
+ def.type === "treemap" ||
933
+ def.type === "funnel" ||
934
+ def.type === "region") {
935
+ return;
936
+ }
937
+ const cats = def.categories ?? [];
938
+ let yMin = 0;
939
+ let yMax = 1;
940
+ for (const s of def.series) {
941
+ for (const v of s.values) {
942
+ if (v > yMax)
943
+ yMax = v;
944
+ if (v < yMin)
945
+ yMin = v;
946
+ }
947
+ if (s.errorBars?.val) {
948
+ for (const v of s.values) {
949
+ if (v + s.errorBars.val > yMax)
950
+ yMax = v + s.errorBars.val;
951
+ if (v - s.errorBars.val < yMin)
952
+ yMin = v - s.errorBars.val;
953
+ }
954
+ }
955
+ }
956
+ ({ yMin, yMax } = (0, chart_geom_1.applyValueAxisScale)(yMin, yMax, def));
957
+ const ticks = ticksIn ?? (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
958
+ const tmin = ticks[0];
959
+ const tmax = ticks[ticks.length - 1];
960
+ const yOf = (v) => plot.y + plot.h - ((v - tmin) / (tmax - tmin || 1)) * plot.h;
961
+ const step = cats.length > 1 ? plot.w / (cats.length - 1) : plot.w;
962
+ for (const s of def.series) {
963
+ const ty = s.chartType ?? def.type;
964
+ const color = s.color ?? ts.muted;
965
+ if (ty === "scatter" || ty === "bubble") {
966
+ const pts = (0, chart_geom_1.seriesXyPoints)(def, s);
967
+ if (s.trendline) {
968
+ const fit = (0, chart_geom_1.linearFit)(pts.map((p) => p.x), pts.map((p) => p.y));
969
+ if (fit && pts.length >= 2) {
970
+ const xs = pts.map((p) => p.x);
971
+ const x0 = Math.min(...xs);
972
+ const x1 = Math.max(...xs);
973
+ const xAt = (xv) => (0, chart_geom_1.comboXyPlotX)(xv, cats, plot, xs);
974
+ const line = document.createElementNS(SVG_NS, "line");
975
+ line.setAttribute("x1", String(xAt(x0)));
976
+ line.setAttribute("y1", String(yOf(fit.a + fit.b * x0)));
977
+ line.setAttribute("x2", String(xAt(x1)));
978
+ line.setAttribute("y2", String(yOf(fit.a + fit.b * x1)));
979
+ line.setAttribute("stroke", color);
980
+ line.setAttribute("stroke-width", "1.25");
981
+ line.setAttribute("stroke-dasharray", "5 3");
982
+ g.appendChild(line);
983
+ }
984
+ }
985
+ continue;
986
+ }
987
+ if (s.errorBars) {
988
+ const half = s.errorBars.val ?? 0;
989
+ s.values.forEach((v, i) => {
990
+ const xx = plot.x + step * i;
991
+ svgLine(g, xx, yOf(v - half), xx, yOf(v + half), color, 1);
992
+ svgLine(g, xx - 3, yOf(v - half), xx + 3, yOf(v - half), color, 1);
993
+ svgLine(g, xx - 3, yOf(v + half), xx + 3, yOf(v + half), color, 1);
994
+ });
995
+ }
996
+ if (s.trendline) {
997
+ const xs = s.values.map((_, i) => i);
998
+ const fit = (0, chart_geom_1.linearFit)(xs, s.values);
999
+ if (fit && xs.length >= 2) {
1000
+ const line = document.createElementNS(SVG_NS, "line");
1001
+ line.setAttribute("x1", String(plot.x));
1002
+ line.setAttribute("y1", String(yOf(fit.a)));
1003
+ line.setAttribute("x2", String(plot.x + step * (s.values.length - 1)));
1004
+ line.setAttribute("y2", String(yOf(fit.a + fit.b * (s.values.length - 1))));
1005
+ line.setAttribute("stroke", color);
1006
+ line.setAttribute("stroke-width", "1.25");
1007
+ line.setAttribute("stroke-dasharray", "5 3");
1008
+ g.appendChild(line);
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+ function overlayDataLabels(g, def, plot) {
1014
+ const add = (x, y, text, anchor = "middle", baseline = "bottom") => {
1015
+ const t = document.createElementNS(SVG_NS, "text");
1016
+ t.setAttribute("x", String(x));
1017
+ // SVG text is placed by its alphabetic baseline; shift for the other two.
1018
+ t.setAttribute("y", String(baseline === "middle" ? y + ts.size * 0.35 : baseline === "top" ? y + ts.size : y));
1019
+ t.setAttribute("text-anchor", anchor);
1020
+ t.setAttribute("font-size", String(ts.size));
1021
+ t.setAttribute("fill", ts.text);
1022
+ t.textContent = text;
1023
+ g.appendChild(t);
1024
+ };
1025
+ // Clustered / stacked bars: label each bar at its own free end, in the bar renderer's geometry.
1026
+ if ((def.type === "column" || def.type === "bar") && !hasCombo(def)) {
1027
+ const geo = (0, chart_geom_1.barGeometry)(def, plot);
1028
+ const cats = def.categories ?? [];
1029
+ for (let ci = 0; ci < cats.length; ci++) {
1030
+ let stackPos = 0;
1031
+ let stackNeg = 0;
1032
+ def.series.forEach((s, si) => {
1033
+ let v = s.values[ci] ?? 0;
1034
+ if (def.percentStacked)
1035
+ v = (geo.totals[ci] ?? 0) > 0 ? v / geo.totals[ci] : 0;
1036
+ const label = (0, chart_geom_1.pointLabelText)(def, s, ci, s.values[ci] ?? 0);
1037
+ if (label) {
1038
+ const a = (0, chart_geom_1.barLabelAnchor)(geo, plot, si, ci, v, v >= 0 ? stackPos : stackNeg);
1039
+ add(a.x, a.y, label, a.anchor, a.baseline);
1040
+ }
1041
+ if (v >= 0)
1042
+ stackPos += v;
1043
+ else
1044
+ stackNeg += v;
1045
+ });
1046
+ }
1047
+ return;
1048
+ }
1049
+ if (def.type === "pie" || def.type === "doughnut") {
1050
+ const s0 = def.series[0];
1051
+ if (!s0)
1052
+ return;
1053
+ const total = s0.values.reduce((a, b) => a + Math.abs(b), 0);
1054
+ if (!total)
1055
+ return;
1056
+ const cx0 = plot.x + plot.w / 2;
1057
+ const cy0 = plot.y + plot.h / 2;
1058
+ const r = Math.min(plot.w, plot.h) / 2 - 4;
1059
+ let start = -Math.PI / 2 + ((def.startAngle ?? 0) * Math.PI) / 180;
1060
+ for (let i = 0; i < s0.values.length; i++) {
1061
+ const v = Math.abs(s0.values[i]);
1062
+ const frac = v / total;
1063
+ if (frac <= 0)
1064
+ continue;
1065
+ const end = start + frac * Math.PI * 2;
1066
+ const mid = (start + end) / 2;
1067
+ const label = (0, chart_geom_1.pointLabelText)(def, s0, i, s0.values[i]);
1068
+ if (label)
1069
+ add(cx0 + Math.cos(mid) * r * 0.62, cy0 + Math.sin(mid) * r * 0.62, label);
1070
+ start = end;
1071
+ }
1072
+ return;
1073
+ }
1074
+ const cats = def.categories ?? [];
1075
+ const step = cats.length > 1 ? plot.w / (cats.length - 1) : plot.w;
1076
+ let yMin = 0;
1077
+ let yMax = 1;
1078
+ for (const s of def.series)
1079
+ for (const v of s.values) {
1080
+ if (v > yMax)
1081
+ yMax = v;
1082
+ if (v < yMin)
1083
+ yMin = v;
1084
+ }
1085
+ ({ yMin, yMax } = (0, chart_geom_1.applyValueAxisScale)(yMin, yMax, def));
1086
+ const ticks = (0, chart_geom_1.niceTicks)(yMin, yMax, 5);
1087
+ const tmin = ticks[0];
1088
+ const tmax = ticks[ticks.length - 1];
1089
+ const yOf = (v) => plot.y + plot.h - ((v - tmin) / (tmax - tmin || 1)) * plot.h;
1090
+ for (const s of def.series) {
1091
+ const ty = s.chartType ?? def.type;
1092
+ if (ty === "scatter" || ty === "bubble") {
1093
+ const pts = (0, chart_geom_1.seriesXyPoints)(def, s);
1094
+ const xs = pts.map((p) => p.x);
1095
+ pts.forEach((p, i) => {
1096
+ const label = (0, chart_geom_1.pointLabelText)(def, s, i, p.y);
1097
+ if (label)
1098
+ add((0, chart_geom_1.comboXyPlotX)(p.x, cats, plot, xs), yOf(p.y) - 4, label);
1099
+ });
1100
+ continue;
1101
+ }
1102
+ s.values.forEach((v, i) => {
1103
+ const label = (0, chart_geom_1.pointLabelText)(def, s, i, v);
1104
+ if (!label)
1105
+ return;
1106
+ const xx = plot.x + step * (cats.length > 1 ? i : 0.5);
1107
+ add(xx, yOf(v) - 3, label);
1108
+ });
1109
+ }
1110
+ }