waveform-analysis 0.1.72 → 0.1.73
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 +28 -10
- package/dist/index.cjs +2 -2
- package/dist/index.js +2933 -2849
- package/dist/style.css +1 -1
- package/dist/types/components/annotation/useWaveformChartAnnotations.d.ts +0 -1
- package/dist/types/components/core/layout.d.ts +2 -1
- package/dist/types/components/core/types.d.ts +2 -0
- package/dist/types/components/core/useWaveformChartController.d.ts +0 -1
- package/dist/types/components/core/useWaveformData.d.ts +1 -0
- package/dist/types/components/core/yAxisPadding.d.ts +1 -1
- package/dist/types/components/interaction/useExtremaTickViewport.d.ts +5 -0
- package/dist/types/components/rendering/WaveformTrackAxes.vue.d.ts +1 -0
- package/dist/types/data/sx2Channel.d.ts +10 -0
- package/dist/types/demo/types.d.ts +1 -0
- package/dist/types/types/chart.d.ts +2 -0
- package/package.json +1 -1
- package/src/App.vue +3 -0
- package/src/components/WaveformChartView.vue +0 -3
- package/src/components/annotation/useWaveformChartAnnotations.ts +7 -23
- package/src/components/controls/WaveformToolbar.vue +19 -20
- package/src/components/core/layout.ts +16 -4
- package/src/components/core/trackLayoutBuilder.ts +8 -1
- package/src/components/core/types.ts +2 -0
- package/src/components/core/useWaveformData.ts +2 -0
- package/src/components/core/useWaveformLayout.ts +8 -0
- package/src/components/core/yAxisLayoutMetrics.ts +1 -0
- package/src/components/core/yAxisPadding.ts +23 -8
- package/src/components/interaction/useExtremaTickViewport.ts +43 -0
- package/src/components/rendering/WaveformTrack.vue +1 -0
- package/src/components/rendering/WaveformTrackAxes.vue +18 -0
- package/src/components/rendering/yAxisTickVisibility.ts +77 -16
- package/src/data/simulatedWaveforms.ts +10 -0
- package/src/data/sx2Channel.ts +315 -0
- package/src/demo/DemoYAxisPaddingControls.vue +7 -0
- package/src/demo/types.ts +1 -0
- package/src/index.ts +1 -0
- package/src/types/chart.ts +2 -0
|
@@ -28,6 +28,7 @@ export interface DisplaySeries {
|
|
|
28
28
|
source?: WaveformPointSource
|
|
29
29
|
xDomain: [number, number]
|
|
30
30
|
rawYMaximum?: number
|
|
31
|
+
rawYMinimum?: number
|
|
31
32
|
yDomain: [number, number]
|
|
32
33
|
hasErrorPoints: boolean
|
|
33
34
|
}
|
|
@@ -54,6 +55,7 @@ export interface TrackSeriesPath {
|
|
|
54
55
|
|
|
55
56
|
export interface WaveformYAxisLayout {
|
|
56
57
|
maximumTick?: number
|
|
58
|
+
minimumTick?: number
|
|
57
59
|
index: number
|
|
58
60
|
side: 'left' | 'right'
|
|
59
61
|
x: number
|
|
@@ -26,6 +26,7 @@ export interface PreparedWaveformSeries {
|
|
|
26
26
|
source: WaveformPointSource
|
|
27
27
|
xDomain: [number, number]
|
|
28
28
|
rawYMaximum?: number
|
|
29
|
+
rawYMinimum?: number
|
|
29
30
|
yDomain: [number, number]
|
|
30
31
|
hasErrorPoints: boolean
|
|
31
32
|
}
|
|
@@ -50,6 +51,7 @@ export function prepareWaveformSeries(data: WaveformData): PreparedWaveformSerie
|
|
|
50
51
|
: [],
|
|
51
52
|
),
|
|
52
53
|
rawYMaximum: Number.isFinite(sourceMetrics.yMaximum) ? sourceMetrics.yMaximum : undefined,
|
|
54
|
+
rawYMinimum: Number.isFinite(sourceMetrics.yMinimum) ? sourceMetrics.yMinimum : undefined,
|
|
53
55
|
hasErrorPoints: sourceMetrics.hasErrorPoints,
|
|
54
56
|
}
|
|
55
57
|
})
|
|
@@ -17,6 +17,7 @@ import type { ResolvedWaveformChartProps } from './waveformChartTypes'
|
|
|
17
17
|
import { applyXDomainStrategy } from './xDomain'
|
|
18
18
|
import { alignIntegerZoomDomain, normalizeIntegerZoomDomain } from '../interaction/integerZoom'
|
|
19
19
|
import { resolveViewportYDomains } from './yAxisLayoutMetrics'
|
|
20
|
+
import { useExtremaTickViewport } from '../interaction/useExtremaTickViewport'
|
|
20
21
|
import { useWaveformYAxisLayout } from './useWaveformYAxisLayout'
|
|
21
22
|
import type { useWaveformAnnotationInteraction } from '../annotation'
|
|
22
23
|
interface LayoutContext {
|
|
@@ -279,6 +280,13 @@ export function useWaveformLayout(context: LayoutContext) {
|
|
|
279
280
|
showEmptyTracks: hasWaveformData.value,
|
|
280
281
|
}),
|
|
281
282
|
)
|
|
283
|
+
useExtremaTickViewport(
|
|
284
|
+
() => props.axes?.y?.extremaTicksVisible !== false,
|
|
285
|
+
innerWidth,
|
|
286
|
+
trackLayouts,
|
|
287
|
+
sharedTransform,
|
|
288
|
+
independentTransforms,
|
|
289
|
+
)
|
|
282
290
|
const annotationLayoutsForTrack = (track: TrackLayout): AnnotationTrackLayout[] =>
|
|
283
291
|
track.seriesList.map((series) => ({
|
|
284
292
|
...track,
|
|
@@ -14,26 +14,41 @@ export function applyYAxisPadding(
|
|
|
14
14
|
viewport: [number, number] | undefined,
|
|
15
15
|
options: YAxisPaddingOptions | number,
|
|
16
16
|
): YAxisSeriesGroup {
|
|
17
|
-
if (group.fixed) return group
|
|
18
17
|
const config = typeof options === 'number' ? { upperPaddingRatio: options } : options
|
|
18
|
+
if (group.fixed) return withYAxisExtremaTicks(group, config?.extremaTicksVisible)
|
|
19
19
|
const upper = paddingRatio(config?.upperPaddingEnabled, config?.upperPaddingRatio)
|
|
20
20
|
const lower = paddingRatio(config?.lowerPaddingEnabled, config?.lowerPaddingRatio)
|
|
21
|
-
const maxima = group.seriesList.flatMap((series) =>
|
|
22
|
-
Number.isFinite(series.rawYMaximum) ? [series.rawYMaximum!] : [],
|
|
23
|
-
)
|
|
24
|
-
const maximumTick = upper > 0 && maxima.length ? Math.max(...maxima) : undefined
|
|
25
21
|
const [a, b] = group.domain
|
|
26
22
|
const span = b - a
|
|
27
23
|
const min = lower > 0 ? a - span * lower : a
|
|
28
24
|
const max = upper > 0 ? b + span * upper : b
|
|
25
|
+
return withYAxisExtremaTicks(
|
|
26
|
+
{
|
|
27
|
+
...group,
|
|
28
|
+
domain: viewport ?? [Number.isFinite(min) ? min : a, Number.isFinite(max) ? max : b],
|
|
29
|
+
},
|
|
30
|
+
config?.extremaTicksVisible,
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function withYAxisExtremaTicks(group: YAxisSeriesGroup, visible = true): YAxisSeriesGroup {
|
|
35
|
+
const maxima = group.seriesList.flatMap((series) =>
|
|
36
|
+
Number.isFinite(series.rawYMaximum) ? [series.rawYMaximum!] : [],
|
|
37
|
+
)
|
|
38
|
+
const minima = group.seriesList.flatMap((series) =>
|
|
39
|
+
Number.isFinite(series.rawYMinimum) ? [series.rawYMinimum!] : [],
|
|
40
|
+
)
|
|
29
41
|
return {
|
|
30
42
|
...group,
|
|
31
|
-
maximumTick,
|
|
32
|
-
|
|
43
|
+
maximumTick: visible && maxima.length ? Math.max(...maxima) : undefined,
|
|
44
|
+
minimumTick: visible && minima.length ? Math.min(...minima) : undefined,
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
|
|
36
|
-
export function withMaximumTick(values: number[], maximum?: number): number[] {
|
|
48
|
+
export function withMaximumTick(values: number[], maximum?: number, minimum?: number): number[] {
|
|
49
|
+
if (minimum !== undefined && Number.isFinite(minimum)) {
|
|
50
|
+
values = [...values.filter((value) => !sameTick(value, minimum)), minimum].sort((a, b) => a - b)
|
|
51
|
+
}
|
|
37
52
|
if (maximum === undefined || !Number.isFinite(maximum)) return values
|
|
38
53
|
return [...values.filter((value) => !sameTick(value, maximum)), maximum].sort((a, b) => a - b)
|
|
39
54
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { watch, type ComputedRef, type ShallowRef } from 'vue'
|
|
2
|
+
import type { ZoomTransform } from 'd3'
|
|
3
|
+
|
|
4
|
+
import type { TrackLayout } from '../core/types'
|
|
5
|
+
|
|
6
|
+
/** Keep X coordinates stable when toggling extrema changes the reserved Y-label width. */
|
|
7
|
+
export function useExtremaTickViewport(
|
|
8
|
+
visible: () => boolean,
|
|
9
|
+
innerWidth: ComputedRef<number>,
|
|
10
|
+
tracks: ComputedRef<TrackLayout[]>,
|
|
11
|
+
sharedTransform: ShallowRef<ZoomTransform>,
|
|
12
|
+
independentTransforms: ShallowRef<ZoomTransform[]>,
|
|
13
|
+
) {
|
|
14
|
+
const resizeTransform = (transform: ZoomTransform, previous: number, next: number) =>
|
|
15
|
+
previous > 0 && next > 0
|
|
16
|
+
? transform.translate(((next / previous - 1) * transform.x) / transform.k, 0)
|
|
17
|
+
: transform
|
|
18
|
+
|
|
19
|
+
watch(
|
|
20
|
+
() => ({
|
|
21
|
+
visible: visible(),
|
|
22
|
+
width: innerWidth.value,
|
|
23
|
+
tracks: tracks.value.map((track) => ({
|
|
24
|
+
id: track.id,
|
|
25
|
+
index: track.index,
|
|
26
|
+
width: track.width,
|
|
27
|
+
})),
|
|
28
|
+
}),
|
|
29
|
+
(next, previous) => {
|
|
30
|
+
if (next.visible === previous.visible) return
|
|
31
|
+
sharedTransform.value = resizeTransform(sharedTransform.value, previous.width, next.width)
|
|
32
|
+
const transforms = [...independentTransforms.value]
|
|
33
|
+
next.tracks.forEach((track) => {
|
|
34
|
+
const prior = previous.tracks.find((item) => item.id === track.id)
|
|
35
|
+
const transform = transforms[track.index]
|
|
36
|
+
if (prior && transform) {
|
|
37
|
+
transforms[track.index] = resizeTransform(transform, prior.width, track.width)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
independentTransforms.value = transforms
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -21,6 +21,7 @@ interface Props {
|
|
|
21
21
|
showUnits?: boolean
|
|
22
22
|
showLabelUnits?: boolean
|
|
23
23
|
hideYAxisTitles?: boolean
|
|
24
|
+
hideOverlappingMaximumLabel?: boolean
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
const props = withDefaults(defineProps<Props>(), { showUnits: true })
|
|
@@ -56,6 +57,7 @@ function renderAxes() {
|
|
|
56
57
|
const element = yAxisElements.value[index]
|
|
57
58
|
if (!element) return
|
|
58
59
|
const unit = props.showUnits ? axis.seriesList[0]?.unit : undefined
|
|
60
|
+
const highestTick = Math.max(...axis.tickValues)
|
|
59
61
|
const yAxis = (axis.side === 'left' ? axisLeft(axis.scale) : axisRight(axis.scale))
|
|
60
62
|
.tickFormat((value) =>
|
|
61
63
|
formatYAxisTickLabel(
|
|
@@ -78,11 +80,26 @@ function renderAxes() {
|
|
|
78
80
|
.attr('y', null)
|
|
79
81
|
.attr('dy', '0')
|
|
80
82
|
.attr('dominant-baseline', 'central')
|
|
83
|
+
.attr('data-axis-maximum-label', (value) =>
|
|
84
|
+
props.hideOverlappingMaximumLabel && sameTick(value, highestTick) ? 'true' : null,
|
|
85
|
+
)
|
|
86
|
+
.attr('data-axis-minimum-label', (value) =>
|
|
87
|
+
props.hideOverlappingMaximumLabel && sameTick(value, Math.min(...axis.tickValues))
|
|
88
|
+
? 'true'
|
|
89
|
+
: null,
|
|
90
|
+
)
|
|
91
|
+
selection
|
|
92
|
+
.attr('data-label-domain-min', axis.scale.domain()[0])
|
|
93
|
+
.attr('data-label-domain-max', axis.scale.domain()[1])
|
|
94
|
+
.attr('data-label-unit', unit ?? null)
|
|
81
95
|
selection
|
|
82
96
|
.selectAll<SVGGElement, number>('.tick')
|
|
83
97
|
.attr('data-maximum-tick', (value) =>
|
|
84
98
|
axis.maximumTick !== undefined && sameTick(value, axis.maximumTick) ? 'true' : null,
|
|
85
99
|
)
|
|
100
|
+
.attr('data-minimum-tick', (value) =>
|
|
101
|
+
axis.minimumTick !== undefined && sameTick(value, axis.minimumTick) ? 'true' : null,
|
|
102
|
+
)
|
|
86
103
|
.attr('data-tick-value', (value) => value)
|
|
87
104
|
.attr('data-tick-y', (value) => axis.scale(value))
|
|
88
105
|
selection
|
|
@@ -147,6 +164,7 @@ watch(
|
|
|
147
164
|
() => props.axes?.x?.labelFormatter,
|
|
148
165
|
() => props.axes?.x?.lineVisible,
|
|
149
166
|
() => props.axes?.y?.lineVisible,
|
|
167
|
+
() => props.hideOverlappingMaximumLabel,
|
|
150
168
|
],
|
|
151
169
|
async () => {
|
|
152
170
|
await nextTick()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sameTick } from '../core/yAxisPadding'
|
|
2
|
+
import { formatYAxisTickLabel } from '../core/layout'
|
|
2
3
|
|
|
3
4
|
interface Box {
|
|
4
5
|
left: number
|
|
@@ -23,7 +24,13 @@ function textBox(text: SVGTextElement): Box {
|
|
|
23
24
|
y += Number(match[2])
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
+
const startAligned = text.getAttribute('text-anchor') === 'start'
|
|
28
|
+
return {
|
|
29
|
+
left: startAligned ? x : x - width,
|
|
30
|
+
right: startAligned ? x + width : x,
|
|
31
|
+
top: y - height / 2,
|
|
32
|
+
bottom: y + height / 2,
|
|
33
|
+
}
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
export function labelsOverlap(a: Box, b: Box): boolean {
|
|
@@ -43,12 +50,46 @@ function strokeWidth(element: Element): number {
|
|
|
43
50
|
return Number.parseFloat(getComputedStyle(element).strokeWidth) || 1
|
|
44
51
|
}
|
|
45
52
|
|
|
53
|
+
function formatCompactLabels(svg: SVGSVGElement) {
|
|
54
|
+
svg.querySelectorAll<SVGTextElement>('text[data-axis-maximum-label]').forEach((maximum) => {
|
|
55
|
+
const axis = maximum.parentElement!.parentElement!
|
|
56
|
+
const labels = [...axis.querySelectorAll<SVGTextElement>('.tick text')]
|
|
57
|
+
const value = (text: SVGTextElement) =>
|
|
58
|
+
Number(text.parentElement!.getAttribute('data-tick-value'))
|
|
59
|
+
const visibleValues = labels
|
|
60
|
+
.filter((text) => text.getAttribute('display') !== 'none')
|
|
61
|
+
.map(value)
|
|
62
|
+
const domain: [number, number] = [
|
|
63
|
+
Number(axis.getAttribute('data-label-domain-min')),
|
|
64
|
+
Number(axis.getAttribute('data-label-domain-max')),
|
|
65
|
+
]
|
|
66
|
+
labels.forEach((text) => {
|
|
67
|
+
text.textContent = formatYAxisTickLabel(
|
|
68
|
+
value(text),
|
|
69
|
+
domain,
|
|
70
|
+
visibleValues,
|
|
71
|
+
axis.getAttribute('data-label-unit') ?? undefined,
|
|
72
|
+
axis.getAttribute('data-y-axis-side') === 'right' ? 'right' : 'left',
|
|
73
|
+
)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
46
78
|
/** Apply after axes and grid are in the DOM; never feed the filtered set back into layout. */
|
|
47
79
|
export function updateMaximumTickVisibility(svg: SVGSVGElement) {
|
|
48
80
|
svg.querySelectorAll('[data-maximum-hidden]').forEach((el) => {
|
|
49
81
|
el.removeAttribute('display')
|
|
50
82
|
el.removeAttribute('data-maximum-hidden')
|
|
51
83
|
})
|
|
84
|
+
svg.querySelectorAll('.waveform-track__axis--y .tick text').forEach((text) => {
|
|
85
|
+
text.removeAttribute('display')
|
|
86
|
+
})
|
|
87
|
+
formatCompactLabels(svg)
|
|
88
|
+
const minimumLabels = [...svg.querySelectorAll<SVGTextElement>('text[data-axis-minimum-label]')]
|
|
89
|
+
svg.querySelectorAll<SVGTextElement>('text[data-axis-maximum-label]').forEach((maximum) => {
|
|
90
|
+
const box = textBox(maximum)
|
|
91
|
+
if (minimumLabels.some((minimum) => labelsOverlap(box, textBox(minimum)))) hide(maximum)
|
|
92
|
+
})
|
|
52
93
|
svg.querySelectorAll<SVGGElement>('.waveform-track__axis--y .tick').forEach((tick) => {
|
|
53
94
|
const position = Number(tick.getAttribute('data-tick-y'))
|
|
54
95
|
const height = Number(tick.closest('.waveform-track')?.getAttribute('data-track-height'))
|
|
@@ -56,33 +97,49 @@ export function updateMaximumTickVisibility(svg: SVGSVGElement) {
|
|
|
56
97
|
if (sameTick(position, 0) || sameTick(position, height)) line?.setAttribute('display', 'none')
|
|
57
98
|
else line?.removeAttribute('display')
|
|
58
99
|
})
|
|
100
|
+
const minimumTicks = [...svg.querySelectorAll<SVGGElement>('.tick[data-minimum-tick]')]
|
|
59
101
|
const maximumTicks = [...svg.querySelectorAll<SVGGElement>('.tick[data-maximum-tick]')]
|
|
60
102
|
const labels = [...svg.querySelectorAll<SVGTextElement>('.waveform-track__axis--y .tick text')]
|
|
61
|
-
|
|
103
|
+
minimumTicks.forEach((tick) => {
|
|
104
|
+
const text = tick.querySelector('text')
|
|
105
|
+
if (!text) return
|
|
106
|
+
maximumTicks.forEach((maximum) => {
|
|
107
|
+
const other = maximum.querySelector('text')
|
|
108
|
+
if (other && other !== text && labelsOverlap(textBox(text), textBox(other))) hide(other)
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
const extremeTicks = [...new Set([...minimumTicks, ...maximumTicks])]
|
|
112
|
+
extremeTicks.forEach((tick) => {
|
|
62
113
|
const text = tick.querySelector('text')
|
|
63
114
|
const line = tick.querySelector('line')
|
|
64
115
|
if (!text || !line) return
|
|
65
116
|
const box = textBox(text)
|
|
66
117
|
labels.forEach((other) => {
|
|
67
|
-
if (
|
|
118
|
+
if (text.getAttribute('display') === 'none') return
|
|
119
|
+
if (other.getAttribute('display') === 'none') return
|
|
120
|
+
if (other.parentElement?.matches('[data-minimum-tick], [data-maximum-tick]')) return
|
|
121
|
+
if (!tick.hasAttribute('data-minimum-tick') && other.hasAttribute('data-axis-minimum-label'))
|
|
122
|
+
return
|
|
68
123
|
if (labelsOverlap(box, textBox(other))) hide(other)
|
|
69
124
|
})
|
|
70
125
|
const axis = tick.parentElement!
|
|
71
126
|
const value = Number(tick.getAttribute('data-tick-value'))
|
|
72
127
|
const position = Number(tick.getAttribute('data-tick-y'))
|
|
73
|
-
axis
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
otherLine
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
128
|
+
axis
|
|
129
|
+
.querySelectorAll<SVGGElement>('.tick:not([data-maximum-tick]):not([data-minimum-tick])')
|
|
130
|
+
.forEach((other) => {
|
|
131
|
+
const otherLine = other.querySelector('line')
|
|
132
|
+
if (
|
|
133
|
+
otherLine &&
|
|
134
|
+
linesOverlap(
|
|
135
|
+
position,
|
|
136
|
+
Number(other.getAttribute('data-tick-y')),
|
|
137
|
+
strokeWidth(line),
|
|
138
|
+
strokeWidth(otherLine),
|
|
139
|
+
)
|
|
82
140
|
)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
})
|
|
141
|
+
hide(otherLine)
|
|
142
|
+
})
|
|
86
143
|
// Only the primary Y axis owns the existing horizontal grid.
|
|
87
144
|
if (axis.getAttribute('data-y-axis-index') !== '0') return
|
|
88
145
|
const track = axis.closest('.waveform-track')!
|
|
@@ -90,6 +147,10 @@ export function updateMaximumTickVisibility(svg: SVGSVGElement) {
|
|
|
90
147
|
const gridY = Number(gridLine.getAttribute('y1'))
|
|
91
148
|
if (linesOverlap(position, gridY, strokeWidth(line), strokeWidth(gridLine))) hide(gridLine)
|
|
92
149
|
})
|
|
93
|
-
tick.
|
|
150
|
+
if (tick.hasAttribute('data-maximum-tick'))
|
|
151
|
+
tick.setAttribute('data-maximum-value', String(value))
|
|
152
|
+
if (tick.hasAttribute('data-minimum-tick'))
|
|
153
|
+
tick.setAttribute('data-minimum-value', String(value))
|
|
94
154
|
})
|
|
155
|
+
formatCompactLabels(svg)
|
|
95
156
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { WaveformData, WaveformPoint, WaveformSeries } from '../types'
|
|
2
2
|
import { recordedDampedPoints } from './recordedDampedWaveform'
|
|
3
|
+
import { sx2Channel } from './sx2Channel'
|
|
3
4
|
|
|
4
5
|
const POINT_COUNT = 1_000
|
|
5
6
|
const START_TIME = -5
|
|
@@ -176,6 +177,15 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
|
176
177
|
pointType: 'none',
|
|
177
178
|
signal: (time) => 6e-9 * Math.exp(-(time + 5) * 0.2) * Math.cos(TWO_PI * 0.8 * time),
|
|
178
179
|
},
|
|
180
|
+
{
|
|
181
|
+
id: `recorded-${sx2Channel.chnl_id}`,
|
|
182
|
+
shotNo: String(sx2Channel.shot),
|
|
183
|
+
name: sx2Channel.chnl,
|
|
184
|
+
unit: sx2Channel.dat_unit,
|
|
185
|
+
lineType: 'linear',
|
|
186
|
+
pointType: 'none',
|
|
187
|
+
signal: sx2Channel.data.map((value, index) => [sx2Channel.time[index]!, value]),
|
|
188
|
+
},
|
|
179
189
|
]
|
|
180
190
|
|
|
181
191
|
export function createSimulatedWaveformData(trackCount?: number): WaveformData {
|