chartai 0.0.11 → 1.0.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 (78) hide show
  1. package/dist/chart-library.d.ts +33 -146
  2. package/dist/chart-library.d.ts.map +1 -1
  3. package/dist/chart-library.js +378 -321
  4. package/dist/chart-library.min.js +1 -1
  5. package/dist/charts/area.d.ts +6 -0
  6. package/dist/charts/area.d.ts.map +1 -0
  7. package/dist/charts/area.js +65 -0
  8. package/dist/charts/area.min.js +1 -0
  9. package/dist/charts/bar.d.ts +11 -0
  10. package/dist/charts/bar.d.ts.map +1 -0
  11. package/dist/charts/bar.js +65 -0
  12. package/dist/charts/bar.min.js +1 -0
  13. package/dist/charts/boids.js +167 -0
  14. package/dist/charts/boids.min.js +18 -0
  15. package/dist/charts/candlestick.d.ts +21 -0
  16. package/dist/charts/candlestick.d.ts.map +1 -0
  17. package/dist/charts/candlestick.js +132 -0
  18. package/dist/charts/candlestick.min.js +32 -0
  19. package/dist/charts/line.d.ts +12 -0
  20. package/dist/charts/line.d.ts.map +1 -0
  21. package/dist/charts/line.js +62 -0
  22. package/dist/charts/line.min.js +1 -0
  23. package/dist/charts/scatter.d.ts +11 -0
  24. package/dist/charts/scatter.d.ts.map +1 -0
  25. package/dist/charts/scatter.js +46 -0
  26. package/dist/charts/scatter.min.js +1 -0
  27. package/dist/chunk-0jepamv9.js +7 -0
  28. package/dist/chunk-1p45ex5n.min.js +2 -0
  29. package/dist/chunk-831dem4f.js +4 -0
  30. package/dist/chunk-93yrr7er.js +35 -0
  31. package/dist/chunk-94kc81rr.min.js +2 -0
  32. package/dist/chunk-a27be8p9.js +105 -0
  33. package/dist/chunk-bfyv7z27.min.js +2 -0
  34. package/dist/chunk-dmaxrg6s.min.js +2 -0
  35. package/dist/chunk-e7d3zgw5.min.js +2 -0
  36. package/dist/chunk-g6m56ptf.js +609 -0
  37. package/dist/chunk-m17t3vjq.js +9 -0
  38. package/dist/chunk-me3qaz3m.min.js +2 -0
  39. package/dist/chunk-qr6mweck.min.js +2 -0
  40. package/dist/chunk-yabjrff2.js +11 -0
  41. package/dist/gpu-worker.js +625 -686
  42. package/dist/gpu-worker.min.js +1 -1
  43. package/dist/msg.d.ts +33 -0
  44. package/dist/msg.d.ts.map +1 -0
  45. package/dist/plugins/coords.d.ts +18 -0
  46. package/dist/plugins/coords.d.ts.map +1 -0
  47. package/dist/plugins/hover.d.ts +15 -2
  48. package/dist/plugins/hover.d.ts.map +1 -1
  49. package/dist/plugins/hover.js +103 -16
  50. package/dist/plugins/hover.min.js +1 -1
  51. package/dist/plugins/labels-panel.d.ts +4 -0
  52. package/dist/plugins/labels-panel.d.ts.map +1 -0
  53. package/dist/plugins/labels-panel.js +122 -0
  54. package/dist/plugins/labels-panel.min.js +1 -0
  55. package/dist/plugins/labels.d.ts +17 -2
  56. package/dist/plugins/labels.d.ts.map +1 -1
  57. package/dist/plugins/labels.js +11 -99
  58. package/dist/plugins/labels.min.js +1 -1
  59. package/dist/plugins/legend.d.ts +16 -0
  60. package/dist/plugins/legend.d.ts.map +1 -0
  61. package/dist/plugins/legend.js +282 -0
  62. package/dist/plugins/legend.min.js +21 -0
  63. package/dist/plugins/shared.d.ts +7 -0
  64. package/dist/plugins/shared.d.ts.map +1 -0
  65. package/dist/plugins/zoom.d.ts +10 -2
  66. package/dist/plugins/zoom.d.ts.map +1 -1
  67. package/dist/plugins/zoom.js +227 -97
  68. package/dist/plugins/zoom.min.js +1 -1
  69. package/dist/types.d.ts +179 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +0 -0
  72. package/dist/types.min.js +0 -0
  73. package/dist/worker-inline.d.ts +1 -1
  74. package/dist/worker-inline.d.ts.map +1 -1
  75. package/package.json +13 -12
  76. package/readme.md +51 -42
  77. package/dist/chunk-bgfkgcmg.js +0 -25
  78. package/dist/chunk-cj3zanvs.min.js +0 -2
@@ -0,0 +1,609 @@
1
+ import {
2
+ M
3
+ } from "./chunk-93yrr7er.js";
4
+ import {
5
+ __require
6
+ } from "./chunk-m17t3vjq.js";
7
+
8
+ // src/chart-library.ts
9
+ class Chart {
10
+ id;
11
+ _mgr;
12
+ constructor(id, mgr) {
13
+ this.id = id;
14
+ this._mgr = mgr;
15
+ }
16
+ get _c() {
17
+ return this._mgr["charts"].get(this.id);
18
+ }
19
+ setData(series) {
20
+ this._mgr.updateSeries(this.id, series);
21
+ }
22
+ configure(patch) {
23
+ const c = this._c;
24
+ if (!c)
25
+ return;
26
+ Object.assign(c.config, patch);
27
+ const uniformNames = new Set((c.renderer.uniforms ?? []).map((u) => u.name));
28
+ const workerValues = {};
29
+ for (const key of Object.keys(patch)) {
30
+ const val = patch[key];
31
+ if (typeof val === "number" && uniformNames.has(key)) {
32
+ workerValues[key] = val;
33
+ }
34
+ }
35
+ if (Object.keys(workerValues).length > 0) {
36
+ Object.assign(c.customUniforms, workerValues);
37
+ this._mgr["worker"]?.postMessage({
38
+ type: M.SET_UNIFORMS,
39
+ id: this.id,
40
+ values: workerValues
41
+ });
42
+ }
43
+ if ("bgColor" in patch && patch.bgColor !== undefined) {
44
+ const [r, g, b] = patch.bgColor;
45
+ const wrap = c.el.querySelector("div");
46
+ if (wrap)
47
+ wrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
48
+ this._mgr["worker"]?.postMessage({
49
+ type: M.SET_STYLE,
50
+ id: this.id,
51
+ bgColor: patch.bgColor
52
+ });
53
+ }
54
+ this._mgr.requestRender(this.id);
55
+ this._mgr.drawChart(c);
56
+ }
57
+ addPlugin(plugin) {
58
+ const c = this._c;
59
+ if (!c || c.plugins.some((p) => p.name === plugin.name))
60
+ return;
61
+ const wrap = c.el.querySelector("div");
62
+ plugin.install?.(c, wrap);
63
+ c.plugins.push(plugin);
64
+ this._mgr.drawChart(c);
65
+ }
66
+ removePlugin(name) {
67
+ const c = this._c;
68
+ if (!c)
69
+ return;
70
+ const idx = c.plugins.findIndex((p) => p.name === name);
71
+ if (idx >= 0) {
72
+ c.plugins[idx].uninstall?.(c);
73
+ c.plugins.splice(idx, 1);
74
+ this._mgr.drawChart(c);
75
+ }
76
+ }
77
+ hasPlugin(name) {
78
+ return this._c?.plugins.some((p) => p.name === name) ?? false;
79
+ }
80
+ resetView() {
81
+ this._mgr.resetView(this.id);
82
+ }
83
+ destroy() {
84
+ this._mgr.destroy(this.id);
85
+ }
86
+ }
87
+ var _colorEl = null;
88
+ function parseColor(c) {
89
+ if (typeof c !== "string")
90
+ return c;
91
+ if (!_colorEl) {
92
+ _colorEl = document.createElement("i");
93
+ _colorEl.style.cssText = "display:none";
94
+ document.body.appendChild(_colorEl);
95
+ }
96
+ _colorEl.style.color = c;
97
+ const m = getComputedStyle(_colorEl).color.match(/\d+/g);
98
+ return { r: +m[0] / 255, g: +m[1] / 255, b: +m[2] / 255 };
99
+ }
100
+ function resizeCanvas(canvas, cssW, cssH) {
101
+ const dpr = devicePixelRatio || 1;
102
+ canvas.width = Math.round(cssW * dpr);
103
+ canvas.height = Math.round(cssH * dpr);
104
+ if (canvas instanceof HTMLCanvasElement) {
105
+ canvas.style.width = `${cssW}px`;
106
+ canvas.style.height = `${cssH}px`;
107
+ }
108
+ }
109
+
110
+ class _ChartManager {
111
+ static instance = null;
112
+ worker = null;
113
+ charts = new Map;
114
+ renderers = new Map;
115
+ uiPlugins = [];
116
+ pendingRenderers = [];
117
+ chartIdCounter = 0;
118
+ _isDark = false;
119
+ _syncViews = false;
120
+ statsCallbacks = [];
121
+ currentStats = {
122
+ fps: 0,
123
+ renderMs: 0,
124
+ total: 0,
125
+ active: 0
126
+ };
127
+ visibilityObserver;
128
+ resizeObserver;
129
+ constructor() {
130
+ this._isDark = document.documentElement.classList.contains("dark");
131
+ this.visibilityObserver = new IntersectionObserver((entries) => {
132
+ for (const e of entries) {
133
+ const id = e.target.dataset.chartId;
134
+ if (!id)
135
+ continue;
136
+ const chart = this.charts.get(id);
137
+ if (!chart)
138
+ continue;
139
+ chart.visible = e.isIntersecting;
140
+ this.worker?.postMessage({
141
+ type: M.SET_VISIBILITY,
142
+ id,
143
+ visible: e.isIntersecting
144
+ });
145
+ if (e.isIntersecting)
146
+ this.drawChart(chart);
147
+ }
148
+ }, { threshold: 0.01 });
149
+ this.resizeObserver = new ResizeObserver((entries) => {
150
+ for (const e of entries) {
151
+ const id = e.target.dataset.chartId;
152
+ if (!id)
153
+ continue;
154
+ const chart = this.charts.get(id);
155
+ if (!chart)
156
+ continue;
157
+ const { width, height } = e.contentRect;
158
+ if (width <= 0 || height <= 0)
159
+ continue;
160
+ chart.width = width;
161
+ chart.height = height;
162
+ const dpr = devicePixelRatio || 1;
163
+ resizeCanvas(chart.backCanvas, width, height);
164
+ resizeCanvas(chart.frontCanvas, width, height);
165
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(chart.renderer, chart);
166
+ this.worker?.postMessage({
167
+ type: M.RESIZE,
168
+ id,
169
+ width: Math.round(width * dpr),
170
+ height: Math.round(height * dpr),
171
+ bufferSizes,
172
+ perSeriesPassMeta
173
+ });
174
+ this.drawChart(chart);
175
+ }
176
+ });
177
+ }
178
+ static getInstance() {
179
+ if (!_ChartManager.instance)
180
+ _ChartManager.instance = new _ChartManager;
181
+ return _ChartManager.instance;
182
+ }
183
+ get isDark() {
184
+ return this._isDark;
185
+ }
186
+ get syncViews() {
187
+ return this._syncViews;
188
+ }
189
+ use(plugin) {
190
+ if ("passes" in plugin) {
191
+ const r = plugin;
192
+ this.renderers.set(r.name, r);
193
+ if (this.worker)
194
+ this.sendRendererRegistration(r);
195
+ else
196
+ this.pendingRenderers.push(r);
197
+ } else {
198
+ const p = plugin;
199
+ if (!this.uiPlugins.some((x) => x.name === p.name))
200
+ this.uiPlugins.push(p);
201
+ }
202
+ }
203
+ async init() {
204
+ if (this.worker)
205
+ return true;
206
+ return new Promise((resolve) => {
207
+ import("./worker-inline.js").then(({ WORKER_CODE }) => {
208
+ const blob = new Blob([WORKER_CODE], {
209
+ type: "application/javascript"
210
+ });
211
+ this.worker = new Worker(URL.createObjectURL(blob), {
212
+ type: "module"
213
+ });
214
+ this.setupWorkerHandlers(resolve);
215
+ }).catch(() => {
216
+ this.worker = new Worker(new URL("./gpu-worker.js", import.meta.url), { type: "module" });
217
+ this.setupWorkerHandlers(resolve);
218
+ });
219
+ });
220
+ }
221
+ setupWorkerHandlers(resolve) {
222
+ if (!this.worker)
223
+ return;
224
+ this.worker.onmessage = (e) => {
225
+ const { type, ...data } = e.data;
226
+ switch (type) {
227
+ case M.GPU_READY:
228
+ for (const r of this.pendingRenderers)
229
+ this.sendRendererRegistration(r);
230
+ this.pendingRenderers = [];
231
+ resolve(true);
232
+ break;
233
+ case M.ERROR:
234
+ console.error("chartai:", data.code);
235
+ resolve(false);
236
+ break;
237
+ case M.STATS:
238
+ this.currentStats = {
239
+ fps: data.fps,
240
+ renderMs: data.renderMs,
241
+ total: data.totalCharts,
242
+ active: data.activeCharts
243
+ };
244
+ for (const cb of this.statsCallbacks)
245
+ cb(this.currentStats);
246
+ break;
247
+ }
248
+ };
249
+ this.worker.onerror = (e) => {
250
+ console.error("chartai:", e);
251
+ resolve(false);
252
+ };
253
+ this.worker.postMessage({ type: M.INIT, isDark: this._isDark });
254
+ }
255
+ sendRendererRegistration(renderer) {
256
+ const bufferDefs = (renderer.buffers ?? []).map((buf) => ({
257
+ name: buf.name,
258
+ usages: buf.usages,
259
+ perSeries: renderer.passes.some((p) => p.perSeries !== false && p.bindings.some((b) => b.source === buf.name))
260
+ }));
261
+ this.worker?.postMessage({
262
+ type: M.REGISTER_RENDERER,
263
+ name: renderer.name,
264
+ shaders: renderer.shaders,
265
+ passes: renderer.passes.map((p) => ({
266
+ type: p.type,
267
+ shader: p.shader,
268
+ bindings: p.bindings,
269
+ perSeries: p.perSeries !== false,
270
+ topology: p.topology,
271
+ loadOp: p.loadOp,
272
+ blend: p.blend
273
+ })),
274
+ bufferDefs,
275
+ uniformDefs: renderer.uniforms ?? []
276
+ });
277
+ }
278
+ computeRendererMeta(renderer, chart) {
279
+ const bufferSizes = {};
280
+ const perSeriesPassMeta = [];
281
+ const series = chart.series.length > 0 ? chart.series : [
282
+ {
283
+ rawX: [],
284
+ rawY: [],
285
+ extra: {},
286
+ label: "",
287
+ color: { r: 0, g: 0, b: 0 }
288
+ }
289
+ ];
290
+ const dpr = devicePixelRatio || 1;
291
+ const physW = Math.round(chart.width * dpr);
292
+ const physH = Math.round(chart.height * dpr);
293
+ for (const s of series) {
294
+ const ctx = {
295
+ width: physW,
296
+ height: physH,
297
+ samples: s.rawX.length,
298
+ seriesCount: series.length,
299
+ bounds: chart.bounds,
300
+ view: chart.view
301
+ };
302
+ for (const buf of renderer.buffers ?? []) {
303
+ const size = buf.bytes(ctx);
304
+ bufferSizes[buf.name] = Math.max(bufferSizes[buf.name] ?? 0, size);
305
+ }
306
+ perSeriesPassMeta.push(renderer.passes.map((p) => ({
307
+ dispatch: p.dispatch?.(ctx),
308
+ draw: p.draw?.(ctx)
309
+ })));
310
+ }
311
+ return { bufferSizes, perSeriesPassMeta };
312
+ }
313
+ create(config) {
314
+ if (!this.worker)
315
+ throw new Error("Call init() before create().");
316
+ const renderer = this.renderers.get(config.type);
317
+ if (!renderer)
318
+ throw new Error(`No renderer "${config.type}". Call manager.use() with a matching RendererPlugin first.`);
319
+ const id = `chart-${++this.chartIdCounter}`;
320
+ const el = document.createElement("div");
321
+ el.dataset.chartId = id;
322
+ el.style.cssText = "width:100%;height:100%;position:relative;";
323
+ const wrap = document.createElement("div");
324
+ wrap.dataset.chartId = id;
325
+ wrap.style.cssText = "width:100%;height:100%;position:relative;";
326
+ const mkCanvas = (z, events) => {
327
+ const c = document.createElement("canvas");
328
+ c.style.cssText = `position:absolute;inset:0;width:100%;height:100%;pointer-events:${events};z-index:${z};`;
329
+ return c;
330
+ };
331
+ const backCanvas = mkCanvas(0, "none");
332
+ const gpuCanvas = mkCanvas(1, "auto");
333
+ const frontCanvas = mkCanvas(2, "none");
334
+ wrap.append(backCanvas, gpuCanvas, frontCanvas);
335
+ el.appendChild(wrap);
336
+ config.container.appendChild(el);
337
+ let offscreen;
338
+ try {
339
+ offscreen = gpuCanvas.transferControlToOffscreen();
340
+ } catch (e) {
341
+ throw new Error(`Failed to acquire OffscreenCanvas: ${e}`);
342
+ }
343
+ const rect = wrap.getBoundingClientRect();
344
+ const cssW = rect.width || 400;
345
+ const cssH = rect.height || 200;
346
+ resizeCanvas(offscreen, cssW, cssH);
347
+ resizeCanvas(backCanvas, cssW, cssH);
348
+ resizeCanvas(frontCanvas, cssW, cssH);
349
+ if (config.bgColor) {
350
+ const [r, g, b] = config.bgColor;
351
+ wrap.style.background = `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`;
352
+ }
353
+ const customUniforms = {};
354
+ for (const u of renderer.uniforms ?? []) {
355
+ const v = config[u.name];
356
+ customUniforms[u.name] = typeof v === "number" ? v : u.default;
357
+ }
358
+ const chart = {
359
+ id,
360
+ config,
361
+ el,
362
+ backCanvas,
363
+ frontCanvas,
364
+ width: cssW,
365
+ height: cssH,
366
+ series: [],
367
+ bounds: { minX: 0, maxX: 1, minY: 0, maxY: 1 },
368
+ view: { panX: 0, panY: 0, zoomX: 1, zoomY: 1 },
369
+ visible: true,
370
+ dragging: false,
371
+ plugins: [...this.uiPlugins],
372
+ renderer,
373
+ customUniforms
374
+ };
375
+ this.charts.set(id, chart);
376
+ const dpr = devicePixelRatio || 1;
377
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(renderer, chart);
378
+ this.worker.postMessage({
379
+ type: M.REGISTER_CHART,
380
+ id,
381
+ canvas: offscreen,
382
+ rendererName: config.type,
383
+ bgColor: config.bgColor ?? null,
384
+ bufferSizes,
385
+ perSeriesPassMeta,
386
+ customUniformValues: customUniforms,
387
+ width: Math.round(cssW * dpr),
388
+ height: Math.round(cssH * dpr)
389
+ }, [offscreen]);
390
+ this.visibilityObserver.observe(el);
391
+ this.resizeObserver.observe(wrap);
392
+ for (const plugin of chart.plugins)
393
+ plugin.install?.(chart, wrap);
394
+ renderer.install?.(chart, wrap);
395
+ this.updateSeries(id, config.series);
396
+ return new Chart(id, this);
397
+ }
398
+ destroy(id) {
399
+ const chart = this.charts.get(id);
400
+ if (!chart)
401
+ return;
402
+ chart.renderer.uninstall?.(chart);
403
+ for (const p of chart.plugins)
404
+ p.uninstall?.(chart);
405
+ this.visibilityObserver.unobserve(chart.el);
406
+ const wrap = chart.el.querySelector("div");
407
+ if (wrap)
408
+ this.resizeObserver.unobserve(wrap);
409
+ chart.el.remove();
410
+ this.worker?.postMessage({ type: M.UNREGISTER_CHART, id });
411
+ this.charts.delete(id);
412
+ }
413
+ updateSeries(id, series) {
414
+ const chart = this.charts.get(id);
415
+ if (!chart || !this.worker || series.length === 0)
416
+ return;
417
+ chart.series = series.map((s) => {
418
+ const n = s.x.length;
419
+ const color = parseColor(s.color);
420
+ if (n === 0)
421
+ return { label: s.label, color, rawX: [], rawY: [], extra: {} };
422
+ const idx = Array.from({ length: n }, (_, i) => i).sort((a, b) => s.x[a] - s.x[b]);
423
+ const extra = {};
424
+ for (const key in s) {
425
+ if (key !== "label" && key !== "color" && key !== "x" && key !== "y" && Array.isArray(s[key])) {
426
+ extra[key] = idx.map((i) => s[key][i]);
427
+ }
428
+ }
429
+ return {
430
+ label: s.label,
431
+ color,
432
+ rawX: idx.map((i) => s.x[i]),
433
+ rawY: idx.map((i) => s.y[i]),
434
+ extra
435
+ };
436
+ });
437
+ const customBounds = chart.renderer.computeBounds?.(chart.series);
438
+ let { minX, maxX, minY, maxY } = customBounds ?? (() => {
439
+ let minX2 = Infinity, maxX2 = -Infinity, minY2 = Infinity, maxY2 = -Infinity;
440
+ for (const s of chart.series) {
441
+ for (let i = 0;i < s.rawX.length; i++) {
442
+ if (s.rawX[i] < minX2)
443
+ minX2 = s.rawX[i];
444
+ if (s.rawX[i] > maxX2)
445
+ maxX2 = s.rawX[i];
446
+ if (s.rawY[i] < minY2)
447
+ minY2 = s.rawY[i];
448
+ if (s.rawY[i] > maxY2)
449
+ maxY2 = s.rawY[i];
450
+ }
451
+ }
452
+ const px = (maxX2 - minX2) * 0.05 || 1;
453
+ const py = (maxY2 - minY2) * 0.1 || 1;
454
+ return {
455
+ minX: minX2 - px,
456
+ maxX: maxX2 + px,
457
+ minY: minY2 - py,
458
+ maxY: maxY2 + py
459
+ };
460
+ })();
461
+ const db = chart.config.defaultBounds;
462
+ if (db) {
463
+ if (db.minX !== undefined)
464
+ minX = db.minX;
465
+ if (db.maxX !== undefined)
466
+ maxX = db.maxX;
467
+ if (db.minY !== undefined)
468
+ minY = db.minY;
469
+ if (db.maxY !== undefined)
470
+ maxY = db.maxY;
471
+ }
472
+ chart.bounds = { minX, maxX, minY, maxY };
473
+ const { bufferSizes, perSeriesPassMeta } = this.computeRendererMeta(chart.renderer, chart);
474
+ const seriesData = chart.series.map((s) => {
475
+ const extra = {};
476
+ for (const key in s.extra)
477
+ extra[key] = new Float32Array(s.extra[key]);
478
+ return {
479
+ label: s.label,
480
+ colorR: s.color.r,
481
+ colorG: s.color.g,
482
+ colorB: s.color.b,
483
+ dataX: new Float32Array(s.rawX),
484
+ dataY: new Float32Array(s.rawY),
485
+ extra
486
+ };
487
+ });
488
+ const transferables = seriesData.flatMap((s) => [
489
+ s.dataX.buffer,
490
+ s.dataY.buffer,
491
+ ...Object.values(s.extra).map((a) => a.buffer)
492
+ ]);
493
+ this.worker.postMessage({
494
+ type: M.UPDATE_SERIES,
495
+ id,
496
+ series: seriesData,
497
+ bounds: chart.bounds,
498
+ bufferSizes,
499
+ perSeriesPassMeta
500
+ }, transferables);
501
+ this.sendViewTransform(chart);
502
+ this.drawChart(chart);
503
+ }
504
+ setSyncViews(sync) {
505
+ this._syncViews = sync;
506
+ }
507
+ setTheme(dark) {
508
+ this._isDark = dark;
509
+ this.worker?.postMessage({ type: M.THEME, isDark: dark });
510
+ for (const chart of this.charts.values())
511
+ this.drawChart(chart);
512
+ }
513
+ onStats(callback) {
514
+ this.statsCallbacks.push(callback);
515
+ return () => {
516
+ const idx = this.statsCallbacks.indexOf(callback);
517
+ if (idx >= 0)
518
+ this.statsCallbacks.splice(idx, 1);
519
+ };
520
+ }
521
+ getStats() {
522
+ return { ...this.currentStats };
523
+ }
524
+ resetView(id) {
525
+ const chart = this.charts.get(id);
526
+ if (!chart)
527
+ return;
528
+ for (const p of chart.plugins)
529
+ p.resetView?.(chart);
530
+ const { panX: spx, panY: spy, zoomX: szx, zoomY: szy } = chart.view;
531
+ const t0 = performance.now();
532
+ const animate = () => {
533
+ const t = Math.min(1, (performance.now() - t0) / 300);
534
+ const e = 1 - Math.pow(1 - t, 3);
535
+ chart.view.panX = spx * (1 - e);
536
+ chart.view.panY = spy * (1 - e);
537
+ chart.view.zoomX = szx + (1 - szx) * e;
538
+ chart.view.zoomY = szy + (1 - szy) * e;
539
+ this.sendViewTransform(chart);
540
+ this.drawChart(chart);
541
+ if (this._syncViews)
542
+ this.syncAllViews(chart);
543
+ if (t < 1)
544
+ requestAnimationFrame(animate);
545
+ };
546
+ requestAnimationFrame(animate);
547
+ }
548
+ requestRender(id) {
549
+ const chart = this.charts.get(id);
550
+ if (chart)
551
+ this.sendViewTransform(chart);
552
+ }
553
+ sendViewTransform(chart) {
554
+ this.worker?.postMessage({
555
+ type: M.VIEW_TRANSFORM,
556
+ id: chart.id,
557
+ panX: chart.view.panX,
558
+ panY: chart.view.panY,
559
+ zoomX: chart.view.zoomX,
560
+ zoomY: chart.view.zoomY
561
+ });
562
+ }
563
+ syncAllViews(source) {
564
+ const transforms = [];
565
+ for (const chart of this.charts.values()) {
566
+ if (chart.id !== source.id) {
567
+ chart.view = { ...source.view };
568
+ transforms.push({ id: chart.id });
569
+ this.drawChart(chart);
570
+ }
571
+ }
572
+ if (transforms.length > 0) {
573
+ this.worker?.postMessage({
574
+ type: M.BATCH_VIEW_TRANSFORM,
575
+ panX: source.view.panX,
576
+ panY: source.view.panY,
577
+ zoomX: source.view.zoomX,
578
+ zoomY: source.view.zoomY,
579
+ transforms
580
+ });
581
+ }
582
+ }
583
+ drawChart(chart) {
584
+ if (!chart.visible)
585
+ return;
586
+ const dpr = devicePixelRatio || 1;
587
+ const backCtx = chart.backCanvas.getContext("2d");
588
+ if (backCtx) {
589
+ backCtx.clearRect(0, 0, chart.backCanvas.width, chart.backCanvas.height);
590
+ backCtx.save();
591
+ backCtx.scale(dpr, dpr);
592
+ for (const p of chart.plugins)
593
+ p.beforeDraw?.(backCtx, chart);
594
+ backCtx.restore();
595
+ }
596
+ const ctx = chart.frontCanvas.getContext("2d");
597
+ if (ctx) {
598
+ ctx.clearRect(0, 0, chart.frontCanvas.width, chart.frontCanvas.height);
599
+ ctx.save();
600
+ ctx.scale(dpr, dpr);
601
+ for (const p of chart.plugins)
602
+ p.afterDraw?.(ctx, chart);
603
+ ctx.restore();
604
+ }
605
+ }
606
+ }
607
+ var ChartManager = _ChartManager.getInstance();
608
+
609
+ export { Chart, ChartManager };
@@ -0,0 +1,9 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined")
5
+ return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ export { __require };
@@ -0,0 +1,2 @@
1
+ var d=((a)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});
2
+ export{d as o};
@@ -0,0 +1,2 @@
1
+ import{j as T}from"./chunk-e7d3zgw5.js";import{l as p}from"./chunk-94kc81rr.js";var D='-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',F=12,c=(t,m,s)=>{let o=m-t;if(o<=0)return[t];let e=o/s,n=10**Math.floor(Math.log10(e)),a=e/n,r=n*(a<=1.5?1:a<=3?2:a<=7?5:10),l=[];for(let g=Math.ceil(t/r)*r;g<=m;g+=r)l.push(g);return l},M=(t)=>{let{width:m,height:s}=t,o=T,{bounds:e,view:n}=t,a=e.maxX-e.minX,r=e.maxY-e.minY,l=a/n.zoomX,g=r/n.zoomY,u=e.minX+n.panX*a,i=e.minY+n.panY*r,h=t.config.bgColor??(p.isDark?[0.11,0.11,0.12]:[0.98,0.98,0.98]);return{w:m,h:s,m:o,rx:l,ry:g,mx:u,my:i,bg:`${Math.round(h[0]*255)},${Math.round(h[1]*255)},${Math.round(h[2]*255)}`,font:t.config.fontFamily??D,text:t.config.textColor??(p.isDark?"#c0c0c0":"#333333"),grid:t.config.gridColor??(p.isDark?"rgba(255,255,255,0.06)":"rgba(0,0,0,0.06)")}},A={name:"labels",beforeDraw(t,m){let{w:s,h:o,m:e,rx:n,ry:a,mx:r,my:l,grid:g}=M(m);t.strokeStyle=g,t.lineWidth=1,t.beginPath(),c(l,l+a,7).forEach((u)=>{let i=o*(1-(u-l)/a);if(i>5&&i<o-e.bottom-5)t.moveTo(e.left,i),t.lineTo(s,i)}),c(r,r+n,8).forEach((u)=>{let i=s*((u-r)/n);if(i>e.left&&i<s)t.moveTo(i,0),t.lineTo(i,o-e.bottom)}),t.stroke()},afterDraw(t,m){let{w:s,h:o,m:e,rx:n,ry:a,mx:r,my:l,bg:g,font:u,text:i}=M(m),{formatX:h=String,formatY:E=String,labelSize:k=F}=m.config,d=(b,f,C,y,S)=>{let L=b==="left"?t.createLinearGradient(f,0,f+y,0):t.createLinearGradient(0,C,0,C+S),w=b==="left"?[1,0.7,0.2,0.05,0]:[0,0.05,0.2,0.7,1];[0,0.35,0.55,0.7,1].forEach((X,Y)=>L.addColorStop(X,`rgba(${g},${w[Y]})`)),t.fillStyle=L,t.fillRect(f,C,y,S)};d("left",0,0,e.left+20,o),d("bottom",0,o-e.bottom-20,s,e.bottom+20),t.font=`${k}px ${u}`,t.fillStyle=i,t.textAlign="right",t.textBaseline="middle",c(l,l+a,7).forEach((b)=>{let f=o*(1-(b-l)/a);if(f>5&&f<o-e.bottom-5)t.fillText(E(b),e.left-5,f)}),t.textAlign="right",t.textBaseline="top",c(r,r+n,8).forEach((b)=>{let f=s*((b-r)/n);if(f<e.left-10||f>s+30)return;t.save(),t.translate(f,o-e.bottom+5),t.rotate(-Math.PI/14),t.fillText(h(b),0,0),t.restore()})}};
2
+ export{D as g,F as h,A as i};
@@ -0,0 +1,11 @@
1
+ import {
2
+ BINARY_SEARCH,
3
+ COMPUTE_WG,
4
+ UNIFORM_STRUCT
5
+ } from "./chunk-0jepamv9.js";
6
+
7
+ // src/shaders/line.ts
8
+ var LINE_COMPUTE_SHADER = `${UNIFORM_STRUCT}struct LineUniforms{maxSamplesPerPixel: u32,_p1: u32,_p2: u32,_p3: u32};struct LineData{screenX: f32,minScreenY: f32,maxScreenY: f32,valid: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> dataX: array<f32>;@group(0)@binding(2)var<storage,read> dataY: array<f32>;@group(0)@binding(3)var<storage,read_write> lineData: array<LineData>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;@group(0)@binding(5)var<uniform> lu: LineUniforms;${BINARY_SEARCH}@compute @workgroup_size(${COMPUTE_WG})fn main(@builtin(global_invocation_id)id: vec3u){let outputIdx = id.x;let maxCols = u32(u.width);let count = u.pointCount;if(outputIdx >= maxCols || count == 0u){if(outputIdx < maxCols){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);}return;}let viewRangeX = u.viewMaxX - u.viewMinX;let viewRangeY = u.viewMaxY - u.viewMinY;if(viewRangeX < 0.0001 || viewRangeY < 0.0001){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}let relPx = f32(outputIdx);let pixelMinX = u.viewMinX +(relPx / u.width)* viewRangeX;let pixelMaxX = u.viewMinX +((relPx + 1.0)/ u.width)* viewRangeX;if(pixelMaxX < u.dataMinX || pixelMinX > u.dataMaxX){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}let startIdx = lowerBound(pixelMinX,count);var endIdx = lowerBound(pixelMaxX,count);endIdx = min(endIdx,count);let centerX =(pixelMinX + pixelMaxX)* 0.5;if(startIdx >= endIdx){var bestIdx = startIdx;if(startIdx > 0u && startIdx < count){let distPrev = abs(dataX[startIdx - 1u] - centerX);let distCurr = abs(dataX[startIdx] - centerX);if(distPrev < distCurr){bestIdx = startIdx - 1u;}}else if(startIdx >= count && count > 0u){bestIdx = count - 1u;}if(bestIdx >= count){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}let y = dataY[bestIdx];let normY =(y - u.viewMinY)/ viewRangeY;let screenY = 1.0 - normY;let normX =(dataX[bestIdx] - u.viewMinX)/ viewRangeX;let screenX = normX;lineData[outputIdx] = LineData(screenX,screenY,screenY,1.0);return;}var dataMinY = dataY[startIdx];var dataMaxY = dataY[startIdx];let rangeCount = endIdx - startIdx;let maxSamples = lu.maxSamplesPerPixel;if(maxSamples > 1u && rangeCount > maxSamples){let stride = f32(rangeCount - 1u)/ f32(maxSamples - 1u);for(var s = 0u;s < maxSamples;s++){let idx = startIdx + u32(f32(s)* stride);if(idx < endIdx){let y = dataY[idx];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let lastY = dataY[endIdx - 1u];dataMinY = min(dataMinY,lastY);dataMaxY = max(dataMaxY,lastY);}else{for(var i = startIdx + 1u;i < endIdx;i++){let y = dataY[i];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let normX =(centerX - u.viewMinX)/ viewRangeX;let screenX = normX;let normMaxY =(dataMaxY - u.viewMinY)/ viewRangeY;let normMinY =(dataMinY - u.viewMinY)/ viewRangeY;let minScreenY = 1.0 - normMaxY;let maxScreenY = 1.0 - normMinY;lineData[outputIdx] = LineData(screenX,minScreenY,maxScreenY,1.0);}`;
9
+ var LINE_RENDER_SHADER = `${UNIFORM_STRUCT}struct LineData{screenX: f32,minScreenY: f32,maxScreenY: f32,valid: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> lineData: array<LineData>;@group(0)@binding(2)var<storage,read> allSeries: array<SeriesInfo>;struct VertexOutput{@builtin(position)pos: vec4f,@location(0)alpha: f32,@location(1)@interpolate(flat)seriesIdx: u32,};@vertex fn vs(@builtin(vertex_index)vi: u32,@builtin(instance_index)series_idx: u32)-> VertexOutput{var out: VertexOutput;out.seriesIdx = series_idx;let maxCols = u32(u.width);let segIdx = vi / 2u;let endpoint = vi % 2u;if(segIdx < maxCols){let d = lineData[segIdx];let y = select(d.maxScreenY,d.minScreenY,endpoint == 0u);out.pos = vec4f(d.screenX * 2.0 - 1.0,1.0 - y * 2.0,0.0,d.valid);out.alpha = d.valid;}else{let connIdx = segIdx - maxCols;if(connIdx + 1u >= maxCols){out.pos = vec4f(0.0,0.0,0.0,0.0);out.alpha = 0.0;return out;}let d0 = lineData[connIdx];let d1 = lineData[connIdx + 1u];let segValid = min(d0.valid,d1.valid);if(endpoint == 0u){let midY =(d0.minScreenY + d0.maxScreenY)* 0.5;out.pos = vec4f(d0.screenX * 2.0 - 1.0,1.0 - midY * 2.0,0.0,segValid);}else{let midY =(d1.minScreenY + d1.maxScreenY)* 0.5;out.pos = vec4f(d1.screenX * 2.0 - 1.0,1.0 - midY * 2.0,0.0,segValid);}out.alpha = segValid;}return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{if(in.alpha < 0.1){discard;}let series = allSeries[in.seriesIdx];return vec4f(series.color.rgb,1.0);}`;
10
+
11
+ export { LINE_COMPUTE_SHADER, LINE_RENDER_SHADER };