velo-plot 1.12.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/{index.core-xiNcuFfJ.js → ChartCore-DQ691eXH.js} +1807 -1287
- package/dist/ChartCore-DQ691eXH.js.map +1 -0
- package/dist/assets/downsample.worker-C70QJ04y.js +2 -0
- package/dist/assets/downsample.worker-C70QJ04y.js.map +1 -0
- package/dist/assets/indicator.worker-bv0zC4EX.js +2 -0
- package/dist/assets/indicator.worker-bv0zC4EX.js.map +1 -0
- package/dist/core/OverlayRenderer.d.ts +2 -0
- package/dist/core/chart/ChartAlerts.d.ts +42 -0
- package/dist/core/chart/ChartCore.d.ts +20 -1
- package/dist/core/chart/ChartRenderLoop.d.ts +4 -2
- package/dist/core/chart/ChartRenderer.d.ts +3 -2
- package/dist/core/chart/candlestickMarkers.d.ts +23 -0
- package/dist/core/chart/exporter/SVGExporter.d.ts +7 -2
- package/dist/core/chart/heikinAshi.d.ts +10 -0
- package/dist/core/chart/series/types.d.ts +2 -2
- package/dist/core/chart/types.d.ts +9 -0
- package/dist/core/indicator/addIndicator.d.ts +36 -0
- package/dist/core/indicator/index.d.ts +2 -0
- package/dist/core/indicator/indicatorPresets.d.ts +37 -0
- package/dist/core/render/pixelSnap.d.ts +8 -0
- package/dist/core/series/Series.d.ts +3 -0
- package/dist/core/stacked/index.d.ts +3 -1
- package/dist/core/stacked/paneResize.d.ts +15 -18
- package/dist/core/stacked/stackExport.d.ts +18 -0
- package/dist/core/stacked/types.d.ts +42 -5
- package/dist/core/time/TimeScale.d.ts +31 -0
- package/dist/createStackedChart-BPooTQ9z.js +1164 -0
- package/dist/createStackedChart-BPooTQ9z.js.map +1 -0
- package/dist/gpu/adapter/gpuRenderer.d.ts +21 -0
- package/dist/gpu/backends/webgpu/WebGPUBackend.d.ts +2 -1
- package/dist/gpuRenderer-BpaMYz-k.js +1848 -0
- package/dist/gpuRenderer-BpaMYz-k.js.map +1 -0
- package/dist/index-CUGS98p4.js +277 -0
- package/dist/index-CUGS98p4.js.map +1 -0
- package/dist/index-D7dTq6VB.js +503 -0
- package/dist/index-D7dTq6VB.js.map +1 -0
- package/dist/{index-Ce2JgkH_.js → index-DoYxf1cV.js} +86 -43
- package/dist/{index-Ce2JgkH_.js.map → index-DoYxf1cV.js.map} +1 -1
- package/dist/index.core-BtGFYMOu.js +167 -0
- package/dist/index.core-BtGFYMOu.js.map +1 -0
- package/dist/index.d.ts +12 -4
- package/dist/indicators--eJGmVox.js +302 -0
- package/dist/indicators--eJGmVox.js.map +1 -0
- package/dist/math-Ddm2EZjj.js +85 -0
- package/dist/math-Ddm2EZjj.js.map +1 -0
- package/dist/plugins/analysis.js +543 -818
- package/dist/plugins/analysis.js.map +1 -1
- package/dist/plugins/anomaly-detection.d.ts +1 -0
- package/dist/plugins/anomaly-detection.js +221 -0
- package/dist/plugins/anomaly-detection.js.map +1 -0
- package/dist/plugins/broken-axis.d.ts +3 -0
- package/dist/plugins/broken-axis.js +262 -0
- package/dist/plugins/broken-axis.js.map +1 -0
- package/dist/plugins/caching.d.ts +3 -0
- package/dist/plugins/caching.js +187 -0
- package/dist/plugins/caching.js.map +1 -0
- package/dist/plugins/data-transform.d.ts +3 -0
- package/dist/plugins/data-transform.js +146 -0
- package/dist/plugins/data-transform.js.map +1 -0
- package/dist/plugins/debug.js +12 -11
- package/dist/plugins/debug.js.map +1 -1
- package/dist/plugins/drawing-tools/index.d.ts +16 -0
- package/dist/plugins/forecasting.d.ts +1 -0
- package/dist/plugins/forecasting.js +236 -0
- package/dist/plugins/forecasting.js.map +1 -0
- package/dist/plugins/gpu.js +202 -1895
- package/dist/plugins/gpu.js.map +1 -1
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/keyboard/index.d.ts +1 -7
- package/dist/plugins/keyboard.js +1 -1
- package/dist/plugins/latex.d.ts +1 -0
- package/dist/plugins/latex.js +454 -0
- package/dist/plugins/latex.js.map +1 -0
- package/dist/plugins/lazy-load/types.d.ts +7 -0
- package/dist/plugins/lazy-load.d.ts +3 -0
- package/dist/plugins/lazy-load.js +212 -0
- package/dist/plugins/lazy-load.js.map +1 -0
- package/dist/plugins/ml-integration.d.ts +3 -0
- package/dist/plugins/ml-integration.js +355 -0
- package/dist/plugins/ml-integration.js.map +1 -0
- package/dist/plugins/offscreen.d.ts +3 -0
- package/dist/plugins/offscreen.js +169 -0
- package/dist/plugins/offscreen.js.map +1 -0
- package/dist/plugins/pattern-recognition.d.ts +3 -0
- package/dist/plugins/pattern-recognition.js +690 -0
- package/dist/plugins/pattern-recognition.js.map +1 -0
- package/dist/plugins/radar.d.ts +3 -0
- package/dist/plugins/radar.js +103 -0
- package/dist/plugins/radar.js.map +1 -0
- package/dist/plugins/regression.d.ts +3 -0
- package/dist/plugins/regression.js +634 -0
- package/dist/plugins/regression.js.map +1 -0
- package/dist/plugins/replay/index.d.ts +19 -0
- package/dist/plugins/roi.d.ts +1 -0
- package/dist/plugins/roi.js +223 -0
- package/dist/plugins/roi.js.map +1 -0
- package/dist/plugins/snapshot/types.d.ts +1 -1
- package/dist/plugins/snapshot.d.ts +3 -0
- package/dist/plugins/snapshot.js +90 -0
- package/dist/plugins/snapshot.js.map +1 -0
- package/dist/plugins/sync/index.d.ts +2 -1
- package/dist/plugins/sync.js +13 -269
- package/dist/plugins/sync.js.map +1 -1
- package/dist/plugins/video-recorder.d.ts +3 -0
- package/dist/plugins/video-recorder.js +104 -0
- package/dist/plugins/video-recorder.js.map +1 -0
- package/dist/plugins/virtualization/types.d.ts +12 -0
- package/dist/plugins/virtualization.d.ts +3 -0
- package/dist/plugins/virtualization.js +6 -0
- package/dist/plugins/virtualization.js.map +1 -0
- package/dist/pool-15HMCNv8.js +88 -0
- package/dist/pool-15HMCNv8.js.map +1 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +31 -0
- package/dist/react.js.map +1 -0
- package/dist/renderer/CandlestickRenderer.d.ts +1 -1
- package/dist/renderer/ChartSeriesRenderer.d.ts +6 -0
- package/dist/renderer/GpuChartRenderer.d.ts +23 -0
- package/dist/renderer/index.d.ts +3 -0
- package/dist/renderer/spike/WebGLGridSpike.d.ts +40 -0
- package/dist/testing/baselines/v1.15.0.json.d.ts +51 -0
- package/dist/testing/gridSpikeBenchmark.d.ts +49 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/rendererBenchmark.d.ts +13 -0
- package/dist/testing/stage1BrowserBench.d.ts +42 -0
- package/dist/types.d.ts +21 -1
- package/dist/utils-BK5UUXxj.js +172 -0
- package/dist/utils-BK5UUXxj.js.map +1 -0
- package/dist/velo-plot.full.js +1382 -5932
- package/dist/velo-plot.full.js.map +1 -1
- package/dist/velo-plot.js +26 -25
- package/dist/velo-plot.js.map +1 -1
- package/dist/workers/downsample.d.ts +38 -0
- package/dist/workers/downsampleAsync.d.ts +6 -0
- package/dist/workers/indicator.worker.d.ts +32 -0
- package/dist/workers/indicatorsAsync.d.ts +26 -0
- package/dist/workers/pool.d.ts +36 -0
- package/package.json +19 -2
- package/dist/core/OverlayRenderer.test.d.ts +0 -1
- package/dist/core/chart/ChartScaling.test.d.ts +0 -1
- package/dist/core/chart/NavigationUtils.test.d.ts +0 -1
- package/dist/core/format/axisFormat.test.d.ts +0 -1
- package/dist/core/indicator/buildIndicatorSeries.test.d.ts +0 -1
- package/dist/core/series/SeriesBounds.test.d.ts +0 -1
- package/dist/core/stacked/createStackedChart.test.d.ts +0 -1
- package/dist/core/stacked/paneAxis.test.d.ts +0 -1
- package/dist/core/stacked/paneResize.test.d.ts +0 -1
- package/dist/core/sync/index.test.d.ts +0 -1
- package/dist/index.core-xiNcuFfJ.js.map +0 -1
- package/dist/plugins/tools/tooltip/format.test.d.ts +0 -1
- package/dist/utils-Dbro9-qM.js +0 -254
- package/dist/utils-Dbro9-qM.js.map +0 -1
package/dist/react.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { c as k } from "./createStackedChart-BPooTQ9z.js";
|
|
2
|
+
import { S as x, u as C } from "./createStackedChart-BPooTQ9z.js";
|
|
3
|
+
import { useRef as n, useState as a, useEffect as m, useCallback as f } from "react";
|
|
4
|
+
function A(c) {
|
|
5
|
+
const s = n(null), r = n(null), [i, l] = a(null), [d, u] = a(!1), o = n(c);
|
|
6
|
+
o.current = c, m(() => {
|
|
7
|
+
const t = s.current;
|
|
8
|
+
if (!t) return;
|
|
9
|
+
const e = k({
|
|
10
|
+
...o.current,
|
|
11
|
+
container: t
|
|
12
|
+
});
|
|
13
|
+
return r.current = e, l(e), e.whenReady().then(() => u(!0)), () => {
|
|
14
|
+
u(!1), e.destroy(), r.current = null, l(null);
|
|
15
|
+
};
|
|
16
|
+
}, []);
|
|
17
|
+
const R = f((t) => {
|
|
18
|
+
var e;
|
|
19
|
+
(e = r.current) == null || e.fitAll(t);
|
|
20
|
+
}, []), S = f(() => {
|
|
21
|
+
var t;
|
|
22
|
+
(t = r.current) == null || t.resetAll();
|
|
23
|
+
}, []);
|
|
24
|
+
return { containerRef: s, stack: i, isReady: d, fitAll: R, resetAll: S };
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
x as SciPlot,
|
|
28
|
+
C as useSciPlot,
|
|
29
|
+
A as useStackedPlot
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sources":["../src/react/useStackedPlot.ts"],"sourcesContent":["/**\n * useStackedPlot - React hook for multi-pane stacked charts\n */\n\nimport {\n useEffect,\n useRef,\n useState,\n useCallback,\n type RefObject,\n} from \"react\";\nimport { createStackedChart } from \"../core/stacked\";\nimport type { StackedChart, StackedChartOptions } from \"../core/stacked\";\nimport type { Range } from \"../types\";\n\nexport interface UseStackedPlotOptions extends Omit<StackedChartOptions, \"container\"> {}\n\nexport interface UseStackedPlotReturn {\n containerRef: RefObject<HTMLDivElement>;\n stack: StackedChart | null;\n isReady: boolean;\n fitAll: (options?: { x?: Range; padding?: number }) => void;\n resetAll: () => void;\n}\n\nexport function useStackedPlot(\n options: UseStackedPlotOptions,\n): UseStackedPlotReturn {\n const containerRef = useRef<HTMLDivElement>(null);\n const stackRef = useRef<StackedChart | null>(null);\n const [stack, setStack] = useState<StackedChart | null>(null);\n const [isReady, setIsReady] = useState(false);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const created = createStackedChart({\n ...optionsRef.current,\n container: el,\n });\n stackRef.current = created;\n setStack(created);\n\n created.whenReady().then(() => setIsReady(true));\n\n return () => {\n setIsReady(false);\n created.destroy();\n stackRef.current = null;\n setStack(null);\n };\n }, []);\n\n const fitAll = useCallback((opts?: { x?: Range; padding?: number }) => {\n stackRef.current?.fitAll(opts);\n }, []);\n\n const resetAll = useCallback(() => {\n stackRef.current?.resetAll();\n }, []);\n\n return { containerRef, stack, isReady, fitAll, resetAll };\n}\n"],"names":["useStackedPlot","options","containerRef","useRef","stackRef","stack","setStack","useState","isReady","setIsReady","optionsRef","useEffect","el","created","createStackedChart","fitAll","useCallback","opts","_a","resetAll"],"mappings":";;;AAyBO,SAASA,EACdC,GACsB;AACtB,QAAMC,IAAeC,EAAuB,IAAI,GAC1CC,IAAWD,EAA4B,IAAI,GAC3C,CAACE,GAAOC,CAAQ,IAAIC,EAA8B,IAAI,GACtD,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAK,GACtCG,IAAaP,EAAOF,CAAO;AACjC,EAAAS,EAAW,UAAUT,GAErBU,EAAU,MAAM;AACd,UAAMC,IAAKV,EAAa;AACxB,QAAI,CAACU,EAAI;AAET,UAAMC,IAAUC,EAAmB;AAAA,MACjC,GAAGJ,EAAW;AAAA,MACd,WAAWE;AAAA,IAAA,CACZ;AACD,WAAAR,EAAS,UAAUS,GACnBP,EAASO,CAAO,GAEhBA,EAAQ,YAAY,KAAK,MAAMJ,EAAW,EAAI,CAAC,GAExC,MAAM;AACX,MAAAA,EAAW,EAAK,GAChBI,EAAQ,QAAA,GACRT,EAAS,UAAU,MACnBE,EAAS,IAAI;AAAA,IACf;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAMS,IAASC,EAAY,CAACC,MAA2C;;AACrE,KAAAC,IAAAd,EAAS,YAAT,QAAAc,EAAkB,OAAOD;AAAA,EAC3B,GAAG,CAAA,CAAE,GAECE,IAAWH,EAAY,MAAM;;AACjC,KAAAE,IAAAd,EAAS,YAAT,QAAAc,EAAkB;AAAA,EACpB,GAAG,CAAA,CAAE;AAEL,SAAO,EAAE,cAAAhB,GAAc,OAAAG,GAAO,SAAAG,GAAS,QAAAO,GAAQ,UAAAI,EAAA;AACjD;"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Each candle is represented by a body (box) and a wick (line/thin box)
|
|
7
7
|
* Returns a Float32Array of interleaved [x, y] coordinates for TRIANGLES
|
|
8
8
|
*/
|
|
9
|
-
export declare function interleaveCandlestickData(x: Float32Array | Float64Array, open: Float32Array | Float64Array, high: Float32Array | Float64Array, low: Float32Array | Float64Array, close: Float32Array | Float64Array, width: number): {
|
|
9
|
+
export declare function interleaveCandlestickData(x: Float32Array | Float64Array, open: Float32Array | Float64Array, high: Float32Array | Float64Array, low: Float32Array | Float64Array, close: Float32Array | Float64Array, width: number, hollow?: boolean): {
|
|
10
10
|
bullish: Float32Array;
|
|
11
11
|
bearish: Float32Array;
|
|
12
12
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { NativeWebGLRenderer } from './NativeWebGLRenderer';
|
|
2
|
+
import { GpuChartRenderer } from './GpuChartRenderer';
|
|
3
|
+
|
|
4
|
+
export type ChartSeriesRenderer = NativeWebGLRenderer | GpuChartRenderer;
|
|
5
|
+
export type ChartRendererBackend = "webgl" | "webgpu";
|
|
6
|
+
export declare function getRendererBackend(renderer: ChartSeriesRenderer): ChartRendererBackend;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GpuRenderer, GpuRendererOptions } from '../gpu/adapter/gpuRenderer';
|
|
2
|
+
import { NativeRenderOptions, NativeSeriesRenderData } from './native/types';
|
|
3
|
+
|
|
4
|
+
export declare class GpuChartRenderer {
|
|
5
|
+
private gpu;
|
|
6
|
+
private dpr;
|
|
7
|
+
private readonly canvas;
|
|
8
|
+
constructor(gpu: GpuRenderer, canvas: HTMLCanvasElement);
|
|
9
|
+
get available(): boolean;
|
|
10
|
+
get backend(): "webgpu" | "webgl";
|
|
11
|
+
setDPR(dpr: number): void;
|
|
12
|
+
createBuffer(id: string, data: Float32Array): void;
|
|
13
|
+
updateBuffer(id: string, data: Float32Array, offsetInBytes: number): boolean;
|
|
14
|
+
getBuffer(id: string): WebGLBuffer | undefined;
|
|
15
|
+
deleteBuffer(id: string): void;
|
|
16
|
+
createColormapTexture(id: string, data: Uint8Array): void;
|
|
17
|
+
getTexture(id: string): WebGLTexture | undefined;
|
|
18
|
+
render(series: NativeSeriesRenderData[], options: NativeRenderOptions): void;
|
|
19
|
+
resize(): void;
|
|
20
|
+
getLimits(): Record<string, unknown>;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function createGpuChartRenderer(canvas: HTMLCanvasElement, options?: GpuRendererOptions): Promise<GpuChartRenderer | null>;
|
package/dist/renderer/index.d.ts
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { NativeWebGLRenderer, interleaveData, interleaveStepData, interleaveBandData, interleaveErrorData, interleaveBoxPlotData, interleaveWaterfallData, parseColor, type NativeSeriesRenderData, type NativeRenderOptions, } from './NativeWebGLRenderer';
|
|
5
5
|
export { type IWebGLRenderer, type SeriesRenderData, type RenderOptions, createRenderer, createNativeRenderer, } from './RendererInterface';
|
|
6
|
+
export { GpuChartRenderer, createGpuChartRenderer } from './GpuChartRenderer';
|
|
6
7
|
export { WebGPURenderer, type WebGPURendererOptions } from './WebGPURenderer';
|
|
8
|
+
export type { ChartSeriesRenderer, ChartRendererBackend } from './ChartSeriesRenderer';
|
|
9
|
+
export { getRendererBackend } from './ChartSeriesRenderer';
|
|
7
10
|
export * from './shaders';
|
|
8
11
|
export * from './radar';
|
|
9
12
|
export { interleaveBarData, calculateBarWidth } from './BarRenderer';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebGL grid spike — batched GL_LINES renderer for axis grid lines.
|
|
3
|
+
* Labels remain on Canvas 2D; this spike covers grid lines only.
|
|
4
|
+
* @module renderer/spike/WebGLGridSpike
|
|
5
|
+
*/
|
|
6
|
+
export interface GridLineSpec {
|
|
7
|
+
/** Plot area in CSS pixels */
|
|
8
|
+
plotArea: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
14
|
+
/** Pixel X positions for vertical grid lines */
|
|
15
|
+
xLines: number[];
|
|
16
|
+
/** Pixel Y positions for horizontal grid lines */
|
|
17
|
+
yLines: number[];
|
|
18
|
+
/** Canvas width (physical pixels) */
|
|
19
|
+
width: number;
|
|
20
|
+
/** Canvas height (physical pixels) */
|
|
21
|
+
height: number;
|
|
22
|
+
/** Line color RGBA 0–1 */
|
|
23
|
+
color?: [number, number, number, number];
|
|
24
|
+
/** Line width in pixels */
|
|
25
|
+
lineWidth?: number;
|
|
26
|
+
}
|
|
27
|
+
/** Build interleaved [x,y] vertex pairs for GL_LINES from tick pixel positions */
|
|
28
|
+
export declare function buildGridLineVertices(spec: GridLineSpec): Float32Array;
|
|
29
|
+
export declare class WebGLGridSpike {
|
|
30
|
+
private gl;
|
|
31
|
+
private program;
|
|
32
|
+
private buffer;
|
|
33
|
+
private aPosition;
|
|
34
|
+
private uResolution;
|
|
35
|
+
private uColor;
|
|
36
|
+
constructor(canvas: HTMLCanvasElement);
|
|
37
|
+
/** Upload vertices and draw grid lines */
|
|
38
|
+
draw(spec: GridLineSpec): number;
|
|
39
|
+
destroy(): void;
|
|
40
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
"version": "1.17.0",
|
|
3
|
+
"recordedAt": "2026-07-05",
|
|
4
|
+
"notes": "Stage 1 baselines. Headless CI uses effective FPS (render throughput when rAF throttled) via scripts/benchmark-scenario-eval.mjs smoke floors.",
|
|
5
|
+
"regressionThreshold": 0.10,
|
|
6
|
+
"webgpuMinFpsRatio": 0.95,
|
|
7
|
+
"scenarios": {
|
|
8
|
+
"line-1m-pan": {
|
|
9
|
+
"minAvgFps": 50,
|
|
10
|
+
"maxAvgFrameTimeMs": 20,
|
|
11
|
+
"points": 1000000,
|
|
12
|
+
"description": "1M line points, continuous pan/zoom simulation"
|
|
13
|
+
},
|
|
14
|
+
"candlestick-500k": {
|
|
15
|
+
"minAvgFps": 45,
|
|
16
|
+
"maxAvgFrameTimeMs": 22,
|
|
17
|
+
"points": 500000,
|
|
18
|
+
"description": "500k OHLC bars with PluginVirtualization"
|
|
19
|
+
},
|
|
20
|
+
"stack-5pane-resize": {
|
|
21
|
+
"minAvgFps": 48,
|
|
22
|
+
"maxAvgFrameTimeMs": 21,
|
|
23
|
+
"description": "5-pane stack divider drag (batched overlay redraw)"
|
|
24
|
+
},
|
|
25
|
+
"line-200k-webgl": {
|
|
26
|
+
"minAvgFps": 45,
|
|
27
|
+
"description": "200k line WebGL baseline for renderer compare"
|
|
28
|
+
},
|
|
29
|
+
"line-200k-webgpu": {
|
|
30
|
+
"minAvgFps": 42,
|
|
31
|
+
"description": "200k line WebGPU — target ≥95% of WebGL FPS"
|
|
32
|
+
},
|
|
33
|
+
"boxplot-5k-webgpu": {
|
|
34
|
+
"minAvgFps": 50,
|
|
35
|
+
"description": "5k boxplot series on WebGPU chart path"
|
|
36
|
+
},
|
|
37
|
+
"grid-canvas2d-dpr2": {
|
|
38
|
+
"maxAvgFrameUs": 1200,
|
|
39
|
+
"dpr": 2,
|
|
40
|
+
"description": "Canvas 2D grid+minor at 1920×1080 @2x"
|
|
41
|
+
},
|
|
42
|
+
"grid-webgl-dpr2": {
|
|
43
|
+
"maxAvgFrameUs": 400,
|
|
44
|
+
"dpr": 2,
|
|
45
|
+
"description": "WebGL grid spike lines only @2x"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
;
|
|
50
|
+
|
|
51
|
+
export default _default;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grid rendering benchmark — Canvas 2D vs WebGL spike comparison.
|
|
3
|
+
* @module testing/gridSpikeBenchmark
|
|
4
|
+
*/
|
|
5
|
+
export interface GridBenchmarkResult {
|
|
6
|
+
backend: "canvas2d" | "webgl";
|
|
7
|
+
dpr: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
xTicks: number;
|
|
11
|
+
yTicks: number;
|
|
12
|
+
lineSegments: number;
|
|
13
|
+
avgFrameUs: number;
|
|
14
|
+
p95FrameUs: number;
|
|
15
|
+
frames: number;
|
|
16
|
+
}
|
|
17
|
+
export interface GridCompareResult {
|
|
18
|
+
canvas2d: GridBenchmarkResult;
|
|
19
|
+
webgl: GridBenchmarkResult | null;
|
|
20
|
+
/** Positive = WebGL faster */
|
|
21
|
+
gainPercent: number;
|
|
22
|
+
recommendation: "implement" | "defer";
|
|
23
|
+
}
|
|
24
|
+
/** Benchmark Canvas 2D grid via OverlayRenderer.drawGrid */
|
|
25
|
+
export declare function benchmarkCanvasGrid(options?: {
|
|
26
|
+
width?: number;
|
|
27
|
+
height?: number;
|
|
28
|
+
dpr?: number;
|
|
29
|
+
xTickCount?: number;
|
|
30
|
+
yTickCount?: number;
|
|
31
|
+
frames?: number;
|
|
32
|
+
showMinor?: boolean;
|
|
33
|
+
}): GridBenchmarkResult;
|
|
34
|
+
/** Benchmark WebGL grid spike (returns null if WebGL unavailable) */
|
|
35
|
+
export declare function benchmarkWebGLGrid(options?: {
|
|
36
|
+
width?: number;
|
|
37
|
+
height?: number;
|
|
38
|
+
dpr?: number;
|
|
39
|
+
xTickCount?: number;
|
|
40
|
+
yTickCount?: number;
|
|
41
|
+
frames?: number;
|
|
42
|
+
}): GridBenchmarkResult | null;
|
|
43
|
+
/** Compare backends; recommends implement if WebGL ≥20% faster */
|
|
44
|
+
export declare function compareGridBackends(options?: {
|
|
45
|
+
dpr?: number;
|
|
46
|
+
frames?: number;
|
|
47
|
+
}): GridCompareResult;
|
|
48
|
+
/** Vertex builder smoke test (no GL required) */
|
|
49
|
+
export declare function countGridVertices(xTickCount: number, yTickCount: number): number;
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -68,6 +68,8 @@ export interface BenchmarkResult {
|
|
|
68
68
|
pointsRendered: number;
|
|
69
69
|
/** Points per second throughput */
|
|
70
70
|
throughput: number;
|
|
71
|
+
/** Render-only throughput (1000 / avgFrameTime); can exceed wall FPS */
|
|
72
|
+
renderFps?: number;
|
|
71
73
|
}
|
|
72
74
|
export interface BenchmarkOptions {
|
|
73
75
|
/** Duration of benchmark in ms (default: 5000) */
|
|
@@ -223,3 +225,6 @@ export declare function waitForFrames(count?: number): Promise<void>;
|
|
|
223
225
|
* Wait for a specified duration
|
|
224
226
|
*/
|
|
225
227
|
export declare function waitFor(ms: number): Promise<void>;
|
|
228
|
+
export { benchmarkCanvasGrid, benchmarkWebGLGrid, compareGridBackends, countGridVertices, type GridBenchmarkResult, type GridCompareResult, } from './gridSpikeBenchmark';
|
|
229
|
+
export { getBaseline, compareScenarioToBaseline, buildStage1Report, effectiveBenchmarkFps, HEADLESS_LOW_FRAME_THRESHOLD, SMOKE_FLOORS, type Stage1ScenarioResult, type Stage1BrowserReport, } from './stage1BrowserBench';
|
|
230
|
+
export { evaluateRendererCompare, type RendererCompareResult, } from './rendererBenchmark';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BenchmarkResult } from './index';
|
|
2
|
+
|
|
3
|
+
export interface RendererCompareResult {
|
|
4
|
+
webgl: BenchmarkResult;
|
|
5
|
+
webgpu: BenchmarkResult | null;
|
|
6
|
+
webgpuActive: boolean;
|
|
7
|
+
/** webgpuFps / webglFps — ≥1 means WebGPU is faster or equal */
|
|
8
|
+
fpsRatio: number;
|
|
9
|
+
/** Meets ADR target: WebGPU ≥ 95% of WebGL FPS */
|
|
10
|
+
meetsTarget: boolean;
|
|
11
|
+
recommendation: "webgpu-ready" | "webgl-faster" | "webgpu-unavailable";
|
|
12
|
+
}
|
|
13
|
+
export declare function evaluateRendererCompare(webgl: BenchmarkResult, webgpu: BenchmarkResult | null, webgpuActive: boolean, minRatio?: number): RendererCompareResult;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { BenchmarkResult } from './index';
|
|
2
|
+
import { default as baseline } from './baselines/v1.15.0.json';
|
|
3
|
+
|
|
4
|
+
export interface Stage1ScenarioResult {
|
|
5
|
+
id: string;
|
|
6
|
+
benchmark: BenchmarkResult;
|
|
7
|
+
passed: boolean;
|
|
8
|
+
failures: string[];
|
|
9
|
+
baselineMinFps?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface Stage1BrowserReport {
|
|
12
|
+
timestamp: number;
|
|
13
|
+
version: string;
|
|
14
|
+
scenarios: Stage1ScenarioResult[];
|
|
15
|
+
allPassed: boolean;
|
|
16
|
+
regressionThreshold: number;
|
|
17
|
+
}
|
|
18
|
+
export interface BaselineScenario {
|
|
19
|
+
minAvgFps?: number;
|
|
20
|
+
maxAvgFrameTimeMs?: number;
|
|
21
|
+
maxAvgFrameUs?: number;
|
|
22
|
+
dpr?: number;
|
|
23
|
+
points?: number;
|
|
24
|
+
description?: string;
|
|
25
|
+
}
|
|
26
|
+
/** When wall-clock frame count is below this, headless rAF is unreliable — use renderFps. */
|
|
27
|
+
export declare const HEADLESS_LOW_FRAME_THRESHOLD = 30;
|
|
28
|
+
/** Headless smoke floors (renderFps or effective FPS). Desktop uses baseline minAvgFps. */
|
|
29
|
+
export declare const SMOKE_FLOORS: Record<string, number>;
|
|
30
|
+
/** Pick wall or render FPS depending on headless throttling artifacts. */
|
|
31
|
+
export declare function effectiveBenchmarkFps(result: Pick<BenchmarkResult, "avgFps" | "renderFps" | "frameCount">, isHeadless: boolean): number;
|
|
32
|
+
export declare function getBaseline(): typeof baseline;
|
|
33
|
+
/** Compare a benchmark result against v1.15 baseline for a scenario */
|
|
34
|
+
export declare function compareScenarioToBaseline(scenarioId: string, result: BenchmarkResult | {
|
|
35
|
+
avgFrameUs: number;
|
|
36
|
+
backend?: string;
|
|
37
|
+
}): {
|
|
38
|
+
passed: boolean;
|
|
39
|
+
failures: string[];
|
|
40
|
+
};
|
|
41
|
+
/** Build a full Stage 1 report from scenario results */
|
|
42
|
+
export declare function buildStage1Report(scenarios: Stage1ScenarioResult[]): Stage1BrowserReport;
|
package/dist/types.d.ts
CHANGED
|
@@ -69,6 +69,8 @@ export interface AxisOptions {
|
|
|
69
69
|
labelRotation?: number;
|
|
70
70
|
/** Target number of tick divisions on this axis (default: 8 for X, 6 for Y) */
|
|
71
71
|
tickCount?: number;
|
|
72
|
+
/** Trading time scale (Stage 2) — business-day gaps, session presets */
|
|
73
|
+
timeScale?: import('./core/time/TimeScale').TimeScaleOptions;
|
|
72
74
|
}
|
|
73
75
|
export type SeriesType = "line" | "scatter" | "line+scatter" | "step" | "step+scatter" | "band" | "area" | "bar" | "heatmap" | "candlestick" | "polar" | "radar" | "boxplot" | "waterfall" | "gauge" | "sankey" | "ternary" | "indicator";
|
|
74
76
|
/** Step mode defines where the step occurs */
|
|
@@ -202,6 +204,8 @@ export interface SeriesOptions {
|
|
|
202
204
|
maxPoints?: number;
|
|
203
205
|
/** Identifier for stacking series (same ID will be stacked together) */
|
|
204
206
|
stackId?: string;
|
|
207
|
+
/** Buy/sell markers on candlestick series (Stage 2.14) */
|
|
208
|
+
markers?: import('./core/chart/candlestickMarkers').CandlestickMarker[];
|
|
205
209
|
}
|
|
206
210
|
export interface SeriesUpdateData {
|
|
207
211
|
/** New X values */
|
|
@@ -509,7 +513,14 @@ export interface ChartOptions {
|
|
|
509
513
|
container: HTMLDivElement;
|
|
510
514
|
/** Stable chart id (used by sync groups; auto-generated if omitted) */
|
|
511
515
|
id?: string;
|
|
512
|
-
/**
|
|
516
|
+
/**
|
|
517
|
+
* Renderer backend selection (default: `'webgl'`).
|
|
518
|
+
* `'webgl'` uses WebGL2 via `NativeWebGLRenderer`.
|
|
519
|
+
* `'webgpu'` uses the GPU abstraction layer (WebGPU when available, otherwise
|
|
520
|
+
* falls back to WebGL2 with a console warning). Use `chart.getActiveRenderer()`
|
|
521
|
+
* to inspect the runtime backend. Boxplot/waterfall series render on WebGL only.
|
|
522
|
+
* @see docs/adr/001-webgpu-renderer-strategy.md
|
|
523
|
+
*/
|
|
513
524
|
renderer?: "webgl" | "webgpu";
|
|
514
525
|
/** X-axis configuration */
|
|
515
526
|
xAxis?: AxisOptions;
|
|
@@ -752,6 +763,15 @@ export interface ChartEventMap {
|
|
|
752
763
|
dataLoading: DataLoadingEvent;
|
|
753
764
|
deltaMeasure: DeltaMeasurement;
|
|
754
765
|
peakMeasure: PeakMeasurement;
|
|
766
|
+
/** Price alert triggered (Stage 2.18) */
|
|
767
|
+
alert: {
|
|
768
|
+
id: string;
|
|
769
|
+
price: number;
|
|
770
|
+
direction: "above" | "below" | "cross";
|
|
771
|
+
seriesId?: string;
|
|
772
|
+
triggeredAt: number;
|
|
773
|
+
triggerPrice: number;
|
|
774
|
+
};
|
|
755
775
|
}
|
|
756
776
|
/** GPU uniform values */
|
|
757
777
|
export interface Uniforms {
|
|
@@ -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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"}
|