feat(data): 扩展示例波形数据与测试覆盖
All checks were successful
Package component / package (push) Successful in 4m26s

新增示例谐波对比数据,完善图例、多轴和演示测试,并同步文档说明。
This commit is contained in:
李启源
2026-08-17 14:34:15 +08:00
parent dda87f7508
commit 7a6a7e5c26
9 changed files with 146 additions and 60 deletions

View File

@@ -96,37 +96,37 @@ const data = ref<WaveformData>({
### Props ### Props
| Prop | 类型 | 默认值 | 说明 | | Prop | 类型 | 默认值 | 说明 |
| --------------------------------- | ------------------------------------------- | ------------------------------------------------------- | ---------------------------------- | | --------------------------------- | ------------------------------------------- | ------------------------------------------------------- | --------------------------------------------- |
| `data` | `WaveformData` | 必填 | 波形数据 | | `data` | `WaveformData` | 必填 | 波形数据 |
| `displayMode` | `'independent' \| 'separated' \| 'compact'` | `'independent'` | 图框布局 | | `displayMode` | `'independent' \| 'separated' \| 'compact'` | `'independent'` | 图框布局 |
| `overlayMode` | `'single-axis' \| 'multi-axis'` | `'single-axis'` | 叠加曲线的 Y 轴模式 | | `overlayMode` | `'single-axis' \| 'multi-axis'` | `'single-axis'` | 叠加曲线的 Y 轴模式 |
| `timeUnit` | `'s' \| 'ms'` | `'ms'` | 坐标轴和 tooltip 展示单位 | | `timeUnit` | `'s' \| 'ms'` | `'ms'` | 坐标轴和 tooltip 展示单位 |
| `xLabel` / `yLabel` | `string` | `时间timeUnit` / `'幅值'` | 坐标轴名称 | | `xLabel` / `yLabel` | `string` | `时间timeUnit` / `'幅值'` | 坐标轴名称 |
| `lineColor` | `string` | `'#0960bd'` | 单波形默认颜色 | | `lineColor` | `string` | `'#0960bd'` | 单波形默认颜色 |
| `width` / `height` | `number` | 自适应 | 组件总尺寸,单位为 CSS 像素 | | `width` / `height` | `number` | 自适应 | 组件总尺寸,单位为 CSS 像素 |
| `zoomable` / `showTooltip` | `boolean` | `true` / `true` | 缩放和数值 tooltip 开关 | | `zoomable` / `showTooltip` | `boolean` | `true` / `true` | 缩放和数值 tooltip 开关 |
| `pannable` | `boolean` | `false` | 空格拖拽平移开关 | | `pannable` | `boolean` | `false` | 空格拖拽平移开关 |
| `minZoomSpan` | `number` | 未设置 | 最小缩放跨度,使用原始 X 数据单位 | | `minZoomSpan` | `number` | 未设置 | 最小缩放跨度,使用原始 X 数据单位 |
| `minVisiblePoints` | `number` | `0` | 缩放后至少保留的不同 X 坐标数 | | `minVisiblePoints` | `number` | `0` | 缩放后至少保留的不同 X 坐标数 |
| `initialXDomain` | `[number, number]` | 未设置 | 所有图框的初始 X 范围(可超出数据,空白显示) | | `initialXDomain` | `[number, number]` | 未设置 | 所有图框的初始 X 范围(可超出数据,空白显示) |
| `initialXDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置初始范围(可超出数据) | | `initialXDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置初始范围(可超出数据) |
| `xDomainStrategy` | `WaveformXDomainStrategy` | `{ type: 'data' }` | 自动 X 轴视口范围策略 | | `xDomainStrategy` | `WaveformXDomainStrategy` | `{ type: 'data' }` | 自动 X 轴视口范围策略 |
| `yDomain` | `[number, number]` | 未设置 | 所有波形的固定 Y 轴范围 | | `yDomain` | `[number, number]` | 未设置 | 所有波形的固定 Y 轴范围 |
| `yDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置固定范围 | | `yDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置固定范围 |
| `grid` | `WaveformGridOptions` | `{ rowCount: 2, columnCount: 1, showPagination: true }` | 网格和分页 | | `grid` | `WaveformGridOptions` | `{ rowCount: 2, columnCount: 1, showPagination: true }` | 网格和分页 |
| `axes` | `WaveformAxesOptions` | 轴线均显示 | X/Y 轴基线显隐与 X 轴 label 格式化 | | `axes` | `WaveformAxesOptions` | 轴线均显示 | X/Y 轴基线显隐与 X 轴 label 格式化 |
| `rendering` | `WaveformRenderingOptions` | `{}` | 降采样与点/误差棒间距 | | `rendering` | `WaveformRenderingOptions` | `{}` | 降采样与点/误差棒间距 |
| `title` / `legend` / `frameStyle` | 对应公开类型 | 未设置 | 标题、图例和图框样式 | | `title` / `legend` / `frameStyle` | 对应公开类型 | 未设置 | 标题、图例和图框样式 |
| `frameNumber` | `string \| number` | 未设置 | 图框水印内容 | | `frameNumber` | `string \| number` | 未设置 | 图框水印内容 |
| `zeroLine` | `WaveformZeroLineOptions` | `{ visible: false }` | 零值参考线显隐与样式 | | `zeroLine` | `WaveformZeroLineOptions` | `{ visible: false }` | 零值参考线显隐与样式 |
| `cleanView` | `boolean` | `false` | 保留波形、图框和刻度的净图模式 | | `cleanView` | `boolean` | `false` | 保留波形、图框和刻度的净图模式 |
| `presentationMode` | `boolean` | `false` | 禁用绘图区交互的展示模式 | | `presentationMode` | `boolean` | `false` | 禁用绘图区交互的展示模式 |
| `annotations` | `WaveformAnnotation[]` | `[]` | 受控标注数据 | | `annotations` | `WaveformAnnotation[]` | `[]` | 受控标注数据 |
| `annotationsVisible` | `boolean` | `true` | 标注图层显隐 | | `annotationsVisible` | `boolean` | `true` | 标注图层显隐 |
| `interactionMode` | `'zoom' \| 'annotation'` | `'zoom'` | 左键交互模式 | | `interactionMode` | `'zoom' \| 'annotation'` | `'zoom'` | 左键交互模式 |
| `hiddenSeriesIds` | `string[]` | 未设置 | 受控隐藏系列 ID | | `hiddenSeriesIds` | `string[]` | 未设置 | 受控隐藏系列 ID |
| `defaultHiddenSeriesIds` | `string[]` | `[]` | 非受控模式的初始隐藏系列 | | `defaultHiddenSeriesIds` | `string[]` | `[]` | 非受控模式的初始隐藏系列 |
所有公开类型均可从包入口导入,例如 `WaveformData``WaveformSeries` 所有公开类型均可从包入口导入,例如 `WaveformData``WaveformSeries`
`WaveformAnnotation``WaveformLineStyle``WaveformRenderingOptions` `WaveformAnnotation``WaveformLineStyle``WaveformRenderingOptions`
@@ -415,7 +415,8 @@ Demo 左侧控制面板提供标题实时预览,可配置标题名称、显隐
### 图例与曲线显隐 ### 图例与曲线显隐
`legend.backgroundColor` 设置多曲线图例的背景颜色。该字段接受任意有效 CSS 颜色值, 每个图框独立管理自己的图例:图框内有两条或更多曲线时显示图例,只有一条曲线时不显示。
`legend.backgroundColor` 设置图例的背景颜色。该字段接受任意有效 CSS 颜色值,
可通过 `rgba(...)``hsla(...)` 中的 alpha 通道调整透明度: 可通过 `rgba(...)``hsla(...)` 中的 alpha 通道调整透明度:
```vue ```vue

View File

@@ -1,6 +1,6 @@
{ {
"name": "waveform-analysis", "name": "waveform-analysis",
"version": "0.1.37", "version": "0.1.38",
"main": "./dist/index.cjs", "main": "./dist/index.cjs",
"module": "./dist/index.js", "module": "./dist/index.js",
"types": "./dist/types/index.d.ts", "types": "./dist/types/index.d.ts",

View File

@@ -235,7 +235,7 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
tracks.map((track) => tracks.map((track) =>
track.findAll('.waveform-chart__series').map((item) => item.attributes('data-series-name')), track.findAll('.waveform-chart__series').map((item) => item.attributes('data-series-name')),
), ),
).toEqual([['正弦基波'], ['谐波扰动'], ['阻尼振荡'], ['阶跃响应']]) ).toEqual([['正弦基波'], ['谐波扰动', '谐波对比'], ['阻尼振荡'], ['阶跃响应']])
expect( expect(
tracks tracks
.slice(1) .slice(1)
@@ -253,6 +253,7 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
expect(simulatedSeries.map((item) => item.name)).toEqual([ expect(simulatedSeries.map((item) => item.name)).toEqual([
'正弦基波', '正弦基波',
'谐波扰动', '谐波扰动',
'谐波对比',
'阻尼振荡', '阻尼振荡',
'阶跃响应', '阶跃响应',
'脉冲响应', '脉冲响应',
@@ -321,7 +322,6 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
const styleSelect = frameControls.findAllComponents(Select)[0] const styleSelect = frameControls.findAllComponents(Select)[0]
expect(colorPickers).toHaveLength(2) expect(colorPickers).toHaveLength(2)
const initialFrames = wrapper.findAll('.waveform-chart__plot-frame') const initialFrames = wrapper.findAll('.waveform-chart__plot-frame')
expect(initialFrames.length > 1 && initialFrames.every((frame) => frame.attributes('stroke-width') === '2')).toBe(true) expect(initialFrames.length > 1 && initialFrames.every((frame) => frame.attributes('stroke-width') === '2')).toBe(true)

View File

@@ -16,13 +16,30 @@ function annotationEditorExists() {
} }
describe('WaveformChart', () => { describe('WaveformChart', () => {
it('shows legends when a track contains at least two series', async () => {
const oneSeries = visibilitySeries()
if (oneSeries.kind === 'series') oneSeries.series = oneSeries.series.slice(0, 1)
const withoutLegend = await mountSizedChart(oneSeries, {
grid: { rowCount: 1, columnCount: 1 },
})
expect(withoutLegend.findAll('.waveform-chart__legend')).toHaveLength(0)
const twoSeries = visibilitySeries()
if (twoSeries.kind === 'series') twoSeries.series = twoSeries.series.slice(0, 2)
const withLegend = await mountSizedChart(twoSeries, {
grid: { rowCount: 1, columnCount: 1 },
})
expect(withLegend.findAll('.waveform-chart__legend')).toHaveLength(1)
expect(withLegend.find('.waveform-chart__legend').attributes('data-position')).toBe('top-right')
})
it('resolves legend positions by stable track id across pages', async () => { it('resolves legend positions by stable track id across pages', async () => {
const wrapper = await mountSizedChart( const wrapper = await mountSizedChart(
{ {
kind: 'series', kind: 'series',
series: Array.from({ length: 8 }, (_, index) => ({ series: Array.from({ length: 12 }, (_, index) => ({
id: `series-${index}`, id: `series-${index}`,
trackId: `frame-${Math.floor(index / 2)}`, trackId: `frame-${Math.floor(index / 3)}`,
name: `series ${index}`, name: `series ${index}`,
data: { data: {
kind: 'points' as const, kind: 'points' as const,
@@ -75,9 +92,9 @@ describe('WaveformChart', () => {
const wrapper = await mountSizedChart( const wrapper = await mountSizedChart(
{ {
kind: 'series', kind: 'series',
series: Array.from({ length: 4 }, (_, index) => ({ series: Array.from({ length: 6 }, (_, index) => ({
id: `series-${index}`, id: `series-${index}`,
trackId: `frame-${Math.floor(index / 2)}`, trackId: `frame-${Math.floor(index / 3)}`,
name: `series ${index}`, name: `series ${index}`,
data: { data: {
kind: 'points', kind: 'points',
@@ -112,7 +129,7 @@ describe('WaveformChart', () => {
}) })
const items = wrapper.findAll('.waveform-chart__legend-item') const items = wrapper.findAll('.waveform-chart__legend-item')
expect(items).toHaveLength(2) expect(items).toHaveLength(3)
expect(items.every((item) => item.attributes('disabled') !== undefined)).toBe(true) expect(items.every((item) => item.attributes('disabled') !== undefined)).toBe(true)
expect(wrapper.get('.waveform-legend__panel').classes()).not.toContain( expect(wrapper.get('.waveform-legend__panel').classes()).not.toContain(
'waveform-legend__panel--interactive', 'waveform-legend__panel--interactive',
@@ -128,7 +145,7 @@ describe('WaveformChart', () => {
overlayMode: 'multi-axis', overlayMode: 'multi-axis',
}) })
expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(2) expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(3)
expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(true) expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(true)
const annotationLayer = wrapper.get('.waveform-annotation-layer').element const annotationLayer = wrapper.get('.waveform-annotation-layer').element
const legendLayer = wrapper.get('.waveform-chart__legend-layer').element const legendLayer = wrapper.get('.waveform-chart__legend-layer').element
@@ -143,8 +160,8 @@ describe('WaveformChart', () => {
expect( expect(
wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')), wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
).toEqual(['low']) ).toEqual(['low', 'mid'])
expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(1) expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(2)
expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(false) expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(false)
expect(wrapper.findAll('.waveform-chart__legend-item')[1].classes()).toContain('is-hidden') expect(wrapper.findAll('.waveform-chart__legend-item')[1].classes()).toContain('is-hidden')
expect(wrapper.findAll('.waveform-chart__legend-item')[1].attributes('aria-pressed')).toBe( expect(wrapper.findAll('.waveform-chart__legend-item')[1].attributes('aria-pressed')).toBe(
@@ -170,12 +187,12 @@ describe('WaveformChart', () => {
) )
flushAnimationFrames() flushAnimationFrames()
await flushPromises() await flushPromises()
expect(wrapper.findAll('.waveform-chart__tooltip-series')).toHaveLength(1) expect(wrapper.findAll('.waveform-chart__tooltip-series')).toHaveLength(2)
expect(wrapper.get('.waveform-chart__tooltip-series').text()).toContain('低量程') expect(wrapper.get('.waveform-chart__tooltip-series').text()).toContain('低量程')
await wrapper.findAll('.waveform-chart__legend-item')[1].trigger('click') await wrapper.findAll('.waveform-chart__legend-item')[1].trigger('click')
await flushPromises() await flushPromises()
expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(2) expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(3)
expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(true) expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(true)
expect(wrapper.emitted('series-visibility-change')?.at(-1)).toEqual([ expect(wrapper.emitted('series-visibility-change')?.at(-1)).toEqual([
{ seriesId: 'high', visible: true, hiddenSeriesIds: [] }, { seriesId: 'high', visible: true, hiddenSeriesIds: [] },
@@ -191,20 +208,20 @@ describe('WaveformChart', () => {
expect( expect(
wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')), wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
).toEqual(['low']) ).toEqual(['low', 'mid'])
await wrapper.findAll('.waveform-chart__legend-item')[0].trigger('click') await wrapper.findAll('.waveform-chart__legend-item')[0].trigger('click')
expect(wrapper.emitted('update:hidden-series-ids')?.at(-1)).toEqual([ expect(wrapper.emitted('update:hidden-series-ids')?.at(-1)).toEqual([
['high', 'temporarily-absent', 'low'], ['high', 'temporarily-absent', 'low'],
]) ])
expect( expect(
wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')), wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
).toEqual(['low']) ).toEqual(['low', 'mid'])
await wrapper.setProps({ hiddenSeriesIds: ['low'] }) await wrapper.setProps({ hiddenSeriesIds: ['low'] })
await flushPromises() await flushPromises()
expect( expect(
wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')), wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
).toEqual(['high']) ).toEqual(['high', 'mid'])
}) })
it('retains uncontrolled visibility by stable ID and clears removed IDs', async () => { it('retains uncontrolled visibility by stable ID and clears removed IDs', async () => {
@@ -223,7 +240,7 @@ describe('WaveformChart', () => {
await flushPromises() await flushPromises()
expect( expect(
wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')), wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
).toEqual(['low']) ).toEqual(['mid', 'low'])
await wrapper.setProps({ await wrapper.setProps({
data: { data: {
@@ -234,12 +251,12 @@ describe('WaveformChart', () => {
await flushPromises() await flushPromises()
await wrapper.setProps({ data: original }) await wrapper.setProps({ data: original })
await flushPromises() await flushPromises()
expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(2) expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(3)
}) })
it('keeps a recoverable legend and stops chart interaction when every series is hidden', async () => { it('keeps a recoverable legend and stops chart interaction when every series is hidden', async () => {
const wrapper = await mountSizedChart(visibilitySeries(), { const wrapper = await mountSizedChart(visibilitySeries(), {
defaultHiddenSeriesIds: ['low', 'high'], defaultHiddenSeriesIds: ['low', 'high', 'mid'],
grid: { rowCount: 1, columnCount: 1 }, grid: { rowCount: 1, columnCount: 1 },
legend: { interactive: true }, legend: { interactive: true },
overlayMode: 'multi-axis', overlayMode: 'multi-axis',
@@ -248,7 +265,7 @@ describe('WaveformChart', () => {
expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(0) expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(0)
expect(wrapper.findAll('.waveform-chart__axis')).toHaveLength(0) expect(wrapper.findAll('.waveform-chart__axis')).toHaveLength(0)
expect(wrapper.findAll('.waveform-chart__overlay')).toHaveLength(0) expect(wrapper.findAll('.waveform-chart__overlay')).toHaveLength(0)
expect(wrapper.findAll('.waveform-chart__legend-item')).toHaveLength(2) expect(wrapper.findAll('.waveform-chart__legend-item')).toHaveLength(3)
expect(wrapper.get('.waveform-track__no-visible-series').text()).toBe('暂无可见曲线') expect(wrapper.get('.waveform-track__no-visible-series').text()).toBe('暂无可见曲线')
await wrapper.findAll('.waveform-chart__legend-item')[0].trigger('click') await wrapper.findAll('.waveform-chart__legend-item')[0].trigger('click')

View File

@@ -177,6 +177,18 @@ describe('WaveformChart', () => {
], ],
}, },
}, },
{
id: 'reference',
trackId: 'frame-1',
name: 'REF_CH_1',
data: {
kind: 'points',
points: [
{ x: 0, y: 0.25 },
{ x: 1, y: 1.25 },
],
},
},
], ],
}, },
{ frameNumber: 1, grid: { rowCount: 2, columnCount: 1 } }, { frameNumber: 1, grid: { rowCount: 2, columnCount: 1 } },
@@ -186,7 +198,7 @@ describe('WaveformChart', () => {
expect(tracks).toHaveLength(2) expect(tracks).toHaveLength(2)
expect( expect(
tracks[0].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')), tracks[0].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
).toEqual(['primary', 'comparison']) ).toEqual(['primary', 'comparison', 'reference'])
expect( expect(
tracks[1].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')), tracks[1].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
).toEqual(['second-frame']) ).toEqual(['second-frame'])
@@ -203,12 +215,13 @@ describe('WaveformChart', () => {
expect(legend.findAll('.waveform-chart__legend-item').map((item) => item.text())).toEqual([ expect(legend.findAll('.waveform-chart__legend-item').map((item) => item.text())).toEqual([
'BT2_2M', 'BT2_2M',
'TEST_CH_1', 'TEST_CH_1',
'REF_CH_1',
]) ])
expect( expect(
legend legend
.findAll('.waveform-legend__swatch') .findAll('.waveform-legend__swatch')
.map((swatch) => swatch.get('path').attributes('stroke')), .map((swatch) => swatch.get('path').attributes('stroke')),
).toEqual(['#0960bd', '#2ca02c']) ).toEqual(['#0960bd', '#2ca02c', '#d62728'])
expect(wrapper.findAll('.waveform-chart__watermark').map((item) => item.text())).toEqual([ expect(wrapper.findAll('.waveform-chart__watermark').map((item) => item.text())).toEqual([
'1', '1',
'2', '2',
@@ -232,10 +245,11 @@ describe('WaveformChart', () => {
await flushPromises() await flushPromises()
const tooltipSeries = wrapper.findAll('.waveform-chart__tooltip-series') const tooltipSeries = wrapper.findAll('.waveform-chart__tooltip-series')
expect(tooltipSeries).toHaveLength(2) expect(tooltipSeries).toHaveLength(3)
expect(tooltipSeries.map((item) => item.text())).toEqual([ expect(tooltipSeries.map((item) => item.text())).toEqual([
expect.stringContaining('BT2_2M'), expect.stringContaining('BT2_2M'),
expect.stringContaining('TEST_CH_1'), expect.stringContaining('TEST_CH_1'),
expect.stringContaining('REF_CH_1'),
]) ])
}) })
@@ -268,6 +282,18 @@ describe('WaveformChart', () => {
], ],
}, },
}, },
{
id: 'third',
trackId: 'shared',
name: 'third',
data: {
kind: 'points',
points: [
{ x: 0, y: 2 },
{ x: 1, y: 3 },
],
},
},
], ],
}, },
{ grid: { rowCount: 1, columnCount: 1 } }, { grid: { rowCount: 1, columnCount: 1 } },

View File

@@ -42,6 +42,19 @@ const presentationData: WaveformData = {
], ],
}, },
}, },
{
id: 'mid',
trackId: 'shared',
name: '中量程',
data: {
kind: 'points',
points: [
{ x: 0, y: 50 },
{ x: 1, y: 100 },
{ x: 2, y: 75 },
],
},
},
{ {
id: 'other', id: 'other',
name: '其他通道', name: '其他通道',
@@ -150,9 +163,9 @@ describe('WaveformChart presentation mode', () => {
cancelable: true, cancelable: true,
}), }),
) )
wrapper.get('.waveform-chart__svg').element.dispatchEvent( wrapper
new MouseEvent('dblclick', { bubbles: true, cancelable: true }), .get('.waveform-chart__svg')
) .element.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, cancelable: true }))
await wrapper.get('[data-annotation-id="note"]').trigger('contextmenu', { await wrapper.get('[data-annotation-id="note"]').trigger('contextmenu', {
clientX: width / 2, clientX: width / 2,
clientY: height / 2, clientY: height / 2,

View File

@@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'
import { createSimulatedWaveformData } from './simulatedWaveforms' import { createSimulatedWaveformData } from './simulatedWaveforms'
describe('simulated waveform data', () => { describe('simulated waveform data', () => {
it('creates deterministic, finite six-channel data', () => { it('creates deterministic, finite seven-channel data with a two-series second frame', () => {
const first = createSimulatedWaveformData() const first = createSimulatedWaveformData()
const second = createSimulatedWaveformData() const second = createSimulatedWaveformData()
@@ -11,8 +11,12 @@ describe('simulated waveform data', () => {
expect(first.kind).toBe('series') expect(first.kind).toBe('series')
if (first.kind !== 'series') return if (first.kind !== 'series') return
expect(first.series).toHaveLength(6) expect(first.series).toHaveLength(7)
expect(new Set(first.series.map((series) => series.id)).size).toBe(6) expect(new Set(first.series.map((series) => series.id)).size).toBe(7)
const secondFrame = first.series.filter(
(series) => series.trackId === 'simulated-harmonic-frame',
)
expect(secondFrame).toHaveLength(2)
expect(new Set(first.series.map((series) => series.shotNo))).toEqual(new Set(['13300'])) expect(new Set(first.series.map((series) => series.shotNo))).toEqual(new Set(['13300']))
first.series.forEach((series) => { first.series.forEach((series) => {
expect(series.data.kind).toBe('points') expect(series.data.kind).toBe('points')

View File

@@ -13,7 +13,7 @@ type ErrorGenerator = (
interface SimulatedSeriesDefinition extends Pick< interface SimulatedSeriesDefinition extends Pick<
WaveformSeries, WaveformSeries,
'id' | 'shotNo' | 'name' | 'unit' | 'lineType' | 'pointType' | 'errorBar' 'id' | 'trackId' | 'shotNo' | 'name' | 'unit' | 'color' | 'lineType' | 'pointType' | 'errorBar'
> { > {
signal: SignalGenerator signal: SignalGenerator
errors?: ErrorGenerator errors?: ErrorGenerator
@@ -60,6 +60,7 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
}, },
{ {
id: 'simulated-harmonic', id: 'simulated-harmonic',
trackId: 'simulated-harmonic-frame',
shotNo: '13300', shotNo: '13300',
name: '谐波扰动', name: '谐波扰动',
unit: 'V', unit: 'V',
@@ -68,6 +69,18 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
signal: (time) => signal: (time) =>
0.9 * Math.sin(TWO_PI * 0.55 * time) + 0.28 * Math.sin(TWO_PI * 2.2 * time + 0.4), 0.9 * Math.sin(TWO_PI * 0.55 * time) + 0.28 * Math.sin(TWO_PI * 2.2 * time + 0.4),
}, },
{
id: 'simulated-harmonic-reference',
trackId: 'simulated-harmonic-frame',
shotNo: '13300',
name: '谐波对比',
unit: 'V',
color: '#2ca02c',
lineType: 'linear',
pointType: 'none',
signal: (time) =>
0.62 * Math.sin(TWO_PI * 0.55 * time + 0.55) + 0.18 * Math.sin(TWO_PI * 2.2 * time - 0.2),
},
{ {
id: 'simulated-damped', id: 'simulated-damped',
shotNo: '13300', shotNo: '13300',

View File

@@ -58,6 +58,18 @@ export function visibilitySeries(): WaveformData {
], ],
}, },
}, },
{
id: 'mid',
trackId: 'shared-frame',
name: '中量程',
data: {
kind: 'points',
points: [
{ x: 0, y: 100 },
{ x: 1, y: 200 },
],
},
},
], ],
} }
} }