waveform-analysis 0.1.47 → 0.1.50

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 (122) hide show
  1. package/README.md +127 -5
  2. package/dist/assets/workerRuntime-Dt94jbqq.js +1 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.js +2701 -1165
  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/layout.d.ts +2 -2
  12. package/dist/types/components/core/trackLayoutBuilder.d.ts +4 -1
  13. package/dist/types/components/core/types.d.ts +2 -0
  14. package/dist/types/components/core/useWaveformChartController.d.ts +1 -0
  15. package/dist/types/components/core/useWaveformData.d.ts +2 -0
  16. package/dist/types/components/core/useWaveformLayout.d.ts +2 -0
  17. package/dist/types/components/core/useWaveformRenderSampling.d.ts +19 -0
  18. package/dist/types/components/core/waveformChartTypes.d.ts +8 -1
  19. package/dist/types/components/core/workerSamplingClient.d.ts +8 -0
  20. package/dist/types/components/data/types.d.ts +1 -1
  21. package/dist/types/components/interaction/zoomConstraints.d.ts +2 -0
  22. package/dist/types/components/waveform.d.ts +1 -1
  23. package/dist/types/core/compactSamplePointSource.d.ts +3 -0
  24. package/dist/types/core/data.d.ts +7 -1
  25. package/dist/types/core/index.d.ts +2 -1
  26. package/dist/types/core/rendering.d.ts +3 -1
  27. package/dist/types/core/renderingOptions.d.ts +11 -1
  28. package/dist/types/core/samplingReference.d.ts +36 -0
  29. package/dist/types/core/samplingReferenceValues.d.ts +4 -0
  30. package/dist/types/core/samplingReferenceVectors.d.ts +17 -0
  31. package/dist/types/core/wasmSampling.d.ts +48 -0
  32. package/dist/types/core/waveformPointSource.d.ts +53 -0
  33. package/dist/types/core/workerSampling/index.d.ts +5 -0
  34. package/dist/types/core/workerSampling/javascriptBackend.d.ts +3 -0
  35. package/dist/types/core/workerSampling/multiresolution.d.ts +29 -0
  36. package/dist/types/core/workerSampling/protocol.d.ts +153 -0
  37. package/dist/types/core/workerSampling/repository.d.ts +36 -0
  38. package/dist/types/core/workerSampling/repositoryDataset.d.ts +29 -0
  39. package/dist/types/core/workerSampling/repositorySupport.d.ts +27 -0
  40. package/dist/types/core/workerSampling/samplingCache.d.ts +24 -0
  41. package/dist/types/core/workerSampling/workerRuntime.d.ts +1 -0
  42. package/dist/types/index.d.ts +2 -2
  43. package/dist/types/types/chart.d.ts +58 -0
  44. package/dist/types/types/data.d.ts +20 -2
  45. package/dist/types/types/index.d.ts +2 -2
  46. package/dist/types/views/WasmSamplingDemo.vue.d.ts +3 -0
  47. package/package.json +5 -4
  48. package/src/App.test.ts +11 -7
  49. package/src/App.vue +2 -2
  50. package/src/DemoRouterApp.vue +1 -0
  51. package/src/components/WaveformChart.css +2 -0
  52. package/src/components/annotation/WaveformAnnotationEditor.css +2 -2
  53. package/src/components/annotation/WaveformAnnotationEditor.vue +8 -4
  54. package/src/components/annotation/markup.ts +45 -26
  55. package/src/components/annotation/types.ts +2 -0
  56. package/src/components/annotation/useWaveformChartAnnotations.ts +12 -15
  57. package/src/components/core/latestTaskScheduler.test.ts +51 -0
  58. package/src/components/core/latestTaskScheduler.ts +75 -0
  59. package/src/components/core/layout.ts +15 -6
  60. package/src/components/core/trackLayoutBuilder.ts +8 -2
  61. package/src/components/core/types.ts +2 -0
  62. package/src/components/core/useWaveformChartController.ts +14 -0
  63. package/src/components/core/useWaveformData.test.ts +95 -0
  64. package/src/components/core/useWaveformData.ts +23 -32
  65. package/src/components/core/useWaveformLayout.ts +16 -10
  66. package/src/components/core/useWaveformRenderSampling.test.ts +17 -0
  67. package/src/components/core/useWaveformRenderSampling.ts +394 -0
  68. package/src/components/core/waveformChartTypes.ts +13 -0
  69. package/src/components/core/workerSamplingClient.ts +148 -0
  70. package/src/components/core/yAxisNice.test.ts +174 -0
  71. package/src/components/data/types.ts +9 -0
  72. package/src/components/interaction/WaveformTooltip.test.ts +1 -3
  73. package/src/components/interaction/WaveformTooltip.vue +13 -12
  74. package/src/components/interaction/useWaveformHover.ts +7 -7
  75. package/src/components/interaction/zoomConstraints.ts +5 -1
  76. package/src/components/waveform.ts +9 -0
  77. package/src/components/waveformChartCases/compactAndSharedHover.test.ts +15 -4
  78. package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +11 -6
  79. package/src/components/waveformChartCases/dataLifecycle.test.ts +1 -3
  80. package/src/components/waveformChartCases/trackOrder.test.ts +130 -0
  81. package/src/components/waveformChartCases/workerSamplingIntegration.test.ts +117 -0
  82. package/src/components/waveformChartCases/xDomainStrategy.test.ts +14 -2
  83. package/src/core/compactSamplePointSource.ts +76 -0
  84. package/src/core/data.test.ts +114 -0
  85. package/src/core/data.ts +177 -75
  86. package/src/core/index.ts +11 -0
  87. package/src/core/rendering.test.ts +87 -12
  88. package/src/core/rendering.ts +35 -1
  89. package/src/core/renderingOptions.ts +124 -15
  90. package/src/core/samplingReference.test.ts +124 -0
  91. package/src/core/samplingReference.ts +243 -0
  92. package/src/core/samplingReferenceValues.ts +169 -0
  93. package/src/core/samplingReferenceVectors.ts +46 -0
  94. package/src/core/wasmSampling.test.ts +205 -0
  95. package/src/core/wasmSampling.ts +230 -0
  96. package/src/core/waveformPointSource.ts +395 -0
  97. package/src/core/workerSampling/index.ts +30 -0
  98. package/src/core/workerSampling/javascriptBackend.ts +15 -0
  99. package/src/core/workerSampling/multiresolution.test.ts +69 -0
  100. package/src/core/workerSampling/multiresolution.ts +300 -0
  101. package/src/core/workerSampling/protocol.test.ts +34 -0
  102. package/src/core/workerSampling/protocol.ts +207 -0
  103. package/src/core/workerSampling/repository.test.ts +400 -0
  104. package/src/core/workerSampling/repository.ts +400 -0
  105. package/src/core/workerSampling/repositoryCache.test.ts +163 -0
  106. package/src/core/workerSampling/repositoryDataset.ts +194 -0
  107. package/src/core/workerSampling/repositorySupport.ts +225 -0
  108. package/src/core/workerSampling/repositoryTypedDataset.test.ts +89 -0
  109. package/src/core/workerSampling/repositoryWasmDataset.test.ts +70 -0
  110. package/src/core/workerSampling/samplingCache.ts +94 -0
  111. package/src/core/workerSampling/workerRuntime.ts +40 -0
  112. package/src/data/simulatedWaveforms.test.ts +19 -0
  113. package/src/data/simulatedWaveforms.ts +11 -8
  114. package/src/demo/xAxisLabelControls.test.ts +1 -1
  115. package/src/demoRoutes.css +177 -0
  116. package/src/index.ts +18 -0
  117. package/src/router.ts +5 -0
  118. package/src/types/chart.ts +65 -0
  119. package/src/types/data.ts +24 -1
  120. package/src/types/index.ts +9 -0
  121. package/src/views/WasmSamplingDemo.test.ts +13 -0
  122. package/src/views/WasmSamplingDemo.vue +180 -0
@@ -0,0 +1,40 @@
1
+ import { createInitializedWasmSamplingBackend, initializeWasmSampling } from '../wasmSampling'
2
+ import { WorkerSamplingRepository } from './repository'
3
+ import type { WorkerSamplingRequest, WorkerSamplingResponse } from './protocol'
4
+
5
+ const repository = new WorkerSamplingRepository()
6
+ let wasmInitialization: Promise<void> | undefined
7
+ let wasmInitializationError: string | undefined
8
+
9
+ function requiresWasm(request: WorkerSamplingRequest) {
10
+ return (
11
+ request.type === 'sample-viewport' && request.series.some((series) => series.mode !== 'raw')
12
+ )
13
+ }
14
+
15
+ async function initializeWasmBackend() {
16
+ if (wasmInitializationError) return
17
+ wasmInitialization ??= initializeWasmSampling()
18
+ .then(() => {
19
+ repository.setWasmBackend(createInitializedWasmSamplingBackend())
20
+ })
21
+ .catch((error: unknown) => {
22
+ wasmInitializationError = error instanceof Error ? error.message : String(error)
23
+ })
24
+ await wasmInitialization
25
+ }
26
+
27
+ function addWasmError(response: WorkerSamplingResponse): WorkerSamplingResponse {
28
+ if (response.type !== 'sample-viewport-response' || !wasmInitializationError) return response
29
+ return { ...response, workerError: wasmInitializationError }
30
+ }
31
+
32
+ const workerScope = globalThis as typeof globalThis & {
33
+ onmessage: ((event: MessageEvent<WorkerSamplingRequest>) => void) | null
34
+ postMessage: (message: WorkerSamplingResponse) => void
35
+ }
36
+
37
+ workerScope.onmessage = async (event: MessageEvent<WorkerSamplingRequest>) => {
38
+ if (requiresWasm(event.data)) await initializeWasmBackend()
39
+ workerScope.postMessage(addWasmError(repository.handle(event.data)))
40
+ }
@@ -18,6 +18,25 @@ describe('simulated waveform data', () => {
18
18
  )
19
19
  expect(secondFrame).toHaveLength(2)
20
20
  expect(new Set(first.series.map((series) => series.shotNo))).toEqual(new Set(['13300']))
21
+ const firstSeries = first.series[0]
22
+ expect(firstSeries).toMatchObject({
23
+ id: 'simulated-sine',
24
+ lineType: 'linear',
25
+ pointType: 'circle',
26
+ errorBar: { visible: true, width: 1.5, capWidth: 10 },
27
+ })
28
+ if (firstSeries?.data.kind === 'points') {
29
+ const lowerErrors = firstSeries.data.points.map((point) => point.lowerError)
30
+ const upperErrors = firstSeries.data.points.map((point) => point.upperError)
31
+ expect(
32
+ lowerErrors.every((error) => error !== undefined && error >= 0.05 && error < 0.5),
33
+ ).toBe(true)
34
+ expect(
35
+ upperErrors.every((error) => error !== undefined && error >= 0.05 && error < 0.5),
36
+ ).toBe(true)
37
+ expect(new Set(lowerErrors).size).toBeGreaterThan(900)
38
+ expect(new Set(upperErrors).size).toBeGreaterThan(900)
39
+ }
21
40
  first.series.forEach((series) => {
22
41
  expect(series.data.kind).toBe('points')
23
42
  if (series.data.kind !== 'points') return
@@ -9,6 +9,7 @@ type SignalGenerator = (time: number, noise: number) => number
9
9
  type ErrorGenerator = (
10
10
  time: number,
11
11
  value: number,
12
+ noise: () => number,
12
13
  ) => Pick<WaveformPoint, 'error' | 'lowerError' | 'upperError'>
13
14
 
14
15
  interface SimulatedSeriesDefinition extends Pick<
@@ -32,6 +33,7 @@ function createPoints(
32
33
  signal: SignalGenerator,
33
34
  noise: () => number,
34
35
  errors?: ErrorGenerator,
36
+ errorNoise: () => number = noise,
35
37
  ): WaveformPoint[] {
36
38
  return Array.from({ length: POINT_COUNT }, (_, index) => {
37
39
  const time = START_TIME + (index * (END_TIME - START_TIME)) / (POINT_COUNT - 1)
@@ -39,7 +41,7 @@ function createPoints(
39
41
  return {
40
42
  x: time,
41
43
  y: value,
42
- ...errors?.(time, value),
44
+ ...errors?.(time, value, errorNoise),
43
45
  }
44
46
  })
45
47
  }
@@ -50,13 +52,13 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
50
52
  shotNo: '13300',
51
53
  name: '正弦基波',
52
54
  unit: 'V',
53
- lineType: 'none',
54
- pointType: 'triangle',
55
- errorBar: { visible: true },
55
+ lineType: 'linear',
56
+ pointType: 'circle',
57
+ errorBar: { visible: true, width: 1.5, capWidth: 10 },
56
58
  signal: (time) => 1.2 * Math.sin(TWO_PI * 0.8 * time),
57
- errors: (time) => ({
58
- lowerError: 0.06 + 0.015 * Math.abs(Math.sin(time)),
59
- upperError: 0.08 + 0.02 * Math.abs(Math.cos(time)),
59
+ errors: (_time, _value, noise) => ({
60
+ lowerError: 0.05 + (noise() + 0.5) * 0.45,
61
+ upperError: 0.05 + (noise() + 0.5) * 0.45,
60
62
  }),
61
63
  },
62
64
  {
@@ -128,10 +130,11 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
128
130
 
129
131
  export function createSimulatedWaveformData(): WaveformData {
130
132
  const noise = createSeededNoise(0x5eed1234)
133
+ const errorNoise = createSeededNoise(0xe220a11)
131
134
  return {
132
135
  kind: 'series',
133
136
  series: seriesDefinitions.map(({ signal, errors, minimumX, ...series }) => {
134
- const points = createPoints(signal, noise, errors)
137
+ const points = createPoints(signal, noise, errors, errorNoise)
135
138
  return {
136
139
  ...series,
137
140
  data: {
@@ -82,5 +82,5 @@ describe('X-axis label demo controls', () => {
82
82
  expect(chart.props('axes')?.x?.labelFormatter).toBeUndefined()
83
83
  expect(wrapper.find('[aria-label="X 轴 Label 运算倍率"]').exists()).toBe(false)
84
84
  wrapper.unmount()
85
- })
85
+ }, 10_000)
86
86
  })
@@ -138,6 +138,116 @@
138
138
  min-height: 0;
139
139
  }
140
140
 
141
+ .sampling-demo {
142
+ display: grid;
143
+ grid-template-rows: auto auto minmax(0, 1fr);
144
+ gap: 12px;
145
+ width: 100%;
146
+ height: 100%;
147
+ padding: 16px;
148
+ overflow: hidden;
149
+ }
150
+
151
+ .sampling-demo__toolbar {
152
+ display: flex;
153
+ gap: 24px;
154
+ align-items: center;
155
+ justify-content: space-between;
156
+ min-width: 0;
157
+ }
158
+
159
+ .sampling-demo__toolbar h1 {
160
+ margin: 0;
161
+ color: #101828;
162
+ font-size: 18px;
163
+ font-weight: 700;
164
+ letter-spacing: 0;
165
+ }
166
+
167
+ .sampling-demo__toolbar p {
168
+ margin: 4px 0 0;
169
+ color: #667085;
170
+ font-size: 12px;
171
+ }
172
+
173
+ .sampling-demo__controls {
174
+ display: flex;
175
+ gap: 16px;
176
+ align-items: center;
177
+ min-height: 48px;
178
+ padding: 8px 12px;
179
+ background: #fff;
180
+ border: 1px solid #e4e7ec;
181
+ border-radius: 6px;
182
+ }
183
+
184
+ .sampling-demo__controls label {
185
+ display: grid;
186
+ grid-template-columns: auto 132px;
187
+ gap: 8px;
188
+ align-items: center;
189
+ color: #475467;
190
+ font-size: 12px;
191
+ }
192
+
193
+ .sampling-demo__workspace {
194
+ display: grid;
195
+ grid-template-columns: minmax(0, 1fr) 450px;
196
+ gap: 12px;
197
+ min-width: 0;
198
+ min-height: 0;
199
+ }
200
+
201
+ .sampling-demo__chart {
202
+ min-width: 0;
203
+ min-height: 0;
204
+ }
205
+
206
+ .sampling-demo__chart > .waveform-chart {
207
+ height: 100%;
208
+ min-height: 0;
209
+ }
210
+
211
+ .sampling-demo__diagnostics {
212
+ min-width: 0;
213
+ overflow: auto;
214
+ background: #fff;
215
+ border: 1px solid #e4e7ec;
216
+ border-radius: 6px;
217
+ }
218
+
219
+ .sampling-demo__diagnostic-header,
220
+ .sampling-demo__diagnostic-row {
221
+ display: grid;
222
+ grid-template-columns: 60px 54px repeat(3, minmax(46px, 1fr)) 55px 48px;
223
+ gap: 5px;
224
+ align-items: center;
225
+ min-width: 405px;
226
+ padding: 9px 10px;
227
+ color: #475467;
228
+ font-size: 11px;
229
+ font-variant-numeric: tabular-nums;
230
+ }
231
+
232
+ .sampling-demo__diagnostic-header {
233
+ position: sticky;
234
+ top: 0;
235
+ z-index: 1;
236
+ color: #667085;
237
+ font-weight: 650;
238
+ background: #f8fafc;
239
+ border-bottom: 1px solid #e4e7ec;
240
+ }
241
+
242
+ .sampling-demo__diagnostic-row + .sampling-demo__diagnostic-row {
243
+ border-top: 1px solid #f0f2f5;
244
+ }
245
+
246
+ .sampling-demo__diagnostic-row .is-hit {
247
+ color: #237804;
248
+ font-weight: 650;
249
+ }
250
+
141
251
  @media (max-width: 900px) {
142
252
  .demo-shell {
143
253
  grid-template-rows: 44px minmax(0, 1fr);
@@ -194,4 +304,71 @@
194
304
  border-top: 1px solid #e4e7ec;
195
305
  border-left: 0;
196
306
  }
307
+
308
+ .sampling-demo {
309
+ gap: 8px;
310
+ padding: 10px;
311
+ }
312
+
313
+ .sampling-demo__toolbar {
314
+ display: grid;
315
+ gap: 8px;
316
+ }
317
+
318
+ .sampling-demo__toolbar .ant-radio-group {
319
+ display: flex;
320
+ width: 100%;
321
+ }
322
+
323
+ .sampling-demo__toolbar .ant-radio-button-wrapper {
324
+ flex: 1 1 0;
325
+ padding-inline: 6px;
326
+ text-align: center;
327
+ }
328
+
329
+ .sampling-demo__controls {
330
+ display: grid;
331
+ grid-template-columns: 1fr 1fr;
332
+ gap: 8px 12px;
333
+ }
334
+
335
+ .sampling-demo__controls label {
336
+ grid-template-columns: 1fr;
337
+ gap: 3px;
338
+ }
339
+
340
+ .sampling-demo__workspace {
341
+ grid-template-columns: minmax(0, 1fr);
342
+ grid-template-rows: minmax(280px, 1fr) 208px;
343
+ }
344
+
345
+ .sampling-demo__diagnostic-header {
346
+ display: none;
347
+ }
348
+
349
+ .sampling-demo__diagnostic-row {
350
+ grid-template-columns: repeat(2, minmax(0, 1fr));
351
+ gap: 7px 12px;
352
+ min-width: 0;
353
+ padding: 10px;
354
+ }
355
+
356
+ .sampling-demo__diagnostic-row span {
357
+ min-width: 0;
358
+ overflow: hidden;
359
+ color: #344054;
360
+ text-overflow: ellipsis;
361
+ white-space: nowrap;
362
+ }
363
+
364
+ .sampling-demo__diagnostic-row span::before {
365
+ display: block;
366
+ color: #667085;
367
+ font-size: 10px;
368
+ content: attr(data-label);
369
+ }
370
+
371
+ .sampling-demo__diagnostic-row span:first-child {
372
+ grid-column: 1 / -1;
373
+ }
197
374
  }
package/src/index.ts CHANGED
@@ -18,6 +18,12 @@ export type {
18
18
  WaveformZoomResetPayload,
19
19
  WaveformAnnotationStyle,
20
20
  WaveformAnnotation,
21
+ WaveformSamplingMode,
22
+ WaveformSamplingStrategy,
23
+ WaveformSamplingOptions,
24
+ WaveformSamplingBackend,
25
+ WaveformSamplingDiagnostics,
26
+ WaveformSamplingError,
21
27
  WaveformRenderingOptions,
22
28
  WaveformPlotMargin,
23
29
  WaveformTitleTextStyle,
@@ -38,6 +44,9 @@ export type {
38
44
  WaveformPointType,
39
45
  WaveformErrorBarOptions,
40
46
  ResolvedWaveformErrorBarOptions,
47
+ WaveformTypedValues,
48
+ TypedSampleData,
49
+ TypedPointData,
41
50
  WaveformSeries,
42
51
  WaveformData,
43
52
  NormalizedWaveformSeries,
@@ -67,10 +76,19 @@ export {
67
76
  } from './utils'
68
77
 
69
78
  export {
79
+ calculateWasmRange,
80
+ findWasmVisibleRange,
70
81
  DEFAULT_WAVEFORM_RENDERING_OPTIONS,
71
82
  resolveWaveformRenderingOptions,
72
83
  selectRenderablePoints,
73
84
  type ResolvedWaveformRenderingOptions,
85
+ type ResolvedWaveformSamplingOptions,
86
+ type WasmRangeMetrics,
87
+ initializeWasmSampling,
88
+ sampleWaveformWasm,
89
+ type WasmSamplingRequest,
90
+ type WasmSamplingResult,
91
+ type WasmSamplingStrategy,
74
92
  } from './core'
75
93
 
76
94
  export { parseWaveformAnnotations, serializeWaveformAnnotations } from './components/annotation'
package/src/router.ts CHANGED
@@ -13,6 +13,11 @@ export const routes: RouteRecordRaw[] = [
13
13
  name: 'fixed-y-domain',
14
14
  component: () => import('./views/FixedYDomainDemo.vue'),
15
15
  },
16
+ {
17
+ path: '/wasm-sampling',
18
+ name: 'wasm-sampling',
19
+ component: () => import('./views/WasmSamplingDemo.vue'),
20
+ },
16
21
  ]
17
22
 
18
23
  export const router = createRouter({
@@ -77,8 +77,71 @@ export interface WaveformAnnotation {
77
77
  createdAt?: string
78
78
  }
79
79
 
80
+ /** Selects the execution path used to obtain points for waveform rendering. */
81
+ export type WaveformSamplingMode = 'auto' | 'wasm' | 'raw'
82
+
83
+ /** Selects the point-reduction algorithm used by the sampling backend. */
84
+ export type WaveformSamplingStrategy =
85
+ 'auto' | 'none' | 'peak' | 'lttb' | 'average' | 'min' | 'max' | 'minmax' | 'sum'
86
+
87
+ /** Controls how waveform points are selected for rendering. */
88
+ export interface WaveformSamplingOptions {
89
+ /** Defaults to `auto`. */
90
+ mode?: WaveformSamplingMode
91
+ /** Per-series visible-point threshold used by auto mode. Defaults to 1,000. */
92
+ autoThreshold?: number
93
+ /** Interaction-only hysteresis around the automatic threshold. Defaults to 0. */
94
+ autoHysteresis?: number
95
+ /** Defaults to the peak-preserving strategy. */
96
+ strategy?: WaveformSamplingStrategy
97
+ /** Upper bound for rendered points per horizontal CSS pixel. */
98
+ maxPointsPerPixel?: number
99
+ /** Visible-point count at which raw rendering should report a diagnostic. */
100
+ rawPointLimit?: number
101
+ /** Controls whether a forced WASM failure may temporarily show JavaScript results. */
102
+ wasmFailureFallback?: 'error' | 'javascript'
103
+ }
104
+
105
+ /** The backend that produced a series' current SVG geometry. */
106
+ export type WaveformSamplingBackend = 'raw' | 'javascript' | 'wasm' | 'unavailable'
107
+
108
+ /** Read-only details emitted after a sampling request settles. */
109
+ export interface WaveformSamplingDiagnostics {
110
+ seriesId: string
111
+ /** Internal dataset identity, useful when correlating request logs. */
112
+ datasetId: string
113
+ mode: WaveformSamplingMode
114
+ selectedMode: 'raw' | 'sampled'
115
+ backend: WaveformSamplingBackend
116
+ strategy: Exclude<WaveformSamplingStrategy, 'auto'>
117
+ sourcePointCount: number
118
+ visiblePointCount: number
119
+ renderedPointCount: number
120
+ durationMs: number
121
+ cacheHit: boolean
122
+ requestId: number
123
+ revision: number
124
+ rawPointLimitExceeded: boolean
125
+ /** Cumulative sampling runs scheduled by this chart instance. */
126
+ scheduledRequestCount: number
127
+ /** Cumulative intermediate runs replaced before they entered the Worker queue. */
128
+ coalescedRequestCount: number
129
+ /** Maximum retained pending runs; bounded to one by the latest-wins scheduler. */
130
+ maxPendingRequestCount: number
131
+ }
132
+
133
+ /** Describes a sampling backend failure without exposing an implementation-specific Error object. */
134
+ export interface WaveformSamplingError {
135
+ message: string
136
+ mode: WaveformSamplingMode
137
+ fallback: 'javascript' | 'none'
138
+ seriesIds: string[]
139
+ }
140
+
80
141
  /** Controls how many source points are included in the rendered SVG path. */
81
142
  export interface WaveformRenderingOptions {
143
+ /** Sampling execution and strategy configuration. Takes precedence over legacy fields. */
144
+ sampling?: WaveformSamplingOptions
82
145
  /** Disable only when every source point must be rendered. */
83
146
  downsample?: boolean
84
147
  /** Visible point count below which the complete range is rendered. */
@@ -176,6 +239,8 @@ export interface WaveformAxesOptions {
176
239
  lineVisible?: boolean
177
240
  /** Target total number of equal Y-axis ticks, including endpoints. Defaults to 5. */
178
241
  splitNumber?: number
242
+ /** Expands Y-axis domains to equal, human-friendly intervals. Defaults to true. */
243
+ nice?: boolean
179
244
  }
180
245
  }
181
246
 
package/src/types/data.ts CHANGED
@@ -27,8 +27,29 @@ export interface ResolvedWaveformErrorBarOptions {
27
27
  capWidth: number
28
28
  }
29
29
 
30
+ /** Numeric storage accepted by the compact waveform input formats. */
31
+ export type WaveformTypedValues = Float32Array | Float64Array
32
+
33
+ /** Compact, evenly spaced samples. X is derived as `startTime + index / sampleRate`. */
34
+ export interface TypedSampleData {
35
+ kind: 'typed-samples'
36
+ values: WaveformTypedValues
37
+ sampleRate: number
38
+ startTime?: number
39
+ }
40
+
41
+ /** Compact points with explicit X coordinates. X uses Float64 precision by contract. */
42
+ export interface TypedPointData {
43
+ kind: 'typed-points'
44
+ x: Float64Array
45
+ y: WaveformTypedValues
46
+ error?: WaveformTypedValues
47
+ lowerError?: WaveformTypedValues
48
+ upperError?: WaveformTypedValues
49
+ }
50
+
30
51
  /**
31
- * 单波形数据格式(采样点或显式坐标点)
52
+ * 单波形数据格式(对象数组或紧凑 TypedArray)
32
53
  */
33
54
  export type SingleWaveformData =
34
55
  | {
@@ -41,6 +62,8 @@ export type SingleWaveformData =
41
62
  kind: 'points'
42
63
  points: WaveformPoint[]
43
64
  }
65
+ | TypedSampleData
66
+ | TypedPointData
44
67
 
45
68
  /**
46
69
  * 波形系列
@@ -13,6 +13,12 @@ export type {
13
13
  WaveformZoomResetPayload,
14
14
  WaveformAnnotationStyle,
15
15
  WaveformAnnotation,
16
+ WaveformSamplingMode,
17
+ WaveformSamplingStrategy,
18
+ WaveformSamplingOptions,
19
+ WaveformSamplingBackend,
20
+ WaveformSamplingDiagnostics,
21
+ WaveformSamplingError,
16
22
  WaveformRenderingOptions,
17
23
  WaveformPlotMargin,
18
24
  WaveformTitleTextStyle,
@@ -36,6 +42,9 @@ export type {
36
42
  WaveformPointType,
37
43
  WaveformErrorBarOptions,
38
44
  ResolvedWaveformErrorBarOptions,
45
+ WaveformTypedValues,
46
+ TypedSampleData,
47
+ TypedPointData,
39
48
  WaveformSeries,
40
49
  WaveformData,
41
50
  NormalizedWaveformSeries,
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { routes } from '../router'
4
+
5
+ describe('WASM sampling demo route', () => {
6
+ it('registers the hash-demo route', () => {
7
+ expect(routes).toEqual(
8
+ expect.arrayContaining([
9
+ expect.objectContaining({ path: '/wasm-sampling', name: 'wasm-sampling' }),
10
+ ]),
11
+ )
12
+ })
13
+ })