waveform-analysis 0.1.63 → 0.1.65

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 (97) hide show
  1. package/README.md +21 -5
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +986 -964
  4. package/dist/style.css +1 -1
  5. package/dist/types/components/core/constants.d.ts +1 -1
  6. package/dist/types/components/core/trackLayoutBuilder.d.ts +1 -0
  7. package/dist/types/components/core/yAxisConstants.d.ts +1 -1
  8. package/dist/types/types/chart.d.ts +6 -1
  9. package/package.json +1 -1
  10. package/src/App.vue +2 -2
  11. package/src/components/WaveformChart.css +4 -0
  12. package/src/components/WaveformChart.vue +2 -2
  13. package/src/components/core/constants.ts +3 -3
  14. package/src/components/core/layout.ts +1 -1
  15. package/src/components/core/trackLayoutBuilder.ts +42 -1
  16. package/src/components/core/useWaveformLayout.ts +2 -1
  17. package/src/components/core/useWaveformPresentation.ts +1 -1
  18. package/src/components/core/xDomain.ts +26 -2
  19. package/src/components/core/yAxisConstants.ts +1 -1
  20. package/src/components/rendering/WaveformLegend.vue +1 -1
  21. package/src/components/rendering/WaveformSeriesLayer.vue +1 -1
  22. package/src/components/rendering/WaveformTrack.css +5 -2
  23. package/src/components/rendering/WaveformTrack.vue +7 -1
  24. package/src/components/rendering/WaveformTrackAxes.vue +8 -2
  25. package/src/demo/DemoChartHost.vue +1 -0
  26. package/src/types/chart.ts +6 -1
  27. package/src/App.test.ts +0 -400
  28. package/src/components/annotation/components.test.ts +0 -372
  29. package/src/components/annotation/markup.test.ts +0 -371
  30. package/src/components/annotation/serialization.test.ts +0 -114
  31. package/src/components/core/constants.test.ts +0 -29
  32. package/src/components/core/fixedYDomainLayout.test.ts +0 -204
  33. package/src/components/core/grid.test.ts +0 -197
  34. package/src/components/core/latestTaskScheduler.test.ts +0 -51
  35. package/src/components/core/layout.test.ts +0 -373
  36. package/src/components/core/title.test.ts +0 -76
  37. package/src/components/core/useWaveformData.test.ts +0 -95
  38. package/src/components/core/useWaveformRenderSampling.test.ts +0 -17
  39. package/src/components/core/xDomain.test.ts +0 -28
  40. package/src/components/core/yAxisNice.test.ts +0 -174
  41. package/src/components/core/yAxisSlotLayout.test.ts +0 -165
  42. package/src/components/core/yDomain.test.ts +0 -56
  43. package/src/components/interaction/WaveformTooltip.test.ts +0 -276
  44. package/src/components/interaction/viewportInteractionState.test.ts +0 -101
  45. package/src/components/utils/useAnimationFrameThrottle.test.ts +0 -53
  46. package/src/components/waveformChartCases/annotationEditingAndReprojection.test.ts +0 -257
  47. package/src/components/waveformChartCases/compactAndSharedHover.test.ts +0 -358
  48. package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +0 -358
  49. package/src/components/waveformChartCases/dataLifecycle.test.ts +0 -310
  50. package/src/components/waveformChartCases/frameGridAndFormatting.test.ts +0 -389
  51. package/src/components/waveformChartCases/gridFill.test.ts +0 -95
  52. package/src/components/waveformChartCases/incompleteDataHover.test.ts +0 -155
  53. package/src/components/waveformChartCases/interactionAndAnnotationCreation.test.ts +0 -328
  54. package/src/components/waveformChartCases/layoutAndDomains.test.ts +0 -371
  55. package/src/components/waveformChartCases/legendVisibility.test.ts +0 -350
  56. package/src/components/waveformChartCases/multiAxisAndPagination.test.ts +0 -400
  57. package/src/components/waveformChartCases/multiAxisLabelAlignment.test.ts +0 -61
  58. package/src/components/waveformChartCases/presentationAndAxes.test.ts +0 -345
  59. package/src/components/waveformChartCases/presentationMode.test.ts +0 -327
  60. package/src/components/waveformChartCases/renderingStyles.test.ts +0 -283
  61. package/src/components/waveformChartCases/sizingAndTitles.test.ts +0 -370
  62. package/src/components/waveformChartCases/trackOrder.test.ts +0 -323
  63. package/src/components/waveformChartCases/viewportBoxAndPan.test.ts +0 -365
  64. package/src/components/waveformChartCases/viewportConstraintsAndReset.test.ts +0 -311
  65. package/src/components/waveformChartCases/viewportLifecycle.test.ts +0 -132
  66. package/src/components/waveformChartCases/viewportReset.test.ts +0 -251
  67. package/src/components/waveformChartCases/wheelZoomOut.test.ts +0 -114
  68. package/src/components/waveformChartCases/workerSamplingIntegration.test.ts +0 -117
  69. package/src/components/waveformChartCases/xDomainStrategy.test.ts +0 -168
  70. package/src/components/waveformChartCases/yAxisLayout.test.ts +0 -103
  71. package/src/components/waveformChartCases/zoomIntentAndDataRebind.test.ts +0 -213
  72. package/src/components/waveformChartCases/zoomPointConstraints.test.ts +0 -308
  73. package/src/components/waveformLegendGeometry.test.ts +0 -21
  74. package/src/components/waveformNormalization.test.ts +0 -170
  75. package/src/core/data.test.ts +0 -241
  76. package/src/core/rendering.test.ts +0 -267
  77. package/src/core/renderingStrategies.test.ts +0 -90
  78. package/src/core/samplingReference.test.ts +0 -124
  79. package/src/core/wasmSampling.test.ts +0 -205
  80. package/src/core/workerSampling/multiresolution.test.ts +0 -69
  81. package/src/core/workerSampling/protocol.test.ts +0 -34
  82. package/src/core/workerSampling/repository.test.ts +0 -400
  83. package/src/core/workerSampling/repositoryCache.test.ts +0 -163
  84. package/src/core/workerSampling/repositorySampling.test.ts +0 -106
  85. package/src/core/workerSampling/repositoryTypedDataset.test.ts +0 -89
  86. package/src/core/workerSampling/repositoryWasmDataset.test.ts +0 -70
  87. package/src/data/simulatedWaveforms.test.ts +0 -60
  88. package/src/demo/plotMarginControls.test.ts +0 -29
  89. package/src/demo/xAxisLabelControls.test.ts +0 -86
  90. package/src/test/setup.ts +0 -82
  91. package/src/test/waveformChart.ts +0 -75
  92. package/src/utils/domain.test.ts +0 -28
  93. package/src/utils/formatters.test.ts +0 -131
  94. package/src/utils/sampling.test.ts +0 -226
  95. package/src/views/FixedYDomainDemo.test.ts +0 -44
  96. package/src/views/WasmSamplingDemo.test.ts +0 -13
  97. package/src/views/wasmSamplingDemoSampling.test.ts +0 -27
@@ -1,400 +0,0 @@
1
- import { flushPromises } from '@vue/test-utils'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- import { flushAnimationFrames } from '../../test/setup'
5
- import { type WaveformData } from '../waveform'
6
-
7
- import { gridSeries, mountSizedChart } from '../../test/waveformChart'
8
-
9
- describe('WaveformChart', () => {
10
- it('resolves a separate scientific multiplier for every Y axis', async () => {
11
- const wrapper = await mountSizedChart(
12
- {
13
- kind: 'series',
14
- series: [
15
- {
16
- trackId: 'shared-frame',
17
- name: '普通量程',
18
- unit: 'V',
19
- data: {
20
- kind: 'points',
21
- points: [
22
- { x: 0, y: 0 },
23
- { x: 1, y: 1 },
24
- ],
25
- },
26
- },
27
- {
28
- trackId: 'shared-frame',
29
- name: '大量程',
30
- unit: 'A',
31
- data: {
32
- kind: 'points',
33
- points: [
34
- { x: 0, y: 0 },
35
- { x: 1, y: 2540 },
36
- ],
37
- },
38
- },
39
- {
40
- trackId: 'shared-frame',
41
- name: '小量程',
42
- unit: 'T',
43
- data: {
44
- kind: 'points',
45
- points: [
46
- { x: 0, y: 0 },
47
- { x: 1, y: 0.0002 },
48
- ],
49
- },
50
- },
51
- ],
52
- },
53
- { overlayMode: 'multi-axis' },
54
- )
55
-
56
- expect(
57
- wrapper.findAll('.waveform-chart__axis--y').map((axis) =>
58
- axis
59
- .findAll('.tick text')
60
- .map((label) => label.text())
61
- .filter((label) => label.startsWith('E')),
62
- ),
63
- ).toEqual([
64
- [],
65
- [expect.stringMatching(/^E\+03 \(A\) /)],
66
- [expect.stringMatching(/^E-04 \(T\) /)],
67
- ])
68
- })
69
-
70
- it('reprojects annotations with the Y axis assigned to their series', async () => {
71
- const data: WaveformData = {
72
- kind: 'series',
73
- series: [
74
- {
75
- id: 'low',
76
- trackId: 'shared-frame',
77
- name: '低量程',
78
- data: {
79
- kind: 'points',
80
- points: [
81
- { x: 0, y: 0 },
82
- { x: 1, y: 10 },
83
- ],
84
- },
85
- },
86
- {
87
- id: 'high',
88
- trackId: 'shared-frame',
89
- name: '高量程',
90
- data: {
91
- kind: 'points',
92
- points: [
93
- { x: 0, y: 1000 },
94
- { x: 1, y: 2000 },
95
- ],
96
- },
97
- },
98
- ],
99
- }
100
- const wrapper = await mountSizedChart(data, {
101
- annotations: [{ id: 'high-note', seriesId: 'high', x: 0.5, y: 1500, text: '高值' }],
102
- })
103
- const singleAxisY = wrapper.get('.waveform-annotation__arrow').attributes('y2')
104
-
105
- await wrapper.setProps({ overlayMode: 'multi-axis' })
106
- await flushPromises()
107
-
108
- expect(wrapper.get('.waveform-annotation__arrow').attributes('y2')).not.toBe(singleAxisY)
109
-
110
- wrapper.unmount()
111
- })
112
-
113
- it('paginates channels into a row-major two by one grid', async () => {
114
- const wrapper = await mountSizedChart(gridSeries(5), {
115
- grid: { rowCount: 2, columnCount: 1 },
116
- })
117
-
118
- expect(wrapper.findAll('.waveform-chart__track')).toHaveLength(2)
119
- expect(
120
- wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
121
- ).toEqual(['channel-0', 'channel-1'])
122
- const previousButton = () => wrapper.get('.ant-pagination-prev button')
123
- const nextButton = () => wrapper.get('.ant-pagination-next button')
124
-
125
- expect(wrapper.get('.ant-pagination-item-1').classes()).toContain('ant-pagination-item-active')
126
- expect(wrapper.get('.ant-pagination-prev').classes()).toContain('ant-pagination-disabled')
127
-
128
- await nextButton().trigger('click')
129
- expect(
130
- wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
131
- ).toEqual(['channel-2', 'channel-3'])
132
- expect(wrapper.emitted('page-change')?.at(-1)).toEqual([2, 3])
133
-
134
- await previousButton().trigger('click')
135
- expect(wrapper.get('.ant-pagination-item-1').classes()).toContain('ant-pagination-item-active')
136
- expect(wrapper.emitted('page-change')?.at(-1)).toEqual([1, 3])
137
-
138
- await nextButton().trigger('click')
139
- await nextButton().trigger('click')
140
- expect(
141
- wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
142
- ).toEqual(['channel-4'])
143
- expect(wrapper.get('.ant-pagination-item-3').classes()).toContain('ant-pagination-item-active')
144
- expect(wrapper.get('.ant-pagination-next').classes()).toContain('ant-pagination-disabled')
145
- })
146
-
147
- it('keeps ordered empty tracks in their page slots after channels are merged', async () => {
148
- const wrapper = await mountSizedChart(
149
- {
150
- kind: 'series',
151
- series: ['frame-1', 'frame-1', 'frame-3', 'frame-4', 'frame-5', 'frame-6'].map(
152
- (trackId, index) => ({
153
- id: `channel-${index + 1}`,
154
- trackId,
155
- name: `channel-${index + 1}`,
156
- data: {
157
- kind: 'points' as const,
158
- points: [
159
- { x: 0, y: index },
160
- { x: 1, y: index + 1 },
161
- ],
162
- },
163
- }),
164
- ),
165
- },
166
- {
167
- grid: {
168
- rowCount: 2,
169
- columnCount: 1,
170
- trackOrder: ['frame-1', 'frame-2', 'frame-3', 'frame-4', 'frame-5', 'frame-6'],
171
- },
172
- },
173
- )
174
- const trackIds = () =>
175
- wrapper.findAll('.waveform-chart__track').map((track) => track.attributes('data-track-id'))
176
- const lineIds = () =>
177
- wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id'))
178
- const nextButton = () => wrapper.get('.ant-pagination-next button')
179
-
180
- expect(trackIds()).toEqual(['frame-1', 'frame-2'])
181
- expect(wrapper.findAll('.waveform-chart__track--empty')).toHaveLength(1)
182
- expect(lineIds()).toEqual(['channel-1', 'channel-2'])
183
- expect(wrapper.findAll('.waveform-chart__legend')).toHaveLength(1)
184
-
185
- await nextButton().trigger('click')
186
- expect(trackIds()).toEqual(['frame-3', 'frame-4'])
187
- expect(lineIds()).toEqual(['channel-3', 'channel-4'])
188
- expect(wrapper.emitted('page-change')?.at(-1)).toEqual([2, 3])
189
-
190
- await nextButton().trigger('click')
191
- expect(trackIds()).toEqual(['frame-5', 'frame-6'])
192
- expect(lineIds()).toEqual(['channel-5', 'channel-6'])
193
- expect(wrapper.emitted('page-change')?.at(-1)).toEqual([3, 3])
194
- })
195
-
196
- it('overlays series with the same track ID without changing the next frame', async () => {
197
- const wrapper = await mountSizedChart(
198
- {
199
- kind: 'series',
200
- series: [
201
- {
202
- id: 'primary',
203
- trackId: 'frame-1',
204
- name: 'BT2_2M',
205
- data: {
206
- kind: 'points',
207
- points: [
208
- { x: 0, y: 0 },
209
- { x: 1, y: 1 },
210
- ],
211
- },
212
- },
213
- {
214
- id: 'second-frame',
215
- name: 'BT1_2M',
216
- data: {
217
- kind: 'points',
218
- points: [
219
- { x: 0, y: 2 },
220
- { x: 1, y: 3 },
221
- ],
222
- },
223
- },
224
- {
225
- id: 'comparison',
226
- trackId: 'frame-1',
227
- name: 'TEST_CH_1',
228
- data: {
229
- kind: 'points',
230
- points: [
231
- { x: 0, y: 0.5 },
232
- { x: 1, y: 1.5 },
233
- ],
234
- },
235
- },
236
- {
237
- id: 'reference',
238
- trackId: 'frame-1',
239
- name: 'REF_CH_1',
240
- data: {
241
- kind: 'points',
242
- points: [
243
- { x: 0, y: 0.25 },
244
- { x: 1, y: 1.25 },
245
- ],
246
- },
247
- },
248
- ],
249
- },
250
- { frameNumber: 1, grid: { rowCount: 2, columnCount: 1 } },
251
- )
252
-
253
- const tracks = wrapper.findAll('.waveform-chart__track')
254
- expect(tracks).toHaveLength(2)
255
- expect(
256
- tracks[0].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
257
- ).toEqual(['primary', 'comparison', 'reference'])
258
- expect(
259
- tracks[1].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
260
- ).toEqual(['second-frame'])
261
- expect(tracks[0].find('.waveform-chart__y-axis-label').exists()).toBe(false)
262
- expect(tracks[0].findAll('.waveform-chart__axis--y .tick').length).toBeGreaterThan(0)
263
- expect(tracks[1].get('.waveform-chart__y-axis-label').text()).toBe('BT1_2M')
264
- expect(wrapper.findAll('.waveform-chart__legend')).toHaveLength(1)
265
- const legend = wrapper.get('.waveform-chart__legend')
266
- expect(legend.attributes('data-position')).toBe('top-right')
267
- expect(legend.attributes('data-orientation')).toBe('vertical')
268
- expect(legend.get('.waveform-legend__panel').attributes('style')).toContain(
269
- 'background-color: rgba(255, 255, 255, 0.7)',
270
- )
271
- expect(legend.findAll('.waveform-chart__legend-item').map((item) => item.text())).toEqual([
272
- 'BT2_2M',
273
- 'TEST_CH_1',
274
- 'REF_CH_1',
275
- ])
276
- expect(
277
- legend
278
- .findAll('.waveform-legend__swatch')
279
- .map((swatch) => swatch.get('path').attributes('stroke')),
280
- ).toEqual(['#0960bd', '#2ca02c', '#d62728'])
281
- expect(wrapper.findAll('.waveform-chart__watermark').map((item) => item.text())).toEqual([
282
- '1',
283
- '2',
284
- ])
285
- expect(wrapper.find('.ant-pagination').exists()).toBe(false)
286
-
287
- const firstTrackOverlay = tracks[0].get('.waveform-chart__overlay')
288
- const overlayWidth = Number(firstTrackOverlay.attributes('width'))
289
- const overlayHeight = Number(firstTrackOverlay.attributes('height'))
290
- Object.defineProperty(firstTrackOverlay.element, 'getBoundingClientRect', {
291
- value: () => ({ left: 0, top: 0, width: overlayWidth, height: overlayHeight }),
292
- })
293
- firstTrackOverlay.element.dispatchEvent(
294
- new MouseEvent('pointermove', {
295
- clientX: overlayWidth / 2,
296
- clientY: overlayHeight / 2,
297
- bubbles: true,
298
- }),
299
- )
300
- flushAnimationFrames()
301
- await flushPromises()
302
-
303
- const tooltipSeries = wrapper.findAll('.waveform-chart__tooltip-series')
304
- expect(tooltipSeries).toHaveLength(3)
305
- expect(tooltipSeries.map((item) => item.text())).toEqual([
306
- expect.stringContaining('BT2_2M'),
307
- expect.stringContaining('TEST_CH_1'),
308
- expect.stringContaining('REF_CH_1'),
309
- ])
310
- })
311
-
312
- it('resolves automatic legend orientation and supports explicit overrides', async () => {
313
- const wrapper = await mountSizedChart(
314
- {
315
- kind: 'series',
316
- series: [
317
- {
318
- id: 'first',
319
- trackId: 'shared',
320
- name: 'first',
321
- data: {
322
- kind: 'points',
323
- points: [
324
- { x: 0, y: 0 },
325
- { x: 1, y: 1 },
326
- ],
327
- },
328
- },
329
- {
330
- id: 'second',
331
- trackId: 'shared',
332
- name: 'second',
333
- data: {
334
- kind: 'points',
335
- points: [
336
- { x: 0, y: 1 },
337
- { x: 1, y: 2 },
338
- ],
339
- },
340
- },
341
- {
342
- id: 'third',
343
- trackId: 'shared',
344
- name: 'third',
345
- data: {
346
- kind: 'points',
347
- points: [
348
- { x: 0, y: 2 },
349
- { x: 1, y: 3 },
350
- ],
351
- },
352
- },
353
- ],
354
- },
355
- { grid: { rowCount: 1, columnCount: 1 } },
356
- )
357
- const positions = [
358
- 'top-left',
359
- 'top',
360
- 'top-right',
361
- 'right',
362
- 'bottom-right',
363
- 'bottom',
364
- 'bottom-left',
365
- 'left',
366
- ] as const
367
-
368
- for (const position of positions) {
369
- await wrapper.setProps({ legend: { position, orientation: 'auto' } })
370
- const legend = wrapper.get('.waveform-chart__legend')
371
- const expectedOrientation =
372
- position === 'top' || position === 'bottom' ? 'horizontal' : 'vertical'
373
- expect(legend.attributes('data-position')).toBe(position)
374
- expect(legend.attributes('data-orientation')).toBe(expectedOrientation)
375
- expect(legend.get('.waveform-legend__viewport').classes()).toContain(
376
- `waveform-legend__viewport--${position}`,
377
- )
378
- expect(
379
- legend
380
- .get('.waveform-legend__panel')
381
- .classes()
382
- .includes('waveform-legend__panel--vertical'),
383
- ).toBe(expectedOrientation === 'vertical')
384
- }
385
-
386
- await wrapper.setProps({ legend: { position: 'top', orientation: 'vertical' } })
387
- expect(wrapper.get('.waveform-chart__legend').attributes('data-orientation')).toBe('vertical')
388
- expect(wrapper.get('.waveform-legend__panel').classes()).toContain(
389
- 'waveform-legend__panel--vertical',
390
- )
391
-
392
- await wrapper.setProps({ legend: { position: 'left', orientation: 'horizontal' } })
393
- expect(wrapper.get('.waveform-chart__legend').attributes('data-orientation')).toBe('horizontal')
394
- expect(wrapper.get('.waveform-legend__panel').classes()).toContain(
395
- 'waveform-legend__panel--horizontal',
396
- )
397
-
398
- expect(wrapper.attributes('data-chart-left-margin')).toBe('80')
399
- })
400
- })
@@ -1,61 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
-
3
- import { mountSizedChart } from '../../test/waveformChart'
4
-
5
- describe('WaveformChart multi-axis labels', () => {
6
- it('aligns single-axis and multi-axis Y-axis titles in one column', async () => {
7
- const wrapper = await mountSizedChart(
8
- {
9
- kind: 'series',
10
- series: [
11
- {
12
- id: 'single',
13
- trackId: 'single-track',
14
- name: '单轴',
15
- data: {
16
- kind: 'points',
17
- points: [
18
- { x: 0, y: -1e120 },
19
- { x: 1, y: 1e120 },
20
- ],
21
- },
22
- },
23
- {
24
- id: 'left',
25
- trackId: 'multi-track',
26
- name: '左轴',
27
- data: {
28
- kind: 'points',
29
- points: [
30
- { x: 0, y: 0 },
31
- { x: 1, y: 1 },
32
- ],
33
- },
34
- },
35
- {
36
- id: 'right',
37
- trackId: 'multi-track',
38
- name: '右轴',
39
- data: {
40
- kind: 'points',
41
- points: [
42
- { x: 0, y: 1000 },
43
- { x: 1, y: 3000 },
44
- ],
45
- },
46
- },
47
- ],
48
- },
49
- { overlayMode: 'multi-axis', grid: { rowCount: 2, columnCount: 1 } },
50
- )
51
- const tracks = wrapper.findAll('.waveform-chart__track')
52
- const titleX = (transform: string | undefined) =>
53
- Number(transform?.match(/translate\(([^,]+)/)?.[1])
54
-
55
- expect(titleX(tracks[0]!.get('.waveform-chart__y-axis-label').attributes('transform'))).toBe(
56
- titleX(
57
- tracks[1]!.findAll('.waveform-track__multi-axis-title text')[0]!.attributes('transform'),
58
- ),
59
- )
60
- })
61
- })