velo-plot 1.11.1 → 1.15.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 (118) hide show
  1. package/README.md +35 -2
  2. package/dist/{index.core-CYu3tydL.js → ChartCore-BimVvUEg.js} +1936 -1588
  3. package/dist/ChartCore-BimVvUEg.js.map +1 -0
  4. package/dist/axisFormat-B7o_uIEA.js +127 -0
  5. package/dist/axisFormat-B7o_uIEA.js.map +1 -0
  6. package/dist/core/OverlayRenderer.d.ts +1 -2
  7. package/dist/core/chart/ChartCore.d.ts +18 -1
  8. package/dist/core/chart/ChartRenderLoop.d.ts +4 -0
  9. package/dist/core/chart/ChartScaling.d.ts +11 -0
  10. package/dist/core/chart/NavigationUtils.d.ts +12 -0
  11. package/dist/core/chart/exporter/SVGExporter.d.ts +7 -2
  12. package/dist/core/chart/types.d.ts +13 -1
  13. package/dist/core/format/axisFormat.d.ts +16 -0
  14. package/dist/core/indicator/buildIndicatorPane.d.ts +22 -0
  15. package/dist/core/indicator/buildIndicatorSeries.d.ts +14 -0
  16. package/dist/core/indicator/index.d.ts +3 -0
  17. package/dist/core/indicator/types.d.ts +74 -0
  18. package/dist/core/render/pixelSnap.d.ts +8 -0
  19. package/dist/core/series/SeriesBounds.d.ts +1 -1
  20. package/dist/core/stacked/createStackedChart.d.ts +3 -0
  21. package/dist/core/stacked/index.d.ts +6 -0
  22. package/dist/core/stacked/paneAxis.d.ts +20 -0
  23. package/dist/core/stacked/paneResize.d.ts +29 -0
  24. package/dist/core/stacked/stackExport.d.ts +18 -0
  25. package/dist/core/stacked/types.d.ts +120 -0
  26. package/dist/core/sync/index.d.ts +25 -70
  27. package/dist/createStackedChart-CjsKcOvX.js +1088 -0
  28. package/dist/createStackedChart-CjsKcOvX.js.map +1 -0
  29. package/dist/index-CUGS98p4.js +277 -0
  30. package/dist/index-CUGS98p4.js.map +1 -0
  31. package/dist/index-ChgIdk3N.js +229 -0
  32. package/dist/index-ChgIdk3N.js.map +1 -0
  33. package/dist/{index-D8ifudJH.js → index-DjeWClO9.js} +601 -561
  34. package/dist/index-DjeWClO9.js.map +1 -0
  35. package/dist/index.core-C_-CFftD.js +167 -0
  36. package/dist/index.core-C_-CFftD.js.map +1 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/math-Ddm2EZjj.js +85 -0
  39. package/dist/math-Ddm2EZjj.js.map +1 -0
  40. package/dist/plugins/analysis.js +132 -130
  41. package/dist/plugins/analysis.js.map +1 -1
  42. package/dist/plugins/anomaly-detection.d.ts +1 -0
  43. package/dist/plugins/anomaly-detection.js +221 -0
  44. package/dist/plugins/anomaly-detection.js.map +1 -0
  45. package/dist/plugins/broken-axis.d.ts +3 -0
  46. package/dist/plugins/broken-axis.js +262 -0
  47. package/dist/plugins/broken-axis.js.map +1 -0
  48. package/dist/plugins/caching.d.ts +3 -0
  49. package/dist/plugins/caching.js +182 -0
  50. package/dist/plugins/caching.js.map +1 -0
  51. package/dist/plugins/data-transform.d.ts +3 -0
  52. package/dist/plugins/data-transform.js +146 -0
  53. package/dist/plugins/data-transform.js.map +1 -0
  54. package/dist/plugins/debug.js +12 -11
  55. package/dist/plugins/debug.js.map +1 -1
  56. package/dist/plugins/forecasting.d.ts +1 -0
  57. package/dist/plugins/forecasting.js +236 -0
  58. package/dist/plugins/forecasting.js.map +1 -0
  59. package/dist/plugins/latex.d.ts +1 -0
  60. package/dist/plugins/latex.js +454 -0
  61. package/dist/plugins/latex.js.map +1 -0
  62. package/dist/plugins/lazy-load.d.ts +3 -0
  63. package/dist/plugins/lazy-load.js +189 -0
  64. package/dist/plugins/lazy-load.js.map +1 -0
  65. package/dist/plugins/ml-integration.d.ts +3 -0
  66. package/dist/plugins/ml-integration.js +355 -0
  67. package/dist/plugins/ml-integration.js.map +1 -0
  68. package/dist/plugins/offscreen.d.ts +3 -0
  69. package/dist/plugins/offscreen.js +169 -0
  70. package/dist/plugins/offscreen.js.map +1 -0
  71. package/dist/plugins/pattern-recognition.d.ts +3 -0
  72. package/dist/plugins/pattern-recognition.js +690 -0
  73. package/dist/plugins/pattern-recognition.js.map +1 -0
  74. package/dist/plugins/radar.d.ts +3 -0
  75. package/dist/plugins/radar.js +103 -0
  76. package/dist/plugins/radar.js.map +1 -0
  77. package/dist/plugins/regression.d.ts +3 -0
  78. package/dist/plugins/regression.js +634 -0
  79. package/dist/plugins/regression.js.map +1 -0
  80. package/dist/plugins/roi.d.ts +1 -0
  81. package/dist/plugins/roi.js +223 -0
  82. package/dist/plugins/roi.js.map +1 -0
  83. package/dist/plugins/snapshot/types.d.ts +1 -1
  84. package/dist/plugins/snapshot.d.ts +3 -0
  85. package/dist/plugins/snapshot.js +90 -0
  86. package/dist/plugins/snapshot.js.map +1 -0
  87. package/dist/plugins/sync/index.d.ts +2 -1
  88. package/dist/plugins/sync.js +12 -244
  89. package/dist/plugins/sync.js.map +1 -1
  90. package/dist/plugins/tools/tooltip/TooltipManager.d.ts +9 -1
  91. package/dist/plugins/tools/tooltip/format.d.ts +7 -0
  92. package/dist/plugins/tools/tooltip/types.d.ts +6 -8
  93. package/dist/plugins/tools.js +1 -1
  94. package/dist/plugins/video-recorder.d.ts +3 -0
  95. package/dist/plugins/video-recorder.js +104 -0
  96. package/dist/plugins/video-recorder.js.map +1 -0
  97. package/dist/plugins/virtualization.d.ts +3 -0
  98. package/dist/plugins/virtualization.js +6 -0
  99. package/dist/plugins/virtualization.js.map +1 -0
  100. package/dist/react/index.d.ts +2 -1
  101. package/dist/react/useStackedPlot.d.ts +17 -0
  102. package/dist/react.d.ts +1 -0
  103. package/dist/react.js +31 -0
  104. package/dist/react.js.map +1 -0
  105. package/dist/types.d.ts +21 -1
  106. package/dist/utils-BK5UUXxj.js +172 -0
  107. package/dist/utils-BK5UUXxj.js.map +1 -0
  108. package/dist/velo-plot.full.js +1000 -5576
  109. package/dist/velo-plot.full.js.map +1 -1
  110. package/dist/velo-plot.js +23 -22
  111. package/dist/velo-plot.js.map +1 -1
  112. package/package.json +17 -3
  113. package/dist/EventEmitter-DAbs2K1C.js +0 -60
  114. package/dist/EventEmitter-DAbs2K1C.js.map +0 -1
  115. package/dist/index-D8ifudJH.js.map +0 -1
  116. package/dist/index.core-CYu3tydL.js.map +0 -1
  117. package/dist/utils-Dbro9-qM.js +0 -254
  118. package/dist/utils-Dbro9-qM.js.map +0 -1
package/dist/types.d.ts CHANGED
@@ -23,6 +23,8 @@ export interface AxisOptions {
23
23
  position?: "left" | "right" | "top" | "bottom";
24
24
  /** Scale type */
25
25
  scale?: ScaleType;
26
+ /** Tick display type — affects label formatting, not the mathematical scale */
27
+ type?: "linear" | "time";
26
28
  /** Axis label (e.g., 'E / V') */
27
29
  label?: string;
28
30
  /** Unit for formatting (e.g., 'V', 'A') */
@@ -65,8 +67,10 @@ export interface AxisOptions {
65
67
  labelFontSize?: number;
66
68
  /** Label rotation in degrees (useful for crowded X-axis) */
67
69
  labelRotation?: number;
70
+ /** Target number of tick divisions on this axis (default: 8 for X, 6 for Y) */
71
+ tickCount?: number;
68
72
  }
69
- export type SeriesType = "line" | "scatter" | "line+scatter" | "step" | "step+scatter" | "band" | "area" | "bar" | "heatmap" | "candlestick" | "polar" | "radar" | "boxplot" | "waterfall" | "gauge" | "sankey" | "ternary";
73
+ export type SeriesType = "line" | "scatter" | "line+scatter" | "step" | "step+scatter" | "band" | "area" | "bar" | "heatmap" | "candlestick" | "polar" | "radar" | "boxplot" | "waterfall" | "gauge" | "sankey" | "ternary" | "indicator";
70
74
  /** Step mode defines where the step occurs */
71
75
  export type StepMode = "before" | "after" | "center";
72
76
  /** Error bar direction */
@@ -503,6 +507,8 @@ export interface TernaryOptions extends Omit<SeriesOptions, "data" | "style"> {
503
507
  export interface ChartOptions {
504
508
  /** Target container element */
505
509
  container: HTMLDivElement;
510
+ /** Stable chart id (used by sync groups; auto-generated if omitted) */
511
+ id?: string;
506
512
  /** Renderer backend selection (default: 'webgl') */
507
513
  renderer?: "webgl" | "webgpu";
508
514
  /** X-axis configuration */
@@ -589,6 +595,20 @@ export interface ZoomOptions {
589
595
  /** Animate the transition */
590
596
  animate?: boolean;
591
597
  }
598
+ /** Options for {@link Chart.fit} — safe fit that skips empty series */
599
+ export interface FitOptions {
600
+ /** Explicit X range (otherwise derived from visible series) */
601
+ x?: Range;
602
+ /** Explicit Y range for the primary Y axis (otherwise derived per axis) */
603
+ y?: Range;
604
+ /** Padding fraction applied to computed ranges (default: 0.02 for X, 0.05 for Y) */
605
+ padding?: number | {
606
+ x?: number;
607
+ y?: number;
608
+ };
609
+ /** Animate the transition */
610
+ animate?: boolean;
611
+ }
592
612
  export interface CursorOptions {
593
613
  /** Enable cursor */
594
614
  enabled?: boolean;
@@ -0,0 +1,172 @@
1
+ const x = [
2
+ { symbol: "p", factor: 1e-12 },
3
+ { symbol: "n", factor: 1e-9 },
4
+ { symbol: "µ", factor: 1e-6 },
5
+ { symbol: "m", factor: 1e-3 },
6
+ { symbol: "", factor: 1 },
7
+ { symbol: "k", factor: 1e3 },
8
+ { symbol: "M", factor: 1e6 },
9
+ { symbol: "G", factor: 1e9 }
10
+ ];
11
+ function g(t) {
12
+ const e = Math.abs(t);
13
+ if (e === 0)
14
+ return { symbol: "", factor: 1 };
15
+ for (let n = x.length - 1; n >= 0; n--) {
16
+ const r = x[n], o = e / r.factor;
17
+ if (o >= 1 && o < 1e3)
18
+ return r;
19
+ }
20
+ return { symbol: "", factor: 1 };
21
+ }
22
+ function F(t, e, n = 2) {
23
+ const r = g(t);
24
+ return `${(t / r.factor).toFixed(n)} ${r.symbol}${e}`;
25
+ }
26
+ function w(t, e = 3) {
27
+ return Math.abs(t) < 1e-3 || Math.abs(t) >= 1e6 ? t.toExponential(e) : t.toFixed(e);
28
+ }
29
+ function I(t, e = 2) {
30
+ return t.toExponential(e);
31
+ }
32
+ function v(t, e = 1e-3) {
33
+ const n = t instanceof Array ? new Float32Array(t) : t;
34
+ if (n.length < 3) return [];
35
+ const r = [];
36
+ let o = 0, i = 1, f = Math.sign(n[1] - n[0]), l = 0;
37
+ for (let a = 2; a < n.length; a++) {
38
+ const c = Math.sign(n[a] - n[a - 1]);
39
+ if (c !== 0 && c !== f) {
40
+ if (l++, l >= 2) {
41
+ const s = n[o], h = n[a];
42
+ Math.abs(h - s) < e && (r.push({
43
+ number: i,
44
+ startIndex: o,
45
+ endIndex: a,
46
+ direction: n[o + 1] > n[o] ? 1 : -1
47
+ }), i++, o = a, l = 0);
48
+ }
49
+ f = c;
50
+ }
51
+ }
52
+ return o < n.length - 1 && r.push({
53
+ number: i,
54
+ startIndex: o,
55
+ endIndex: n.length - 1,
56
+ direction: n[o + 1] > n[o] ? 1 : -1
57
+ }), r;
58
+ }
59
+ function D(t) {
60
+ const e = [], n = 360 / Math.max(t, 1);
61
+ for (let r = 0; r < t; r++) {
62
+ const o = r * n % 360;
63
+ e.push(`hsl(${o}, 70%, 55%)`);
64
+ }
65
+ return e;
66
+ }
67
+ function S(t, e, n = {}) {
68
+ const { minProminence: r = 0, type: o = "both" } = n, i = [], f = t instanceof Array ? new Float32Array(t) : t, l = e instanceof Array ? new Float32Array(e) : e;
69
+ for (let a = 1; a < l.length - 1; a++) {
70
+ const c = l[a - 1], s = l[a], h = l[a + 1];
71
+ if (s > c && s > h) {
72
+ const y = Math.min(s - c, s - h);
73
+ y >= r && (o === "both" || o === "max") && i.push({ index: a, x: f[a], y: s, type: "max", prominence: y });
74
+ }
75
+ if (s < c && s < h) {
76
+ const y = Math.min(c - s, h - s);
77
+ y >= r && (o === "both" || o === "min") && i.push({ index: a, x: f[a], y: s, type: "min", prominence: y });
78
+ }
79
+ }
80
+ return i;
81
+ }
82
+ function k(t) {
83
+ let e = 0, n = -1;
84
+ const r = t instanceof Array ? t : Array.from(t);
85
+ for (let o = 0; o < r.length; o++)
86
+ isFinite(r[o]) || (e++, n === -1 && (n = o));
87
+ return {
88
+ valid: e === 0,
89
+ invalidCount: e,
90
+ firstInvalidIndex: n
91
+ };
92
+ }
93
+ function C(t) {
94
+ const e = t instanceof Array ? t : Array.from(t), n = e.length;
95
+ if (n === 0)
96
+ return { min: 0, max: 0, mean: 0, stdDev: 0, count: 0 };
97
+ let r = e[0], o = e[0], i = 0;
98
+ for (let c = 0; c < n; c++) {
99
+ const s = e[c];
100
+ s < r && (r = s), s > o && (o = s), i += s;
101
+ }
102
+ const f = i / n;
103
+ let l = 0;
104
+ for (let c = 0; c < n; c++) {
105
+ const s = e[c] - f;
106
+ l += s * s;
107
+ }
108
+ const a = Math.sqrt(l / n);
109
+ return { min: r, max: o, mean: f, stdDev: a, count: n };
110
+ }
111
+ function E(t, e) {
112
+ const n = t instanceof Array ? new Float32Array(t) : t, r = new Float32Array(n.length), o = Math.floor(e / 2);
113
+ for (let i = 0; i < n.length; i++) {
114
+ let f = 0, l = 0;
115
+ for (let a = Math.max(0, i - o); a <= Math.min(n.length - 1, i + o); a++)
116
+ f += n[a], l++;
117
+ r[i] = f / l;
118
+ }
119
+ return r;
120
+ }
121
+ function B(t, e, n) {
122
+ const r = t.length;
123
+ if (n >= r || n < 3)
124
+ return { x: new Float32Array(t), y: new Float32Array(e) };
125
+ const o = new Float32Array(n), i = new Float32Array(n);
126
+ o[0] = t[0], i[0] = e[0];
127
+ const f = (r - 2) / (n - 2);
128
+ let l = 0, a = 1;
129
+ for (let c = 0; c < n - 2; c++) {
130
+ const s = Math.floor((c + 1) * f) + 1, h = Math.floor((c + 2) * f) + 1, y = Math.min(Math.floor((c + 3) * f) + 1, r);
131
+ let b = 0, u = 0, d = 0;
132
+ for (let m = h; m < y; m++)
133
+ b += t[m], u += e[m], d++;
134
+ d > 0 && (b /= d, u /= d);
135
+ let p = -1, A = s;
136
+ for (let m = s; m < h && m < r; m++) {
137
+ const M = Math.abs(
138
+ (t[l] - b) * (e[m] - e[l]) - (t[l] - t[m]) * (u - e[l])
139
+ );
140
+ M > p && (p = M, A = m);
141
+ }
142
+ o[a] = t[A], i[a] = e[A], a++, l = A;
143
+ }
144
+ return o[n - 1] = t[r - 1], i[n - 1] = e[r - 1], { x: o, y: i };
145
+ }
146
+ function V(t, e, n, r) {
147
+ const o = t.length, i = new Float32Array(o);
148
+ let f = 0, l = o - 1, a = 1 / 0, c = 1 / 0;
149
+ for (let u = 0; u < o; u++) {
150
+ const d = Math.abs(t[u] - n), p = Math.abs(t[u] - r);
151
+ d < a && (a = d, f = u), p < c && (c = p, l = u);
152
+ }
153
+ const s = e[f], y = (e[l] - s) / (t[l] - t[f]), b = s - y * t[f];
154
+ for (let u = 0; u < o; u++)
155
+ i[u] = e[u] - (y * t[u] + b);
156
+ return i;
157
+ }
158
+ export {
159
+ v as a,
160
+ S as b,
161
+ C as c,
162
+ B as d,
163
+ w as e,
164
+ F as f,
165
+ D as g,
166
+ I as h,
167
+ g as i,
168
+ E as m,
169
+ V as s,
170
+ k as v
171
+ };
172
+ //# sourceMappingURL=utils-BK5UUXxj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-BK5UUXxj.js","sources":["../src/plugins/analysis/utils.ts"],"sourcesContent":["/**\n * Data Analysis Utilities\n *\n * General-purpose utilities for scientific data formatting,\n * cycle detection, peak detection, and data validation.\n */\n\n// ============================================\n// Unit Prefixes\n// ============================================\n\ntype SIPrefix = 'p' | 'n' | 'µ' | 'm' | '' | 'k' | 'M' | 'G';\n\nexport interface PrefixInfo {\n symbol: SIPrefix;\n factor: number;\n}\n\nconst SI_PREFIXES: PrefixInfo[] = [\n { symbol: 'p', factor: 1e-12 },\n { symbol: 'n', factor: 1e-9 },\n { symbol: 'µ', factor: 1e-6 },\n { symbol: 'm', factor: 1e-3 },\n { symbol: '', factor: 1 },\n { symbol: 'k', factor: 1e3 },\n { symbol: 'M', factor: 1e6 },\n { symbol: 'G', factor: 1e9 },\n];\n\n/**\n * Find the best SI prefix for a value\n */\nexport function getBestPrefix(value: number): PrefixInfo {\n const absValue = Math.abs(value);\n\n if (absValue === 0) {\n return { symbol: '', factor: 1 };\n }\n\n // Find prefix where value becomes 1-999\n for (let i = SI_PREFIXES.length - 1; i >= 0; i--) {\n const prefix = SI_PREFIXES[i];\n const scaled = absValue / prefix.factor;\n if (scaled >= 1 && scaled < 1000) {\n return prefix;\n }\n }\n\n // Default to base unit\n return { symbol: '', factor: 1 };\n}\n\n/**\n * Format a value with automatic SI prefix\n *\n * @example\n * formatWithPrefix(0.000001, 'A') // \"1.00 µA\"\n * formatWithPrefix(0.5, 'V') // \"500 mV\"\n * formatWithPrefix(1500, 'm') // \"1.50 km\"\n */\nexport function formatWithPrefix(\n value: number,\n unit: string,\n decimals = 2\n): string {\n const prefix = getBestPrefix(value);\n const scaled = value / prefix.factor;\n return `${scaled.toFixed(decimals)} ${prefix.symbol}${unit}`;\n}\n\n/**\n * Format a numeric value with specified decimals\n * Automatically switches to scientific notation for very large/small values\n */\nexport function formatValue(value: number, decimals = 3): string {\n if (Math.abs(value) < 0.001 || Math.abs(value) >= 1e6) {\n return value.toExponential(decimals);\n }\n return value.toFixed(decimals);\n}\n\n/**\n * Format value in scientific notation\n */\nexport function formatScientific(value: number, decimals = 2): string {\n return value.toExponential(decimals);\n}\n\n// ============================================\n// Cycle Detection\n// ============================================\n\nexport interface CycleInfo {\n /** Cycle number (1-indexed) */\n number: number;\n /** Start index in data array */\n startIndex: number;\n /** End index in data array */\n endIndex: number;\n /** Direction at start: 1 = forward, -1 = reverse */\n direction: 1 | -1;\n}\n\n/**\n * Detect cycles in oscillating data\n *\n * A cycle is complete when the signal returns to its starting value\n * after going through both sweep directions. Useful for:\n * - Cyclic voltammetry data\n * - Periodic signals\n * - Oscillation analysis\n *\n * @param signal - The signal data (e.g., potential, position, etc.)\n * @param tolerance - How close to starting value to consider a cycle complete\n */\nexport function detectCycles(\n signal: Float32Array | Float64Array | number[],\n tolerance = 0.001\n): CycleInfo[] {\n const data = signal instanceof Array ? new Float32Array(signal) : signal;\n \n if (data.length < 3) return [];\n\n const cycles: CycleInfo[] = [];\n let cycleStart = 0;\n let cycleNumber = 1;\n\n // Determine initial direction\n let prevDirection = Math.sign(data[1] - data[0]);\n let directionChanges = 0;\n\n for (let i = 2; i < data.length; i++) {\n const direction = Math.sign(data[i] - data[i - 1]);\n\n // Skip if no movement\n if (direction === 0) continue;\n\n // Direction change detected\n if (direction !== prevDirection) {\n directionChanges++;\n\n // After 2 direction changes, check if we're back at start\n if (directionChanges >= 2) {\n const startValue = data[cycleStart];\n const currentValue = data[i];\n\n if (Math.abs(currentValue - startValue) < tolerance) {\n cycles.push({\n number: cycleNumber,\n startIndex: cycleStart,\n endIndex: i,\n direction: data[cycleStart + 1] > data[cycleStart] ? 1 : -1,\n });\n\n cycleNumber++;\n cycleStart = i;\n directionChanges = 0;\n }\n }\n\n prevDirection = direction;\n }\n }\n\n // Handle last incomplete cycle\n if (cycleStart < data.length - 1) {\n cycles.push({\n number: cycleNumber,\n startIndex: cycleStart,\n endIndex: data.length - 1,\n direction: data[cycleStart + 1] > data[cycleStart] ? 1 : -1,\n });\n }\n\n return cycles;\n}\n\n/**\n * Generate distinct colors for cycles/series\n *\n * Uses HSL color space to generate evenly distributed hues\n */\nexport function generateCycleColors(count: number): string[] {\n const colors: string[] = [];\n const hueStep = 360 / Math.max(count, 1);\n\n for (let i = 0; i < count; i++) {\n const hue = (i * hueStep) % 360;\n colors.push(`hsl(${hue}, 70%, 55%)`);\n }\n\n return colors;\n}\n\n// ============================================\n// Peak Detection\n// ============================================\n\nexport interface Peak {\n /** Index in data array */\n index: number;\n /** X value at peak */\n x: number;\n /** Y value at peak */\n y: number;\n /** Peak type */\n type: 'max' | 'min';\n /** Prominence of the peak */\n prominence: number;\n}\n\n/**\n * Detect peaks (local maxima and minima) in data\n *\n * Uses simple local extrema detection with optional prominence filtering.\n * Useful for:\n * - Signal peak detection\n * - Finding local maxima/minima\n * - Feature extraction\n *\n * @param x - X values (independent variable)\n * @param y - Y values (dependent variable)\n * @param options - Detection options\n */\nexport function detectPeaks(\n x: Float32Array | Float64Array | number[],\n y: Float32Array | Float64Array | number[],\n options: {\n /** Minimum prominence to be considered a peak */\n minProminence?: number;\n /** Only return 'max' or 'min' peaks */\n type?: 'max' | 'min' | 'both';\n } = {}\n): Peak[] {\n const { minProminence = 0, type = 'both' } = options;\n const peaks: Peak[] = [];\n\n const xData = x instanceof Array ? new Float32Array(x) : x;\n const yData = y instanceof Array ? new Float32Array(y) : y;\n\n for (let i = 1; i < yData.length - 1; i++) {\n const prev = yData[i - 1];\n const curr = yData[i];\n const next = yData[i + 1];\n\n // Local maximum\n if (curr > prev && curr > next) {\n const prominence = Math.min(curr - prev, curr - next);\n if (prominence >= minProminence && (type === 'both' || type === 'max')) {\n peaks.push({ index: i, x: xData[i], y: curr, type: 'max', prominence });\n }\n }\n\n // Local minimum\n if (curr < prev && curr < next) {\n const prominence = Math.min(prev - curr, next - curr);\n if (prominence >= minProminence && (type === 'both' || type === 'min')) {\n peaks.push({ index: i, x: xData[i], y: curr, type: 'min', prominence });\n }\n }\n }\n\n return peaks;\n}\n\n// ============================================\n// Data Validation\n// ============================================\n\nexport interface ValidationResult {\n /** Whether all data is valid */\n valid: boolean;\n /** Number of invalid values (NaN, Infinity, etc.) */\n invalidCount: number;\n /** Index of first invalid value (-1 if all valid) */\n firstInvalidIndex: number;\n}\n\n/**\n * Validate that data contains only finite numbers\n *\n * Checks for NaN, Infinity, and -Infinity values.\n * Useful for data quality checks before rendering.\n */\nexport function validateData(\n data: Float32Array | Float64Array | number[]\n): ValidationResult {\n let invalidCount = 0;\n let firstInvalidIndex = -1;\n\n const arr = data instanceof Array ? data : Array.from(data);\n\n for (let i = 0; i < arr.length; i++) {\n if (!isFinite(arr[i])) {\n invalidCount++;\n if (firstInvalidIndex === -1) {\n firstInvalidIndex = i;\n }\n }\n }\n\n return {\n valid: invalidCount === 0,\n invalidCount,\n firstInvalidIndex,\n };\n}\n\n// ============================================\n// Data Statistics\n// ============================================\n\nexport interface DataStats {\n min: number;\n max: number;\n mean: number;\n stdDev: number;\n count: number;\n}\n\n/**\n * Calculate basic statistics for a dataset\n */\nexport function calculateStats(\n data: Float32Array | Float64Array | number[]\n): DataStats {\n const arr = data instanceof Array ? data : Array.from(data);\n const count = arr.length;\n\n if (count === 0) {\n return { min: 0, max: 0, mean: 0, stdDev: 0, count: 0 };\n }\n\n let min = arr[0];\n let max = arr[0];\n let sum = 0;\n\n for (let i = 0; i < count; i++) {\n const val = arr[i];\n if (val < min) min = val;\n if (val > max) max = val;\n sum += val;\n }\n\n const mean = sum / count;\n\n // Calculate standard deviation\n let sumSquaredDiff = 0;\n for (let i = 0; i < count; i++) {\n const diff = arr[i] - mean;\n sumSquaredDiff += diff * diff;\n }\n const stdDev = Math.sqrt(sumSquaredDiff / count);\n\n return { min, max, mean, stdDev, count };\n}\n\n// ============================================\n// Data Smoothing\n// ============================================\n\n/**\n * Apply moving average smoothing to data\n *\n * @param data - Input data array\n * @param windowSize - Number of points to average (must be odd)\n */\nexport function movingAverage(\n data: Float32Array | Float64Array | number[],\n windowSize: number\n): Float32Array {\n const arr = data instanceof Array ? new Float32Array(data) : data;\n const result = new Float32Array(arr.length);\n const halfWindow = Math.floor(windowSize / 2);\n\n for (let i = 0; i < arr.length; i++) {\n let sum = 0;\n let count = 0;\n\n for (let j = Math.max(0, i - halfWindow); j <= Math.min(arr.length - 1, i + halfWindow); j++) {\n sum += arr[j];\n count++;\n }\n\n result[i] = sum / count;\n }\n\n return result;\n}\n\n/**\n * Downsample data using LTTB (Largest Triangle Three Buckets) algorithm\n *\n * Preserves visual characteristics while reducing point count.\n * Ideal for rendering large datasets efficiently.\n *\n * @param x - X values\n * @param y - Y values\n * @param targetPoints - Desired number of output points\n */\nexport function downsampleLTTB(\n x: Float32Array | Float64Array,\n y: Float32Array | Float64Array,\n targetPoints: number\n): { x: Float32Array; y: Float32Array } {\n const dataLength = x.length;\n\n if (targetPoints >= dataLength || targetPoints < 3) {\n return { x: new Float32Array(x), y: new Float32Array(y) };\n }\n\n const sampledX = new Float32Array(targetPoints);\n const sampledY = new Float32Array(targetPoints);\n\n // Always include first point\n sampledX[0] = x[0];\n sampledY[0] = y[0];\n\n const bucketSize = (dataLength - 2) / (targetPoints - 2);\n\n let a = 0; // Previous selected point index\n let sampledIndex = 1;\n\n for (let i = 0; i < targetPoints - 2; i++) {\n // Calculate bucket range\n const bucketStart = Math.floor((i + 1) * bucketSize) + 1;\n const bucketEnd = Math.floor((i + 2) * bucketSize) + 1;\n const nextBucketEnd = Math.min(Math.floor((i + 3) * bucketSize) + 1, dataLength);\n\n // Calculate average of next bucket for comparison\n let avgX = 0;\n let avgY = 0;\n let avgCount = 0;\n\n for (let j = bucketEnd; j < nextBucketEnd; j++) {\n avgX += x[j];\n avgY += y[j];\n avgCount++;\n }\n\n if (avgCount > 0) {\n avgX /= avgCount;\n avgY /= avgCount;\n }\n\n // Find point in current bucket with largest triangle area\n let maxArea = -1;\n let maxAreaIndex = bucketStart;\n\n for (let j = bucketStart; j < bucketEnd && j < dataLength; j++) {\n // Triangle area calculation\n const area = Math.abs(\n (x[a] - avgX) * (y[j] - y[a]) - (x[a] - x[j]) * (avgY - y[a])\n );\n\n if (area > maxArea) {\n maxArea = area;\n maxAreaIndex = j;\n }\n }\n\n sampledX[sampledIndex] = x[maxAreaIndex];\n sampledY[sampledIndex] = y[maxAreaIndex];\n sampledIndex++;\n a = maxAreaIndex;\n }\n\n // Always include last point\n sampledX[targetPoints - 1] = x[dataLength - 1];\n sampledY[targetPoints - 1] = y[dataLength - 1];\n\n return { x: sampledX, y: sampledY };\n}\n\n/**\n * Subtract a linear baseline from data\n * \n * @param x - X data\n * @param y - Y data\n * @param x1 - Start of baseline segment\n * @param x2 - End of baseline segment\n */\nexport function subtractBaseline(\n x: Float32Array | number[],\n y: Float32Array | number[],\n x1: number,\n x2: number\n): Float32Array {\n const n = x.length;\n const result = new Float32Array(n);\n\n // Find points near x1 and x2 to determine baseline\n let i1 = 0, i2 = n - 1;\n let minDist1 = Infinity, minDist2 = Infinity;\n\n for (let i = 0; i < n; i++) {\n const d1 = Math.abs(x[i] - x1);\n const d2 = Math.abs(x[i] - x2);\n if (d1 < minDist1) { minDist1 = d1; i1 = i; }\n if (d2 < minDist2) { minDist2 = d2; i2 = i; }\n }\n\n const y1 = y[i1];\n const y2 = y[i2];\n const slope = (y2 - y1) / (x[i2] - x[i1]);\n const intercept = y1 - slope * x[i1];\n\n for (let i = 0; i < n; i++) {\n result[i] = y[i] - (slope * x[i] + intercept);\n }\n\n return result;\n}\n"],"names":["SI_PREFIXES","getBestPrefix","value","absValue","i","prefix","scaled","formatWithPrefix","unit","decimals","formatValue","formatScientific","detectCycles","signal","tolerance","data","cycles","cycleStart","cycleNumber","prevDirection","directionChanges","direction","startValue","currentValue","generateCycleColors","count","colors","hueStep","hue","detectPeaks","x","y","options","minProminence","type","peaks","xData","yData","prev","curr","next","prominence","validateData","invalidCount","firstInvalidIndex","arr","calculateStats","min","max","sum","val","mean","sumSquaredDiff","diff","stdDev","movingAverage","windowSize","result","halfWindow","j","downsampleLTTB","targetPoints","dataLength","sampledX","sampledY","bucketSize","a","sampledIndex","bucketStart","bucketEnd","nextBucketEnd","avgX","avgY","avgCount","maxArea","maxAreaIndex","area","subtractBaseline","x1","x2","n","i1","i2","minDist1","minDist2","d1","d2","y1","slope","intercept"],"mappings":"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"}