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.
- package/README.md +127 -5
- package/dist/assets/workerRuntime-Dt94jbqq.js +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +2701 -1165
- package/dist/style.css +1 -1
- package/dist/types/components/WaveformChart.vue.d.ts +14 -0
- package/dist/types/components/annotation/WaveformAnnotationEditor.vue.d.ts +0 -1
- package/dist/types/components/annotation/markup.d.ts +5 -6
- package/dist/types/components/annotation/types.d.ts +2 -0
- package/dist/types/components/core/latestTaskScheduler.d.ts +19 -0
- package/dist/types/components/core/layout.d.ts +2 -2
- package/dist/types/components/core/trackLayoutBuilder.d.ts +4 -1
- package/dist/types/components/core/types.d.ts +2 -0
- package/dist/types/components/core/useWaveformChartController.d.ts +1 -0
- package/dist/types/components/core/useWaveformData.d.ts +2 -0
- package/dist/types/components/core/useWaveformLayout.d.ts +2 -0
- package/dist/types/components/core/useWaveformRenderSampling.d.ts +19 -0
- package/dist/types/components/core/waveformChartTypes.d.ts +8 -1
- package/dist/types/components/core/workerSamplingClient.d.ts +8 -0
- package/dist/types/components/data/types.d.ts +1 -1
- package/dist/types/components/interaction/zoomConstraints.d.ts +2 -0
- package/dist/types/components/waveform.d.ts +1 -1
- package/dist/types/core/compactSamplePointSource.d.ts +3 -0
- package/dist/types/core/data.d.ts +7 -1
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/rendering.d.ts +3 -1
- package/dist/types/core/renderingOptions.d.ts +11 -1
- package/dist/types/core/samplingReference.d.ts +36 -0
- package/dist/types/core/samplingReferenceValues.d.ts +4 -0
- package/dist/types/core/samplingReferenceVectors.d.ts +17 -0
- package/dist/types/core/wasmSampling.d.ts +48 -0
- package/dist/types/core/waveformPointSource.d.ts +53 -0
- package/dist/types/core/workerSampling/index.d.ts +5 -0
- package/dist/types/core/workerSampling/javascriptBackend.d.ts +3 -0
- package/dist/types/core/workerSampling/multiresolution.d.ts +29 -0
- package/dist/types/core/workerSampling/protocol.d.ts +153 -0
- package/dist/types/core/workerSampling/repository.d.ts +36 -0
- package/dist/types/core/workerSampling/repositoryDataset.d.ts +29 -0
- package/dist/types/core/workerSampling/repositorySupport.d.ts +27 -0
- package/dist/types/core/workerSampling/samplingCache.d.ts +24 -0
- package/dist/types/core/workerSampling/workerRuntime.d.ts +1 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/types/chart.d.ts +58 -0
- package/dist/types/types/data.d.ts +20 -2
- package/dist/types/types/index.d.ts +2 -2
- package/dist/types/views/WasmSamplingDemo.vue.d.ts +3 -0
- package/package.json +5 -4
- package/src/App.test.ts +11 -7
- package/src/App.vue +2 -2
- package/src/DemoRouterApp.vue +1 -0
- package/src/components/WaveformChart.css +2 -0
- package/src/components/annotation/WaveformAnnotationEditor.css +2 -2
- package/src/components/annotation/WaveformAnnotationEditor.vue +8 -4
- package/src/components/annotation/markup.ts +45 -26
- package/src/components/annotation/types.ts +2 -0
- package/src/components/annotation/useWaveformChartAnnotations.ts +12 -15
- package/src/components/core/latestTaskScheduler.test.ts +51 -0
- package/src/components/core/latestTaskScheduler.ts +75 -0
- package/src/components/core/layout.ts +15 -6
- package/src/components/core/trackLayoutBuilder.ts +8 -2
- package/src/components/core/types.ts +2 -0
- package/src/components/core/useWaveformChartController.ts +14 -0
- package/src/components/core/useWaveformData.test.ts +95 -0
- package/src/components/core/useWaveformData.ts +23 -32
- package/src/components/core/useWaveformLayout.ts +16 -10
- package/src/components/core/useWaveformRenderSampling.test.ts +17 -0
- package/src/components/core/useWaveformRenderSampling.ts +394 -0
- package/src/components/core/waveformChartTypes.ts +13 -0
- package/src/components/core/workerSamplingClient.ts +148 -0
- package/src/components/core/yAxisNice.test.ts +174 -0
- package/src/components/data/types.ts +9 -0
- package/src/components/interaction/WaveformTooltip.test.ts +1 -3
- package/src/components/interaction/WaveformTooltip.vue +13 -12
- package/src/components/interaction/useWaveformHover.ts +7 -7
- package/src/components/interaction/zoomConstraints.ts +5 -1
- package/src/components/waveform.ts +9 -0
- package/src/components/waveformChartCases/compactAndSharedHover.test.ts +15 -4
- package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +11 -6
- package/src/components/waveformChartCases/dataLifecycle.test.ts +1 -3
- package/src/components/waveformChartCases/trackOrder.test.ts +130 -0
- package/src/components/waveformChartCases/workerSamplingIntegration.test.ts +117 -0
- package/src/components/waveformChartCases/xDomainStrategy.test.ts +14 -2
- package/src/core/compactSamplePointSource.ts +76 -0
- package/src/core/data.test.ts +114 -0
- package/src/core/data.ts +177 -75
- package/src/core/index.ts +11 -0
- package/src/core/rendering.test.ts +87 -12
- package/src/core/rendering.ts +35 -1
- package/src/core/renderingOptions.ts +124 -15
- package/src/core/samplingReference.test.ts +124 -0
- package/src/core/samplingReference.ts +243 -0
- package/src/core/samplingReferenceValues.ts +169 -0
- package/src/core/samplingReferenceVectors.ts +46 -0
- package/src/core/wasmSampling.test.ts +205 -0
- package/src/core/wasmSampling.ts +230 -0
- package/src/core/waveformPointSource.ts +395 -0
- package/src/core/workerSampling/index.ts +30 -0
- package/src/core/workerSampling/javascriptBackend.ts +15 -0
- package/src/core/workerSampling/multiresolution.test.ts +69 -0
- package/src/core/workerSampling/multiresolution.ts +300 -0
- package/src/core/workerSampling/protocol.test.ts +34 -0
- package/src/core/workerSampling/protocol.ts +207 -0
- package/src/core/workerSampling/repository.test.ts +400 -0
- package/src/core/workerSampling/repository.ts +400 -0
- package/src/core/workerSampling/repositoryCache.test.ts +163 -0
- package/src/core/workerSampling/repositoryDataset.ts +194 -0
- package/src/core/workerSampling/repositorySupport.ts +225 -0
- package/src/core/workerSampling/repositoryTypedDataset.test.ts +89 -0
- package/src/core/workerSampling/repositoryWasmDataset.test.ts +70 -0
- package/src/core/workerSampling/samplingCache.ts +94 -0
- package/src/core/workerSampling/workerRuntime.ts +40 -0
- package/src/data/simulatedWaveforms.test.ts +19 -0
- package/src/data/simulatedWaveforms.ts +11 -8
- package/src/demo/xAxisLabelControls.test.ts +1 -1
- package/src/demoRoutes.css +177 -0
- package/src/index.ts +18 -0
- package/src/router.ts +5 -0
- package/src/types/chart.ts +65 -0
- package/src/types/data.ts +24 -1
- package/src/types/index.ts +9 -0
- package/src/views/WasmSamplingDemo.test.ts +13 -0
- 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: '
|
|
54
|
-
pointType: '
|
|
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: (
|
|
58
|
-
lowerError: 0.
|
|
59
|
-
upperError: 0.
|
|
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: {
|
package/src/demoRoutes.css
CHANGED
|
@@ -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({
|
package/src/types/chart.ts
CHANGED
|
@@ -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
|
* 波形系列
|
package/src/types/index.ts
CHANGED
|
@@ -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
|
+
})
|