waveform-analysis 0.1.48 → 0.1.51

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 (117) hide show
  1. package/README.md +120 -5
  2. package/dist/assets/workerRuntime-Dt94jbqq.js +1 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.js +2683 -1153
  5. package/dist/style.css +1 -1
  6. package/dist/types/components/WaveformChart.vue.d.ts +14 -0
  7. package/dist/types/components/annotation/WaveformAnnotationEditor.vue.d.ts +0 -1
  8. package/dist/types/components/annotation/markup.d.ts +5 -6
  9. package/dist/types/components/annotation/types.d.ts +2 -0
  10. package/dist/types/components/core/latestTaskScheduler.d.ts +19 -0
  11. package/dist/types/components/core/trackLayoutBuilder.d.ts +3 -1
  12. package/dist/types/components/core/types.d.ts +2 -0
  13. package/dist/types/components/core/useWaveformChartController.d.ts +1 -0
  14. package/dist/types/components/core/useWaveformData.d.ts +2 -0
  15. package/dist/types/components/core/useWaveformLayout.d.ts +2 -0
  16. package/dist/types/components/core/useWaveformRenderSampling.d.ts +19 -0
  17. package/dist/types/components/core/waveformChartTypes.d.ts +8 -1
  18. package/dist/types/components/core/workerSamplingClient.d.ts +8 -0
  19. package/dist/types/components/data/types.d.ts +1 -1
  20. package/dist/types/components/interaction/zoomConstraints.d.ts +2 -0
  21. package/dist/types/components/waveform.d.ts +1 -1
  22. package/dist/types/core/compactSamplePointSource.d.ts +3 -0
  23. package/dist/types/core/data.d.ts +7 -1
  24. package/dist/types/core/index.d.ts +2 -1
  25. package/dist/types/core/rendering.d.ts +3 -1
  26. package/dist/types/core/renderingOptions.d.ts +11 -1
  27. package/dist/types/core/samplingReference.d.ts +36 -0
  28. package/dist/types/core/samplingReferenceValues.d.ts +4 -0
  29. package/dist/types/core/samplingReferenceVectors.d.ts +17 -0
  30. package/dist/types/core/wasmSampling.d.ts +48 -0
  31. package/dist/types/core/waveformPointSource.d.ts +53 -0
  32. package/dist/types/core/workerSampling/index.d.ts +5 -0
  33. package/dist/types/core/workerSampling/javascriptBackend.d.ts +3 -0
  34. package/dist/types/core/workerSampling/multiresolution.d.ts +29 -0
  35. package/dist/types/core/workerSampling/protocol.d.ts +153 -0
  36. package/dist/types/core/workerSampling/repository.d.ts +36 -0
  37. package/dist/types/core/workerSampling/repositoryDataset.d.ts +29 -0
  38. package/dist/types/core/workerSampling/repositorySupport.d.ts +27 -0
  39. package/dist/types/core/workerSampling/samplingCache.d.ts +24 -0
  40. package/dist/types/core/workerSampling/workerRuntime.d.ts +1 -0
  41. package/dist/types/index.d.ts +2 -2
  42. package/dist/types/types/chart.d.ts +56 -0
  43. package/dist/types/types/data.d.ts +20 -2
  44. package/dist/types/types/index.d.ts +2 -2
  45. package/dist/types/views/WasmSamplingDemo.vue.d.ts +3 -0
  46. package/package.json +5 -4
  47. package/src/App.test.ts +11 -7
  48. package/src/App.vue +2 -2
  49. package/src/DemoRouterApp.vue +1 -0
  50. package/src/components/WaveformChart.css +2 -0
  51. package/src/components/annotation/WaveformAnnotationEditor.css +2 -2
  52. package/src/components/annotation/WaveformAnnotationEditor.vue +8 -4
  53. package/src/components/annotation/markup.ts +45 -26
  54. package/src/components/annotation/types.ts +2 -0
  55. package/src/components/annotation/useWaveformChartAnnotations.ts +12 -15
  56. package/src/components/core/latestTaskScheduler.test.ts +51 -0
  57. package/src/components/core/latestTaskScheduler.ts +75 -0
  58. package/src/components/core/trackLayoutBuilder.ts +3 -0
  59. package/src/components/core/types.ts +2 -0
  60. package/src/components/core/useWaveformChartController.ts +14 -0
  61. package/src/components/core/useWaveformData.test.ts +95 -0
  62. package/src/components/core/useWaveformData.ts +23 -32
  63. package/src/components/core/useWaveformLayout.ts +4 -4
  64. package/src/components/core/useWaveformRenderSampling.test.ts +17 -0
  65. package/src/components/core/useWaveformRenderSampling.ts +394 -0
  66. package/src/components/core/waveformChartTypes.ts +13 -0
  67. package/src/components/core/workerSamplingClient.ts +148 -0
  68. package/src/components/data/types.ts +9 -0
  69. package/src/components/interaction/WaveformTooltip.test.ts +1 -3
  70. package/src/components/interaction/WaveformTooltip.vue +13 -12
  71. package/src/components/interaction/useWaveformHover.ts +7 -7
  72. package/src/components/interaction/zoomConstraints.ts +5 -1
  73. package/src/components/waveform.ts +9 -0
  74. package/src/components/waveformChartCases/compactAndSharedHover.test.ts +15 -4
  75. package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +11 -6
  76. package/src/components/waveformChartCases/dataLifecycle.test.ts +1 -3
  77. package/src/components/waveformChartCases/workerSamplingIntegration.test.ts +117 -0
  78. package/src/components/waveformChartCases/xDomainStrategy.test.ts +14 -2
  79. package/src/core/compactSamplePointSource.ts +76 -0
  80. package/src/core/data.test.ts +114 -0
  81. package/src/core/data.ts +177 -75
  82. package/src/core/index.ts +11 -0
  83. package/src/core/rendering.test.ts +87 -12
  84. package/src/core/rendering.ts +35 -1
  85. package/src/core/renderingOptions.ts +124 -15
  86. package/src/core/samplingReference.test.ts +124 -0
  87. package/src/core/samplingReference.ts +243 -0
  88. package/src/core/samplingReferenceValues.ts +169 -0
  89. package/src/core/samplingReferenceVectors.ts +46 -0
  90. package/src/core/wasmSampling.test.ts +205 -0
  91. package/src/core/wasmSampling.ts +230 -0
  92. package/src/core/waveformPointSource.ts +395 -0
  93. package/src/core/workerSampling/index.ts +30 -0
  94. package/src/core/workerSampling/javascriptBackend.ts +15 -0
  95. package/src/core/workerSampling/multiresolution.test.ts +69 -0
  96. package/src/core/workerSampling/multiresolution.ts +300 -0
  97. package/src/core/workerSampling/protocol.test.ts +34 -0
  98. package/src/core/workerSampling/protocol.ts +207 -0
  99. package/src/core/workerSampling/repository.test.ts +400 -0
  100. package/src/core/workerSampling/repository.ts +400 -0
  101. package/src/core/workerSampling/repositoryCache.test.ts +163 -0
  102. package/src/core/workerSampling/repositoryDataset.ts +194 -0
  103. package/src/core/workerSampling/repositorySupport.ts +225 -0
  104. package/src/core/workerSampling/repositoryTypedDataset.test.ts +89 -0
  105. package/src/core/workerSampling/repositoryWasmDataset.test.ts +70 -0
  106. package/src/core/workerSampling/samplingCache.ts +94 -0
  107. package/src/core/workerSampling/workerRuntime.ts +40 -0
  108. package/src/data/simulatedWaveforms.test.ts +19 -0
  109. package/src/data/simulatedWaveforms.ts +11 -8
  110. package/src/demoRoutes.css +177 -0
  111. package/src/index.ts +18 -0
  112. package/src/router.ts +5 -0
  113. package/src/types/chart.ts +63 -0
  114. package/src/types/data.ts +24 -1
  115. package/src/types/index.ts +9 -0
  116. package/src/views/WasmSamplingDemo.test.ts +13 -0
  117. package/src/views/WasmSamplingDemo.vue +180 -0
@@ -0,0 +1,53 @@
1
+ import type { SingleWaveformData, WaveformPoint, WaveformTypedValues } from '../types';
2
+ export interface WaveformPointRange {
3
+ start: number;
4
+ end: number;
5
+ }
6
+ export interface WaveformPointMetrics {
7
+ xMinimum: number;
8
+ xMaximum: number;
9
+ yMinimum: number;
10
+ yMaximum: number;
11
+ hasErrorPoints: boolean;
12
+ }
13
+ export interface WorkerSamplingNumericDataset {
14
+ kind: 'typed';
15
+ x: Float64Array;
16
+ y: WaveformTypedValues;
17
+ }
18
+ export interface WorkerSamplingSampleDataset {
19
+ kind: 'samples';
20
+ values: WaveformTypedValues;
21
+ sampleRate: number;
22
+ startTime: number;
23
+ /** Present only when invalid input samples created gaps in the original time sequence. */
24
+ sourceIndexes?: Uint32Array;
25
+ }
26
+ export interface WorkerSamplingPointDataset {
27
+ kind: 'points';
28
+ points: readonly WaveformPoint[];
29
+ }
30
+ export type WorkerSamplingDataset = WorkerSamplingNumericDataset | WorkerSamplingSampleDataset | WorkerSamplingPointDataset;
31
+ /**
32
+ * Internal source abstraction. Compact inputs stay as numeric columns until a caller needs a
33
+ * specific point object for SVG, hover, or annotation work.
34
+ */
35
+ export interface WaveformPointSource {
36
+ readonly length: number;
37
+ readonly isCompact: boolean;
38
+ pointAt(index: number): WaveformPoint | undefined;
39
+ pointsInRange(start?: number, end?: number): WaveformPoint[];
40
+ visibleRange(domain: [number, number]): WaveformPointRange;
41
+ nearestPoint(x: number): WaveformPoint | undefined;
42
+ metrics(includeErrors?: boolean): WaveformPointMetrics;
43
+ toWorkerDataset(): WorkerSamplingDataset;
44
+ }
45
+ export declare function createWaveformPointSource(data: SingleWaveformData, normalizeObjectData: (value: SingleWaveformData) => WaveformPoint[]): WaveformPointSource;
46
+ export declare function pointSourceFromPoints(points: readonly WaveformPoint[]): WaveformPointSource;
47
+ /**
48
+ * Keeps existing array-oriented component code compatible while constructing compact points only
49
+ * when an index is actually read. Native array methods use `has` before indexed reads, so both
50
+ * iteration and `slice` retain their normal semantics without eagerly allocating point objects.
51
+ */
52
+ export declare function lazyPointArray(source: WaveformPointSource): WaveformPoint[];
53
+ export declare function isWaveformPointSource(value: unknown): value is WaveformPointSource;
@@ -0,0 +1,5 @@
1
+ export { javascriptSamplingBackend } from './javascriptBackend';
2
+ export { MultiResolutionSamplingIndex, type MultiResolutionSamplingIndexOptions, } from './multiresolution';
3
+ export { WorkerSamplingRepository, type WorkerSamplingRepositoryOptions } from './repository';
4
+ export { SamplingOutputCache, type SamplingCacheMetrics, type SamplingOutputCacheOptions, } from './samplingCache';
5
+ export { DEFAULT_WORKER_SAMPLING_AUTO_THRESHOLD, DEFAULT_WORKER_SAMPLING_MAX_POINTS_PER_PIXEL, isCurrentWorkerSamplingResponse, type DatasetResponse, type DisposeAllResponse, type FindNearestPointResponse, type SampleViewportResponse, type WorkerSamplingBackend, type WorkerSamplingBackendKind, type WorkerSamplingDatasetMetrics, type WorkerSamplingDiagnostics, type WorkerSamplingOutput, type WorkerSamplingRequest, type WorkerSamplingResponse, type WorkerSamplingSeriesRequest, type WorkerSamplingSeriesResponse, type WorkerSamplingStatus, } from './protocol';
@@ -0,0 +1,3 @@
1
+ import type { WorkerSamplingBackend } from './protocol';
2
+ /** The phase 3 fallback backend. A future WASM backend implements the same narrow interface. */
3
+ export declare const javascriptSamplingBackend: WorkerSamplingBackend;
@@ -0,0 +1,29 @@
1
+ import type { WaveformPoint } from '@/types';
2
+ import type { WorkerSamplingOutput } from './protocol';
3
+ export type IndexedSamplingStrategy = 'none' | 'peak' | 'lttb' | 'average' | 'min' | 'max' | 'minmax' | 'sum';
4
+ export interface MultiResolutionSamplingIndexOptions {
5
+ /** Bounds one accessed dataset's lazily-built indexes; source points are not included. */
6
+ maxBytes?: number;
7
+ }
8
+ export declare class MultiResolutionSamplingIndex {
9
+ private readonly points;
10
+ private readonly extremaLayers;
11
+ private readonly sumLayers;
12
+ private indexBytes;
13
+ private readonly maxBytes;
14
+ constructor(points: readonly WaveformPoint[], options?: MultiResolutionSamplingIndexOptions);
15
+ get byteLength(): number;
16
+ dispose(): void;
17
+ sample(start: number, end: number, strategy: IndexedSamplingStrategy, requestedTarget: number): WorkerSamplingOutput | undefined;
18
+ private allIndexes;
19
+ private indexes;
20
+ private extrema;
21
+ private aggregate;
22
+ private queryExtrema;
23
+ private querySum;
24
+ private ensureExtrema;
25
+ private ensureSum;
26
+ private reserve;
27
+ private isMinimum;
28
+ private isMaximum;
29
+ }
@@ -0,0 +1,153 @@
1
+ import type { WaveformPoint, WaveformLineType, WaveformPointType, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingMode, WaveformSamplingStrategy } from '@/types';
2
+ import type { WorkerSamplingDataset } from '../waveformPointSource';
3
+ export declare const DEFAULT_WORKER_SAMPLING_AUTO_THRESHOLD = 1000;
4
+ export declare const DEFAULT_WORKER_SAMPLING_MAX_POINTS_PER_PIXEL = 4;
5
+ export type WorkerSamplingBackendKind = WaveformSamplingBackend;
6
+ export type WorkerSamplingStatus = 'ok' | 'stale-revision' | 'wasm-unavailable' | 'not-found';
7
+ export interface WorkerSamplingDatasetMetrics {
8
+ inputPointCount: number;
9
+ validPointCount: number;
10
+ xDomain: [number, number] | null;
11
+ yDomain: [number, number] | null;
12
+ /** Current lazily-built multiresolution index allocation for this dataset. */
13
+ indexBytes?: number;
14
+ }
15
+ export interface WorkerSamplingSourceIndexes {
16
+ kind: 'source-indexes';
17
+ sourceIndexes: Uint32Array;
18
+ }
19
+ export interface WorkerSamplingAggregates {
20
+ kind: 'aggregates';
21
+ x: Float64Array;
22
+ y: Float64Array;
23
+ }
24
+ export type WorkerSamplingOutput = WorkerSamplingSourceIndexes | WorkerSamplingAggregates;
25
+ export interface WorkerSamplingBackendRequest {
26
+ x: Float64Array;
27
+ y: Float64Array;
28
+ /** Empty for compact datasets; retained for custom legacy backend compatibility. */
29
+ points: readonly WaveformPoint[];
30
+ strategy: Exclude<WaveformSamplingStrategy, 'auto'>;
31
+ targetPointCount: number;
32
+ }
33
+ export interface WorkerSamplingBackend {
34
+ readonly kind: Extract<WorkerSamplingBackendKind, 'javascript' | 'wasm'>;
35
+ sample(request: WorkerSamplingBackendRequest): WorkerSamplingOutput;
36
+ }
37
+ export type WorkerSamplingDiagnostics = WaveformSamplingDiagnostics;
38
+ export interface RegisterDatasetRequest {
39
+ type: 'register-dataset';
40
+ requestId: number;
41
+ datasetId: string;
42
+ revision: 0;
43
+ /** Legacy object points remain accepted for repository consumers. */
44
+ points?: readonly WaveformPoint[];
45
+ /** Compact numeric columns used by TypedArray chart inputs. */
46
+ dataset?: WorkerSamplingDataset;
47
+ }
48
+ export interface ReplaceDatasetRequest {
49
+ type: 'replace-dataset';
50
+ requestId: number;
51
+ datasetId: string;
52
+ revision: number;
53
+ points?: readonly WaveformPoint[];
54
+ dataset?: WorkerSamplingDataset;
55
+ }
56
+ export interface WorkerSamplingSeriesRequest {
57
+ seriesId: string;
58
+ datasetId: string;
59
+ revision: number;
60
+ xDomain: [number, number];
61
+ /** Optional main-thread hint; the Worker recomputes this from xDomain before sampling. */
62
+ visibleStartIndex?: number;
63
+ /** Exclusive visible-range endpoint. */
64
+ visibleEndIndex?: number;
65
+ plotWidth: number;
66
+ mode: WaveformSamplingMode;
67
+ autoThreshold?: number;
68
+ /** Interaction-only hysteresis. Omit previousSelectedMode after the interaction for exact bounds. */
69
+ autoHysteresis?: number;
70
+ previousSelectedMode?: 'raw' | 'sampled';
71
+ strategy: WaveformSamplingStrategy;
72
+ maxPointsPerPixel?: number;
73
+ rawPointLimit?: number;
74
+ wasmFailureFallback?: 'error' | 'javascript';
75
+ /** Rendering inputs that affect the cache key, even when only line geometry is overridden. */
76
+ lineType?: WaveformLineType;
77
+ pointType?: WaveformPointType;
78
+ errorBarVisible?: boolean;
79
+ pointMinSpacing?: number;
80
+ errorBarMinSpacing?: number;
81
+ }
82
+ export interface SampleViewportRequest {
83
+ type: 'sample-viewport';
84
+ requestId: number;
85
+ series: readonly WorkerSamplingSeriesRequest[];
86
+ }
87
+ export interface FindNearestPointRequest {
88
+ type: 'find-nearest-point';
89
+ requestId: number;
90
+ datasetId: string;
91
+ revision: number;
92
+ x: number;
93
+ }
94
+ export interface GetDatasetMetricsRequest {
95
+ type: 'get-dataset-metrics';
96
+ requestId: number;
97
+ datasetId: string;
98
+ revision: number;
99
+ }
100
+ export interface DisposeDatasetRequest {
101
+ type: 'dispose-dataset';
102
+ requestId: number;
103
+ datasetId: string;
104
+ revision: number;
105
+ }
106
+ export interface DisposeAllRequest {
107
+ type: 'dispose-all';
108
+ requestId: number;
109
+ }
110
+ export type WorkerSamplingRequest = RegisterDatasetRequest | ReplaceDatasetRequest | SampleViewportRequest | FindNearestPointRequest | GetDatasetMetricsRequest | DisposeDatasetRequest | DisposeAllRequest;
111
+ export interface DatasetResponse {
112
+ type: 'dataset-response';
113
+ requestId: number;
114
+ datasetId: string;
115
+ revision: number;
116
+ status: WorkerSamplingStatus;
117
+ metrics?: WorkerSamplingDatasetMetrics;
118
+ }
119
+ export interface WorkerSamplingSeriesResponse {
120
+ seriesId: string;
121
+ datasetId: string;
122
+ revision: number;
123
+ status: WorkerSamplingStatus;
124
+ output?: WorkerSamplingOutput;
125
+ diagnostics: WorkerSamplingDiagnostics;
126
+ }
127
+ export interface SampleViewportResponse {
128
+ type: 'sample-viewport-response';
129
+ requestId: number;
130
+ results: readonly WorkerSamplingSeriesResponse[];
131
+ /** Set by the worker when WASM initialization failed and a fallback was selected. */
132
+ workerError?: string;
133
+ }
134
+ export interface FindNearestPointResponse {
135
+ type: 'find-nearest-point-response';
136
+ requestId: number;
137
+ datasetId: string;
138
+ revision: number;
139
+ status: WorkerSamplingStatus;
140
+ point?: WaveformPoint;
141
+ }
142
+ export interface DisposeAllResponse {
143
+ type: 'dispose-all-response';
144
+ requestId: number;
145
+ disposedDatasetIds: readonly string[];
146
+ }
147
+ export type WorkerSamplingResponse = DatasetResponse | SampleViewportResponse | FindNearestPointResponse | DisposeAllResponse;
148
+ export interface WorkerSamplingResponseExpectation {
149
+ requestId: number;
150
+ revisions: Readonly<Record<string, number>>;
151
+ }
152
+ /** Rejects responses from an earlier request or an earlier data revision before UI state is updated. */
153
+ export declare function isCurrentWorkerSamplingResponse(response: WorkerSamplingResponse, expectation: WorkerSamplingResponseExpectation): boolean;
@@ -0,0 +1,36 @@
1
+ import { type WorkerSamplingBackend, type WorkerSamplingRequest, type WorkerSamplingResponse } from './protocol';
2
+ import { type SamplingCacheMetrics } from './samplingCache';
3
+ export interface WorkerSamplingResourceMetrics {
4
+ datasetCount: number;
5
+ indexBytes: number;
6
+ cache: SamplingCacheMetrics;
7
+ }
8
+ export interface WorkerSamplingRepositoryOptions {
9
+ javascriptBackend?: WorkerSamplingBackend;
10
+ wasmBackend?: WorkerSamplingBackend;
11
+ indexMaxBytes?: number;
12
+ cacheMaxEntries?: number;
13
+ cacheMaxBytes?: number;
14
+ }
15
+ export declare class WorkerSamplingRepository {
16
+ private readonly datasets;
17
+ private readonly javascriptBackend;
18
+ private readonly cache;
19
+ private readonly indexMaxBytes;
20
+ private wasmBackend;
21
+ constructor(options?: WorkerSamplingRepositoryOptions);
22
+ get resourceMetrics(): WorkerSamplingResourceMetrics;
23
+ /** Enables the numeric backend after the worker has initialized its WASM module. */
24
+ setWasmBackend(backend: WorkerSamplingBackend | undefined): void;
25
+ handle(request: WorkerSamplingRequest): WorkerSamplingResponse;
26
+ private register;
27
+ private replace;
28
+ private sample;
29
+ private sampleSeries;
30
+ private sampleResponse;
31
+ private findNearest;
32
+ private metrics;
33
+ private dispose;
34
+ private disposeAll;
35
+ private datasetResponse;
36
+ }
@@ -0,0 +1,29 @@
1
+ import type { WaveformPoint } from '@/types';
2
+ import type { WorkerSamplingDataset } from '../waveformPointSource';
3
+ import type { WasmDatasetSamplingBackend } from '../wasmSampling';
4
+ import { MultiResolutionSamplingIndex } from './multiresolution';
5
+ import type { WorkerSamplingBackend, WorkerSamplingDatasetMetrics } from './protocol';
6
+ export interface StoredDataset {
7
+ x?: Float64Array;
8
+ y: Float64Array;
9
+ sampleStartTime?: number;
10
+ sampleRate?: number;
11
+ sampleSourceIndexes?: Uint32Array;
12
+ points?: WaveformPoint[];
13
+ revision: number;
14
+ metrics: WorkerSamplingDatasetMetrics;
15
+ index?: MultiResolutionSamplingIndex;
16
+ wasmDatasetHandle?: number;
17
+ wasmIndexBytes?: number;
18
+ }
19
+ export declare function datasetLength(dataset: StoredDataset | undefined): number;
20
+ export declare function datasetXAt(dataset: StoredDataset, index: number): number;
21
+ export declare function datasetVisibleRange(dataset: StoredDataset, domain: [number, number]): {
22
+ start: number;
23
+ end: number;
24
+ };
25
+ export declare function datasetXRange(dataset: StoredDataset, start: number, end: number): Float64Array<ArrayBufferLike>;
26
+ export declare function isWasmDatasetBackend(backend: WorkerSamplingBackend | undefined): backend is WasmDatasetSamplingBackend;
27
+ export declare function createStoredDataset(input: readonly WaveformPoint[] | undefined, numeric: WorkerSamplingDataset | undefined, revision: number, indexMaxBytes: number | undefined): StoredDataset;
28
+ export declare function registerWasmDataset(backend: WorkerSamplingBackend | undefined, dataset: StoredDataset, indexMaxBytes: number | undefined): void;
29
+ export declare function disposeDatasetIndexes(backend: WorkerSamplingBackend | undefined, dataset: StoredDataset): void;
@@ -0,0 +1,27 @@
1
+ import type { WaveformPoint } from '@/types';
2
+ import { type WorkerSamplingBackendKind, type WorkerSamplingDatasetMetrics, type WorkerSamplingDiagnostics, type WorkerSamplingOutput, type WorkerSamplingSeriesRequest, type WorkerSamplingStatus } from './protocol';
3
+ export declare function copyFinitePoints(points: readonly WaveformPoint[]): WaveformPoint[];
4
+ export declare function metricsFor(points: readonly WaveformPoint[], inputPointCount: number): WorkerSamplingDatasetMetrics;
5
+ export declare function metricsForValues(x: Float64Array, y: Float32Array | Float64Array, inputPointCount?: number): WorkerSamplingDatasetMetrics;
6
+ export declare function statusFor<T extends {
7
+ revision: number;
8
+ }>(dataset: T | undefined, revision: number): WorkerSamplingStatus;
9
+ export declare function selectedStrategy(strategy: WorkerSamplingSeriesRequest['strategy']): "none" | "peak" | "lttb" | "average" | "min" | "max" | "minmax" | "sum";
10
+ export declare function targetPointCount(request: WorkerSamplingSeriesRequest): number;
11
+ export declare function shouldUseRaw(request: WorkerSamplingSeriesRequest, visiblePointCount: number): boolean;
12
+ export declare function visibleRange(points: readonly WaveformPoint[], domain: [number, number]): {
13
+ start: number;
14
+ end: number;
15
+ };
16
+ export declare function visibleValueRange(x: Float64Array, domain: [number, number]): {
17
+ start: number;
18
+ end: number;
19
+ };
20
+ export declare function nearestPoint(points: readonly WaveformPoint[], x: number): WaveformPoint | undefined;
21
+ export declare function outputCount(output: WorkerSamplingOutput | undefined): number;
22
+ export declare function outputWithGlobalIndexes(output: WorkerSamplingOutput, offset: number): WorkerSamplingOutput;
23
+ export declare function cacheKey(request: WorkerSamplingSeriesRequest, range: {
24
+ start: number;
25
+ end: number;
26
+ }, strategy: ReturnType<typeof selectedStrategy>, target: number, backend: WorkerSamplingBackendKind): string;
27
+ export declare function diagnosticsFor(request: WorkerSamplingSeriesRequest, sourcePointCount: number, visiblePointCount: number, backend: WorkerSamplingBackendKind, selectedMode: 'raw' | 'sampled', durationMs: number, renderedPointCount: number, cacheHit: boolean): WorkerSamplingDiagnostics;
@@ -0,0 +1,24 @@
1
+ import type { WorkerSamplingOutput } from './protocol';
2
+ export interface SamplingCacheMetrics {
3
+ entries: number;
4
+ bytes: number;
5
+ maxEntries: number;
6
+ maxBytes: number;
7
+ }
8
+ export interface SamplingOutputCacheOptions {
9
+ maxEntries?: number;
10
+ maxBytes?: number;
11
+ }
12
+ /** A bounded LRU for immutable Worker sampling outputs. */
13
+ export declare class SamplingOutputCache {
14
+ private readonly entries;
15
+ private bytes;
16
+ private readonly maxEntries;
17
+ private readonly maxBytes;
18
+ constructor(options?: SamplingOutputCacheOptions);
19
+ get metrics(): SamplingCacheMetrics;
20
+ get(key: string): WorkerSamplingOutput | undefined;
21
+ set(key: string, output: WorkerSamplingOutput): void;
22
+ deleteDataset(datasetId: string): void;
23
+ clear(): void;
24
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -3,9 +3,9 @@
3
3
  * @packageDocumentation
4
4
  */
5
5
  export { default as WaveformChart } from './components/WaveformChart.vue';
6
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
6
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformTypedValues, TypedSampleData, TypedPointData, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
7
7
  export type { WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './components/core/grid';
8
8
  export { normalizeWaveformData, normalizeWaveformSeries } from './core';
9
9
  export { paddedDomain, buildMinorTicks, displayTime, formatEndpointTime, formatAxisTime, formatTooltipTime, resolveTrackGeometry, clamp, type TimeUnit, type TrackGeometry, } from './utils';
10
- export { DEFAULT_WAVEFORM_RENDERING_OPTIONS, resolveWaveformRenderingOptions, selectRenderablePoints, type ResolvedWaveformRenderingOptions, } from './core';
10
+ export { calculateWasmRange, findWasmVisibleRange, DEFAULT_WAVEFORM_RENDERING_OPTIONS, resolveWaveformRenderingOptions, selectRenderablePoints, type ResolvedWaveformRenderingOptions, type ResolvedWaveformSamplingOptions, type WasmRangeMetrics, initializeWasmSampling, sampleWaveformWasm, type WasmSamplingRequest, type WasmSamplingResult, type WasmSamplingStrategy, } from './core';
11
11
  export { parseWaveformAnnotations, serializeWaveformAnnotations } from './components/annotation';
@@ -68,8 +68,64 @@ export interface WaveformAnnotation {
68
68
  style?: WaveformAnnotationStyle;
69
69
  createdAt?: string;
70
70
  }
71
+ /** Selects the execution path used to obtain points for waveform rendering. */
72
+ export type WaveformSamplingMode = 'auto' | 'wasm' | 'raw';
73
+ /** Selects the point-reduction algorithm used by the sampling backend. */
74
+ export type WaveformSamplingStrategy = 'auto' | 'none' | 'peak' | 'lttb' | 'average' | 'min' | 'max' | 'minmax' | 'sum';
75
+ /** Controls how waveform points are selected for rendering. */
76
+ export interface WaveformSamplingOptions {
77
+ /** Defaults to `auto`. */
78
+ mode?: WaveformSamplingMode;
79
+ /** Per-series visible-point threshold used by auto mode. Defaults to 1,000. */
80
+ autoThreshold?: number;
81
+ /** Interaction-only hysteresis around the automatic threshold. Defaults to 0. */
82
+ autoHysteresis?: number;
83
+ /** Defaults to the peak-preserving strategy. */
84
+ strategy?: WaveformSamplingStrategy;
85
+ /** Upper bound for rendered points per horizontal CSS pixel. */
86
+ maxPointsPerPixel?: number;
87
+ /** Visible-point count at which raw rendering should report a diagnostic. */
88
+ rawPointLimit?: number;
89
+ /** Controls whether a forced WASM failure may temporarily show JavaScript results. */
90
+ wasmFailureFallback?: 'error' | 'javascript';
91
+ }
92
+ /** The backend that produced a series' current SVG geometry. */
93
+ export type WaveformSamplingBackend = 'raw' | 'javascript' | 'wasm' | 'unavailable';
94
+ /** Read-only details emitted after a sampling request settles. */
95
+ export interface WaveformSamplingDiagnostics {
96
+ seriesId: string;
97
+ /** Internal dataset identity, useful when correlating request logs. */
98
+ datasetId: string;
99
+ mode: WaveformSamplingMode;
100
+ selectedMode: 'raw' | 'sampled';
101
+ backend: WaveformSamplingBackend;
102
+ strategy: Exclude<WaveformSamplingStrategy, 'auto'>;
103
+ sourcePointCount: number;
104
+ visiblePointCount: number;
105
+ renderedPointCount: number;
106
+ durationMs: number;
107
+ cacheHit: boolean;
108
+ requestId: number;
109
+ revision: number;
110
+ rawPointLimitExceeded: boolean;
111
+ /** Cumulative sampling runs scheduled by this chart instance. */
112
+ scheduledRequestCount: number;
113
+ /** Cumulative intermediate runs replaced before they entered the Worker queue. */
114
+ coalescedRequestCount: number;
115
+ /** Maximum retained pending runs; bounded to one by the latest-wins scheduler. */
116
+ maxPendingRequestCount: number;
117
+ }
118
+ /** Describes a sampling backend failure without exposing an implementation-specific Error object. */
119
+ export interface WaveformSamplingError {
120
+ message: string;
121
+ mode: WaveformSamplingMode;
122
+ fallback: 'javascript' | 'none';
123
+ seriesIds: string[];
124
+ }
71
125
  /** Controls how many source points are included in the rendered SVG path. */
72
126
  export interface WaveformRenderingOptions {
127
+ /** Sampling execution and strategy configuration. Takes precedence over legacy fields. */
128
+ sampling?: WaveformSamplingOptions;
73
129
  /** Disable only when every source point must be rendered. */
74
130
  downsample?: boolean;
75
131
  /** Visible point count below which the complete range is rendered. */
@@ -16,8 +16,26 @@ export interface ResolvedWaveformErrorBarOptions {
16
16
  width: number;
17
17
  capWidth: number;
18
18
  }
19
+ /** Numeric storage accepted by the compact waveform input formats. */
20
+ export type WaveformTypedValues = Float32Array | Float64Array;
21
+ /** Compact, evenly spaced samples. X is derived as `startTime + index / sampleRate`. */
22
+ export interface TypedSampleData {
23
+ kind: 'typed-samples';
24
+ values: WaveformTypedValues;
25
+ sampleRate: number;
26
+ startTime?: number;
27
+ }
28
+ /** Compact points with explicit X coordinates. X uses Float64 precision by contract. */
29
+ export interface TypedPointData {
30
+ kind: 'typed-points';
31
+ x: Float64Array;
32
+ y: WaveformTypedValues;
33
+ error?: WaveformTypedValues;
34
+ lowerError?: WaveformTypedValues;
35
+ upperError?: WaveformTypedValues;
36
+ }
19
37
  /**
20
- * 单波形数据格式(采样点或显式坐标点)
38
+ * 单波形数据格式(对象数组或紧凑 TypedArray)
21
39
  */
22
40
  export type SingleWaveformData = {
23
41
  kind: 'samples';
@@ -27,7 +45,7 @@ export type SingleWaveformData = {
27
45
  } | {
28
46
  kind: 'points';
29
47
  points: WaveformPoint[];
30
- };
48
+ } | TypedSampleData | TypedPointData;
31
49
  /**
32
50
  * 波形系列
33
51
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * 类型定义统一导出
3
3
  */
4
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
5
- export type { SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './data';
4
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
5
+ export type { SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformTypedValues, TypedSampleData, TypedPointData, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './data';
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waveform-analysis",
3
- "version": "0.1.48",
3
+ "version": "0.1.51",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -55,18 +55,19 @@
55
55
  "scripts": {
56
56
  "dev": "vite",
57
57
  "build": "pnpm typecheck && pnpm build:lib && pnpm build:types && pnpm build:demo",
58
+ "build:wasm": "wasm-pack build wasm --target web --release --out-dir pkg --locked",
58
59
  "build:lib": "vite build --config vite.lib.config.ts",
59
60
  "build:types": "node scripts/clean-types.mjs && vue-tsc -p tsconfig.lib.json",
60
61
  "build:demo": "vite build",
61
62
  "preview": "vite preview --outDir dist-demo",
62
- "typecheck": "vue-tsc -b",
63
+ "typecheck": "pnpm build:wasm && vue-tsc -b",
63
64
  "check:file-length": "node scripts/check-file-length.mjs",
64
65
  "lint": "eslint . --max-warnings=0",
65
66
  "lint:oxlint": "oxlint . --deny-warnings",
66
67
  "lint:all": "pnpm lint:oxlint && pnpm lint",
67
68
  "format": "prettier --write .",
68
69
  "format:check": "prettier --check .",
69
- "test": "vitest run",
70
- "test:coverage": "vitest run --coverage"
70
+ "test": "pnpm build:wasm && vitest run",
71
+ "test:coverage": "pnpm build:wasm && vitest run --coverage"
71
72
  }
72
73
  }
package/src/App.test.ts CHANGED
@@ -210,12 +210,13 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
210
210
  '正弦基波',
211
211
  ])
212
212
 
213
- const triangleSeries = firstFrame.get('.waveform-chart__series[data-series-name="正弦基波"]')
214
- expect(triangleSeries.find('.waveform-chart__line').exists()).toBe(false)
215
- expect(triangleSeries.get('.waveform-chart__points').attributes('data-point-type')).toBe(
216
- 'triangle',
217
- )
218
- expect(triangleSeries.get('.waveform-chart__error-bar').attributes('stroke')).toBe('#0960bd')
213
+ const sineSeries = firstFrame.get('.waveform-chart__series[data-series-name="正弦基波"]')
214
+ expect(sineSeries.find('.waveform-chart__line').exists()).toBe(true)
215
+ expect(sineSeries.get('.waveform-chart__points').attributes('data-point-type')).toBe('circle')
216
+ expect(sineSeries.get('.waveform-chart__error-bar').attributes()).toMatchObject({
217
+ stroke: '#0960bd',
218
+ 'stroke-width': '1.5',
219
+ })
219
220
 
220
221
  wrapper.unmount()
221
222
  })
@@ -318,7 +319,10 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
318
319
 
319
320
  expect(colorPickers).toHaveLength(2)
320
321
  const initialFrames = wrapper.findAll('.waveform-chart__plot-frame')
321
- expect(initialFrames.length > 1 && initialFrames.every((frame) => frame.attributes('stroke-width') === '2')).toBe(true)
322
+ expect(
323
+ initialFrames.length > 1 &&
324
+ initialFrames.every((frame) => frame.attributes('stroke-width') === '2'),
325
+ ).toBe(true)
322
326
 
323
327
  colorPickers[0].vm.$emit('update:pureColor', 'rgba(220, 38, 38, 0.8)')
324
328
  colorPickers[1].vm.$emit('update:pureColor', 'rgba(14, 165, 233, 0.25)')
package/src/App.vue CHANGED
@@ -202,8 +202,8 @@ let zoomRequestSequence = 0
202
202
  function filterWaveformData(data: WaveformData, start: number, end: number): WaveformData {
203
203
  const lower = Math.min(start, end)
204
204
  const upper = Math.max(start, end)
205
- if (data.kind === 'samples') return data
206
- if (data.kind === 'points') {
205
+ if (data.kind !== 'series') {
206
+ if (data.kind !== 'points') return data
207
207
  return {
208
208
  kind: 'points',
209
209
  points: data.points.filter((point) => point.x >= lower && point.x <= upper),
@@ -5,6 +5,7 @@
5
5
  <nav class="demo-shell__nav" aria-label="示例导航">
6
6
  <RouterLink to="/">综合示例</RouterLink>
7
7
  <RouterLink to="/fixed-y-domain">固定振幅范围</RouterLink>
8
+ <RouterLink to="/wasm-sampling">WASM 采样</RouterLink>
8
9
  </nav>
9
10
  </header>
10
11
  <div class="demo-shell__content">
@@ -1,3 +1,5 @@
1
+ @import 'vue3-colorpicker/style.css';
2
+
1
3
  .waveform-chart {
2
4
  box-sizing: border-box;
3
5
  position: relative;
@@ -8,8 +8,8 @@
8
8
  :global(.waveform-annotation-editor-root) {
9
9
  color: #344054;
10
10
  font-family:
11
- -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
12
- 'Noto Sans', sans-serif;
11
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
12
+ sans-serif;
13
13
  line-height: 1.5715;
14
14
  text-align: left;
15
15
  text-transform: none;
@@ -1,8 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { computed, nextTick, ref, watch } from 'vue'
2
+ import { computed, defineAsyncComponent, nextTick, ref, watch, type Component } from 'vue'
3
3
  import { InputNumber, Modal } from 'ant-design-vue'
4
- import { ColorPicker } from 'vue3-colorpicker'
5
- import 'vue3-colorpicker/style.css'
6
4
 
7
5
  import type { WaveformAnnotation } from '../../types'
8
6
  import { formatAnnotationTime, formatPlainNumber, type TimeUnit } from '../../utils'
@@ -10,6 +8,13 @@ import { ANNOTATION_MAX_TEXT_LENGTH, resolveAnnotationStyle } from './markup'
10
8
  import type { AnnotationSeriesCandidate, AnnotationSeriesInfo } from './types'
11
9
  import { useWaveformInstanceId } from '../../utils/waveformId'
12
10
 
11
+ const ColorPicker = defineAsyncComponent(async (): Promise<Component> => {
12
+ if (typeof window === 'undefined') {
13
+ return { name: 'WaveformColorPickerSsrFallback', render: () => null }
14
+ }
15
+ return (await import('vue3-colorpicker')).ColorPicker
16
+ })
17
+
13
18
  interface Props {
14
19
  annotation: WaveformAnnotation
15
20
  mode: 'add' | 'edit'
@@ -256,7 +261,6 @@ function handleSeriesChange(event: Event) {
256
261
  />
257
262
  </label>
258
263
  </fieldset>
259
-
260
264
  </div>
261
265
  </Modal>
262
266
  </template>