waveform-analysis 0.1.75 → 0.1.77
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 +183 -42
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/dist/types/components/animation/context.d.ts +1 -0
- package/dist/types/components/animation/paginationPresentation.d.ts +7 -7
- package/dist/types/components/animation/useWaveformAnimations.d.ts +1 -18
- package/dist/types/components/core/amplitudeLimits.d.ts +6 -0
- package/dist/types/components/core/layout.d.ts +6 -9
- package/dist/types/components/core/trackLayoutBuilder.d.ts +3 -1
- package/dist/types/components/core/useWaveformChartController.d.ts +2 -19
- package/dist/types/components/core/useWaveformData.d.ts +2 -2
- package/dist/types/components/core/useWaveformMeasurements.d.ts +4 -1
- package/dist/types/components/core/waveformChartTypes.d.ts +4 -2
- package/dist/types/components/core/xAxisTicks.d.ts +2 -0
- package/dist/types/components/core/yAxisLayoutMetrics.d.ts +2 -1
- package/dist/types/components/core/yAxisTicks.d.ts +6 -0
- package/dist/types/components/data/types.d.ts +1 -1
- package/dist/types/components/interaction/integerZoom.d.ts +3 -3
- package/dist/types/components/interaction/stepZoom.d.ts +5 -0
- package/dist/types/components/interaction/useWaveformViewport.d.ts +0 -3
- package/dist/types/components/interaction/useWaveformZoom.d.ts +0 -2
- package/dist/types/components/rendering/WaveformSeriesLayer.vue.d.ts +0 -1
- package/dist/types/components/rendering/WaveformTrack.vue.d.ts +0 -1
- package/dist/types/core/data.d.ts +1 -1
- package/dist/types/demo/DemoChartHost.vue.d.ts +1 -1
- package/dist/types/demo/remoteWaveformLoader.d.ts +23 -0
- package/dist/types/demo/types.d.ts +11 -2
- package/dist/types/demo/useRemoteWaveformWindow.d.ts +22 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types/chart.d.ts +8 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/utils/formatters.d.ts +4 -4
- package/dist/types/utils/xAxisNumber.d.ts +2 -0
- package/package.json +1 -1
- package/src/App.vue +34 -100
- package/src/components/WaveformChartView.vue +0 -4
- package/src/components/animation/config.ts +1 -1
- package/src/components/animation/context.ts +9 -1
- package/src/components/animation/paginationPresentation.ts +55 -64
- package/src/components/animation/useWaveformAnimations.ts +36 -13
- package/src/components/controls/useChartCommands.ts +12 -13
- package/src/components/core/amplitudeLimits.ts +44 -0
- package/src/components/core/layout.ts +22 -34
- package/src/components/core/trackLayoutBuilder.ts +12 -5
- package/src/components/core/useWaveformChartController.ts +3 -4
- package/src/components/core/useWaveformChartLifecycle.ts +11 -2
- package/src/components/core/useWaveformData.ts +11 -5
- package/src/components/core/useWaveformLayout.ts +9 -4
- package/src/components/core/useWaveformMeasurements.ts +3 -0
- package/src/components/core/useWaveformYAxisLayout.ts +2 -0
- package/src/components/core/waveformChartTypes.ts +4 -1
- package/src/components/core/xAxisTicks.ts +69 -0
- package/src/components/core/yAxisLayoutMetrics.ts +4 -0
- package/src/components/core/yAxisTicks.ts +39 -0
- package/src/components/data/types.ts +2 -0
- package/src/components/interaction/integerZoom.ts +31 -12
- package/src/components/interaction/remoteViewport.ts +26 -4
- package/src/components/interaction/stepZoom.ts +33 -0
- package/src/components/interaction/useWaveformViewport.ts +3 -26
- package/src/components/interaction/useWaveformZoom.ts +50 -157
- package/src/components/interaction/zoomConstraints.ts +5 -5
- package/src/components/rendering/WaveformSeriesLayer.vue +1 -2
- package/src/components/rendering/WaveformTrack.vue +1 -6
- package/src/core/data.ts +5 -2
- package/src/data/simulatedWaveforms.ts +0 -10
- package/src/demo/DemoChartHost.vue +49 -4
- package/src/demo/remoteWaveformLoader.ts +114 -0
- package/src/demo/types.ts +11 -1
- package/src/demo/useRemoteWaveformWindow.ts +229 -0
- package/src/index.ts +2 -0
- package/src/types/chart.ts +10 -1
- package/src/types/index.ts +2 -0
- package/src/utils/formatters.ts +10 -5
- package/src/utils/xAxisNumber.ts +36 -0
- package/src/views/AnimationDemo.vue +14 -5
- package/dist/types/data/sx2Channel.d.ts +0 -10
- package/src/data/sx2Channel.ts +0 -315
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { scaleLinear } from 'd3'
|
|
2
|
+
|
|
3
|
+
const MAX_TICK_COUNT = 3000
|
|
4
|
+
|
|
5
|
+
/** ECharts-style numeric ticks inside the viewport; never expands its domain. */
|
|
6
|
+
export function resolveXAxisTicks(
|
|
7
|
+
domain: readonly [number, number],
|
|
8
|
+
splitNumber: number,
|
|
9
|
+
minInterval = 0,
|
|
10
|
+
): number[] {
|
|
11
|
+
const [start, end] = domain
|
|
12
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return []
|
|
13
|
+
if (start === end) return normalizeTicks([start], start, end)
|
|
14
|
+
if (start > end) return resolveXAxisTicks([end, start], splitNumber, minInterval).reverse()
|
|
15
|
+
|
|
16
|
+
const count = Number.isFinite(splitNumber) ? Math.max(1, Math.round(splitNumber)) : 5
|
|
17
|
+
const roughInterval = (end - start) / count
|
|
18
|
+
const exponent = Math.floor(Math.log10(roughInterval))
|
|
19
|
+
const power = 10 ** exponent
|
|
20
|
+
const fraction = roughInterval / power
|
|
21
|
+
const coefficient =
|
|
22
|
+
fraction < 1.5 ? 1 : fraction < 2.5 ? 2 : fraction < 4 ? 3 : fraction < 7 ? 5 : 10
|
|
23
|
+
const interval = Math.max(coefficient * power, minInterval)
|
|
24
|
+
const precision = Math.max(0, 2 - Math.floor(Math.log10(interval)))
|
|
25
|
+
const fallback = () => fallbackTicks(start, end, count)
|
|
26
|
+
|
|
27
|
+
// Extreme magnitudes may exceed decimal rounding or safe integer index precision.
|
|
28
|
+
if (!Number.isFinite(interval) || interval <= 0 || precision > 100) return fallback()
|
|
29
|
+
const first = Math.ceil(snapTickIndex(start / interval))
|
|
30
|
+
const last = Math.floor(snapTickIndex(end / interval))
|
|
31
|
+
if (!Number.isSafeInteger(first) || !Number.isSafeInteger(last)) return fallback()
|
|
32
|
+
if (last - first + 1 > MAX_TICK_COUNT) return fallback()
|
|
33
|
+
|
|
34
|
+
const ticks: number[] = []
|
|
35
|
+
for (let index = first; index <= last; index++) {
|
|
36
|
+
const value = Number((index * interval).toFixed(precision))
|
|
37
|
+
if (ticks.length && value <= ticks[ticks.length - 1]!) return fallback()
|
|
38
|
+
ticks.push(value)
|
|
39
|
+
}
|
|
40
|
+
return normalizeTicks(ticks, start, end)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function fallbackTicks(start: number, end: number, count: number): number[] {
|
|
44
|
+
const scale = scaleLinear().domain([start, end])
|
|
45
|
+
let targetCount = Math.min(count, MAX_TICK_COUNT)
|
|
46
|
+
while (true) {
|
|
47
|
+
const ticks = normalizeTicks(scale.ticks(targetCount), start, end)
|
|
48
|
+
if (ticks.length <= MAX_TICK_COUNT) return ticks
|
|
49
|
+
// D3 treats count as a hint. Regenerate with a wider interval instead of truncating.
|
|
50
|
+
targetCount = Math.max(1, Math.floor(targetCount / 2))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeTicks(values: number[], start: number, end: number): number[] {
|
|
55
|
+
return [
|
|
56
|
+
...new Set(
|
|
57
|
+
values
|
|
58
|
+
.filter((value) => Number.isFinite(value) && value >= start && value <= end)
|
|
59
|
+
.map((value) => (value === 0 ? 0 : value)),
|
|
60
|
+
),
|
|
61
|
+
].sort((a, b) => a - b)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function snapTickIndex(value: number): number {
|
|
65
|
+
const rounded = Math.round(value)
|
|
66
|
+
return Math.abs(value - rounded) <= Number.EPSILON * Math.max(1, Math.abs(value)) * 4
|
|
67
|
+
? rounded
|
|
68
|
+
: value
|
|
69
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WaveformAmplitudeLimits } from '../../types'
|
|
1
2
|
import type { WaveformOverlayMode } from '../data/types'
|
|
2
3
|
import {
|
|
3
4
|
Y_AXIS_CHARACTER_WIDTH,
|
|
@@ -44,6 +45,7 @@ export function resolveYAxisLayoutMetrics(
|
|
|
44
45
|
padding?: YAxisPaddingOptions | number,
|
|
45
46
|
showUnits = true,
|
|
46
47
|
measureTextWidth?: (text: string) => number,
|
|
48
|
+
amplitudeLimits?: WaveformAmplitudeLimits,
|
|
47
49
|
): YAxisLayoutMetrics {
|
|
48
50
|
const labelBandWidth = resolveYAxisLabelBandWidth(
|
|
49
51
|
typeof padding === 'object' ? padding.labelTextStyle : undefined,
|
|
@@ -58,6 +60,7 @@ export function resolveYAxisLayoutMetrics(
|
|
|
58
60
|
yDomains,
|
|
59
61
|
viewportYDomains,
|
|
60
62
|
padding,
|
|
63
|
+
amplitudeLimits,
|
|
61
64
|
),
|
|
62
65
|
)
|
|
63
66
|
.map((group) =>
|
|
@@ -71,6 +74,7 @@ export function resolveYAxisLayoutMetrics(
|
|
|
71
74
|
measureTextWidth,
|
|
72
75
|
group.maximumTick,
|
|
73
76
|
group.minimumTick,
|
|
77
|
+
group.amplitudeLimit,
|
|
74
78
|
),
|
|
75
79
|
)
|
|
76
80
|
const tickTextWidth = Math.max(
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WaveformAmplitudeLimit } from '../../types'
|
|
2
|
+
import { resolveYAxisTicksECharts } from './niceScale'
|
|
3
|
+
|
|
4
|
+
export interface ResolvedYAxisTicks {
|
|
5
|
+
domain: [number, number]
|
|
6
|
+
values: number[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveYAxisTicks(
|
|
10
|
+
domain: [number, number],
|
|
11
|
+
tickCount = 5,
|
|
12
|
+
nice = true,
|
|
13
|
+
limit?: WaveformAmplitudeLimit,
|
|
14
|
+
): ResolvedYAxisTicks {
|
|
15
|
+
if (!nice) {
|
|
16
|
+
// 如果不需要 nice,使用简单的线性分割
|
|
17
|
+
const effectiveTickCount =
|
|
18
|
+
typeof tickCount === 'number' && Number.isFinite(tickCount)
|
|
19
|
+
? Math.max(2, Math.floor(tickCount))
|
|
20
|
+
: 5
|
|
21
|
+
const [axisStart, axisEnd] = domain
|
|
22
|
+
return {
|
|
23
|
+
domain,
|
|
24
|
+
values: Array.from(
|
|
25
|
+
{ length: effectiveTickCount },
|
|
26
|
+
(_, index) => axisStart + ((axisEnd - axisStart) * index) / (effectiveTickCount - 1),
|
|
27
|
+
),
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 使用 ECharts 兼容的 1/2/5 系列算法
|
|
32
|
+
const ticks = resolveYAxisTicksECharts(domain, tickCount)
|
|
33
|
+
if (!limit) return ticks
|
|
34
|
+
const bounded: [number, number] = [limit.lower ?? ticks.domain[0], limit.upper ?? ticks.domain[1]]
|
|
35
|
+
return {
|
|
36
|
+
domain: bounded,
|
|
37
|
+
values: ticks.values.filter((value) => value >= bounded[0] && value <= bounded[1]),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveXAxisTicks } from '../core/xAxisTicks'
|
|
2
|
+
import { formatPlainNumber } from '../../utils/formatters'
|
|
2
3
|
|
|
3
4
|
export interface IntegerZoomOptions {
|
|
4
5
|
integerZoom?: boolean
|
|
@@ -10,17 +11,20 @@ export function integerZoomTicks(
|
|
|
10
11
|
count: number,
|
|
11
12
|
options: IntegerZoomOptions,
|
|
12
13
|
) {
|
|
13
|
-
if (!options.integerZoom) return
|
|
14
|
+
if (!options.integerZoom) return resolveXAxisTicks(domain, count)
|
|
14
15
|
const factor = options.timeUnit === 's' ? 1 : 1000
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
16
|
+
const fractional = hasFractionalZoomDomain(domain, options)
|
|
17
|
+
return resolveXAxisTicks(
|
|
18
|
+
domain.map((value) => value * factor) as [number, number],
|
|
19
|
+
count,
|
|
20
|
+
fractional ? 0 : 1,
|
|
21
|
+
)
|
|
22
|
+
.filter((value) => fractional || Number.isInteger(value))
|
|
19
23
|
.map((value) => value / factor)
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export function formatIntegerZoomLabel(value: number): string {
|
|
23
|
-
return
|
|
27
|
+
return formatPlainNumber(Number(stableInteger(value).toPrecision(15)))
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
function stableInteger(value: number): number {
|
|
@@ -30,7 +34,17 @@ function stableInteger(value: number): number {
|
|
|
30
34
|
: value
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
function displayDomain(domain: [number, number], options: IntegerZoomOptions): [number, number] {
|
|
38
|
+
const factor = options.timeUnit === 's' ? 1 : 1000
|
|
39
|
+
return [stableInteger(domain[0] * factor), stableInteger(domain[1] * factor)]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Fractional viewports, including those panned across an integer, need decimal ticks. */
|
|
43
|
+
function hasFractionalZoomDomain(domain: [number, number], options: IntegerZoomOptions): boolean {
|
|
44
|
+
return displayDomain(domain, options).some((value) => !Number.isInteger(value))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Align outwards only when the displayed span reaches one unit. */
|
|
34
48
|
export function alignIntegerZoomDomain(
|
|
35
49
|
domain: [number, number],
|
|
36
50
|
options: IntegerZoomOptions,
|
|
@@ -41,24 +55,29 @@ export function alignIntegerZoomDomain(
|
|
|
41
55
|
const end = stableInteger(Math.max(domain[0], domain[1]) * factor)
|
|
42
56
|
if (!Number.isSafeInteger(Math.floor(start)) || !Number.isSafeInteger(Math.ceil(end)))
|
|
43
57
|
return domain
|
|
58
|
+
if (end > start && end - start < 1) return [start / factor, end / factor]
|
|
44
59
|
const left = Math.floor(start)
|
|
45
60
|
return [left / factor, Math.max(left + 1, Math.ceil(end)) / factor]
|
|
46
61
|
}
|
|
47
62
|
|
|
48
|
-
/** Recover
|
|
63
|
+
/** Recover endpoints after a D3 transform without applying a new zoom or changing pan span. */
|
|
49
64
|
export function normalizeIntegerZoomDomain(
|
|
50
65
|
domain: [number, number],
|
|
51
66
|
options: IntegerZoomOptions,
|
|
52
67
|
): [number, number] {
|
|
53
|
-
|
|
68
|
+
if (!options.integerZoom) return domain
|
|
69
|
+
const factor = options.timeUnit === 's' ? 1 : 1000
|
|
70
|
+
return displayDomain(domain, options).map((value) => value / factor) as [number, number]
|
|
54
71
|
}
|
|
55
72
|
|
|
56
|
-
/**
|
|
73
|
+
/** Integer viewports snap translation; sub-unit viewports pan freely across integers. */
|
|
57
74
|
export function alignIntegerPanDomain(
|
|
58
75
|
domain: [number, number],
|
|
59
76
|
options: IntegerZoomOptions,
|
|
60
77
|
): [number, number] {
|
|
61
78
|
if (!options.integerZoom) return domain
|
|
62
79
|
const factor = options.timeUnit === 's' ? 1 : 1000
|
|
63
|
-
|
|
80
|
+
if (Math.abs(domain[1] - domain[0]) * factor < 1) return domain
|
|
81
|
+
const start = Math.round(domain[0] * factor) / factor
|
|
82
|
+
return [start, start + (domain[1] - domain[0])]
|
|
64
83
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ResolvedWaveformChartProps } from '../core/waveformChartTypes'
|
|
2
|
+
import { hasMinimumVisibleXValues } from '../../core/rendering'
|
|
3
|
+
import { normalizeIntegerZoomDomain } from './integerZoom'
|
|
2
4
|
import {
|
|
3
5
|
constrainZoomDomain,
|
|
4
6
|
resolveMinimumZoomSpan,
|
|
@@ -32,7 +34,13 @@ export function restoreViewportDomain(
|
|
|
32
34
|
const boundary = remotePanBoundary(props)
|
|
33
35
|
return boundary
|
|
34
36
|
? clampViewportDomain(domain, boundary)
|
|
35
|
-
:
|
|
37
|
+
: normalizeIntegerZoomDomain(
|
|
38
|
+
constrainZoomDomain(domain, baseDomain, groups, {
|
|
39
|
+
...props,
|
|
40
|
+
integerZoom: false,
|
|
41
|
+
}),
|
|
42
|
+
props,
|
|
43
|
+
)
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
/** Wheel/box zoom keeps the remote position and uses loaded data for scale limits. */
|
|
@@ -44,10 +52,24 @@ export function constrainViewportZoom(
|
|
|
44
52
|
) {
|
|
45
53
|
const boundary = remotePanBoundary(props)
|
|
46
54
|
if (!boundary) return constrainZoomDomain(domain, baseDomain, groups, props)
|
|
47
|
-
|
|
55
|
+
const availableGroups = groups.filter((group) =>
|
|
56
|
+
hasMinimumVisibleXValues(group, boundary, props.minVisiblePoints),
|
|
57
|
+
)
|
|
58
|
+
// Only constrain against loaded groups that overlap the requested window. An
|
|
59
|
+
// unloaded remote window must remain free to move beyond the cached samples.
|
|
60
|
+
const loadedGroups = availableGroups.filter((group) =>
|
|
61
|
+
group.some((series) => {
|
|
62
|
+
const source = series.source
|
|
63
|
+
const length = source?.length ?? series.points.length
|
|
64
|
+
if (!length) return false
|
|
65
|
+
const first = source ? source.pointAt(0)! : series.points[0]
|
|
66
|
+
const last = source ? source.pointAt(length - 1)! : series.points[length - 1]
|
|
67
|
+
return first.x <= Math.max(...domain) && last.x >= Math.min(...domain)
|
|
68
|
+
}),
|
|
69
|
+
)
|
|
70
|
+
return constrainZoomDomain(domain, boundary, loadedGroups, {
|
|
48
71
|
...props,
|
|
49
|
-
minVisiblePoints: 0,
|
|
50
72
|
maxZoomScale: null,
|
|
51
|
-
minZoomSpan: resolveMinimumZoomSpan(baseDomain,
|
|
73
|
+
minZoomSpan: resolveMinimumZoomSpan(baseDomain, availableGroups, props),
|
|
52
74
|
})
|
|
53
75
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ResolvedWaveformChartProps } from '../core/waveformChartTypes'
|
|
2
|
+
import { normalizeIntegerZoomDomain } from './integerZoom'
|
|
3
|
+
import { constrainViewportZoom } from './remoteViewport'
|
|
4
|
+
import type { ZoomSeriesGroup } from './zoomConstraints'
|
|
5
|
+
|
|
6
|
+
export const sameZoomDomain = (a: [number, number], b: [number, number]) =>
|
|
7
|
+
a.every((value, index) => Math.abs(value - b[index]!) < 1e-12)
|
|
8
|
+
|
|
9
|
+
/** Toolbar and wheel steps share their scale factor and constraints. */
|
|
10
|
+
export function resolveStepZoomDomain(
|
|
11
|
+
current: [number, number],
|
|
12
|
+
action: 'zoom-in' | 'zoom-out',
|
|
13
|
+
boundary: [number, number],
|
|
14
|
+
groups: readonly ZoomSeriesGroup[],
|
|
15
|
+
props: ResolvedWaveformChartProps,
|
|
16
|
+
anchorRatio = 0.5,
|
|
17
|
+
): [number, number] {
|
|
18
|
+
const [start, end] = current
|
|
19
|
+
const ratio = Math.max(0, Math.min(1, anchorRatio))
|
|
20
|
+
const anchor = start + (end - start) * ratio
|
|
21
|
+
const span = (end - start) * (action === 'zoom-in' ? 0.5 : 2)
|
|
22
|
+
const constrained = constrainViewportZoom(
|
|
23
|
+
normalizeIntegerZoomDomain([anchor - span * ratio, anchor + span * (1 - ratio)], props),
|
|
24
|
+
boundary,
|
|
25
|
+
groups,
|
|
26
|
+
props,
|
|
27
|
+
)
|
|
28
|
+
// An unsatisfiable point constraint may expand to the entire boundary.
|
|
29
|
+
// A zoom-in step must not turn into a zoom-out operation.
|
|
30
|
+
return action === 'zoom-in' && constrained[1] - constrained[0] > end - start
|
|
31
|
+
? [...current]
|
|
32
|
+
: constrained
|
|
33
|
+
}
|
|
@@ -12,6 +12,7 @@ import { constrainViewportZoom } from './remoteViewport'
|
|
|
12
12
|
import { createViewportPan } from './viewportPan'
|
|
13
13
|
import { transformForDomain } from './zoomConstraints'
|
|
14
14
|
import { emitBoxZoomIntent } from './zoomEventPayload'
|
|
15
|
+
import { sameZoomDomain } from './stepZoom'
|
|
15
16
|
export interface ViewportContext {
|
|
16
17
|
clearBoundaries?: (trackIndex?: number) => void
|
|
17
18
|
props: ResolvedWaveformChartProps
|
|
@@ -32,8 +33,6 @@ export interface ViewportContext {
|
|
|
32
33
|
isPresentationMode: ComputedRef<boolean>
|
|
33
34
|
editorSeriesOptions: Ref<AnnotationSeriesCandidate[]>
|
|
34
35
|
resolveInitialTrackDomain: (track: TrackLayout) => [number, number]
|
|
35
|
-
canZoomTrack: (track: TrackLayout) => boolean
|
|
36
|
-
canZoomSharedTracks: () => boolean
|
|
37
36
|
configureZoom: () => void
|
|
38
37
|
cancelPendingZoom: () => void
|
|
39
38
|
clearHover: () => void
|
|
@@ -59,8 +58,6 @@ export function useWaveformViewport(context: ViewportContext) {
|
|
|
59
58
|
isPresentationMode,
|
|
60
59
|
editorSeriesOptions,
|
|
61
60
|
resolveInitialTrackDomain,
|
|
62
|
-
canZoomTrack,
|
|
63
|
-
canZoomSharedTracks,
|
|
64
61
|
configureZoom,
|
|
65
62
|
cancelPendingZoom,
|
|
66
63
|
clearHover,
|
|
@@ -184,7 +181,7 @@ export function useWaveformViewport(context: ViewportContext) {
|
|
|
184
181
|
}
|
|
185
182
|
const applyBoxZoom = (active: ViewportSelectionState) => {
|
|
186
183
|
const track = trackLayouts.value.find((item) => item.index === active.trackIndex)
|
|
187
|
-
if (!track
|
|
184
|
+
if (!track) return
|
|
188
185
|
const offsetX = active.independent ? 0 : track.left
|
|
189
186
|
const left = Math.max(
|
|
190
187
|
0,
|
|
@@ -205,6 +202,7 @@ export function useWaveformViewport(context: ViewportContext) {
|
|
|
205
202
|
groups,
|
|
206
203
|
props,
|
|
207
204
|
)
|
|
205
|
+
if (sameZoomDomain(track.xScale.domain() as [number, number], xDomain)) return
|
|
208
206
|
if (active.independent) {
|
|
209
207
|
const next = [...independentTransforms.value]
|
|
210
208
|
next[track.index] = transformForDomain(xDomain, baseXDomain, track.width)
|
|
@@ -333,26 +331,6 @@ export function useWaveformViewport(context: ViewportContext) {
|
|
|
333
331
|
editorSeriesOptions,
|
|
334
332
|
configureZoom,
|
|
335
333
|
})
|
|
336
|
-
const requestViewportReset = (event: MouseEvent) => {
|
|
337
|
-
if (isPresentationMode.value || !props.zoomable || !isZoomMode.value) return
|
|
338
|
-
if (props.displayMode === 'independent') {
|
|
339
|
-
const target = event.target instanceof Element ? event.target : null
|
|
340
|
-
const overlay = target?.closest('[data-independent-overlay-index]')
|
|
341
|
-
const trackIndex = Number(overlay?.getAttribute('data-independent-overlay-index'))
|
|
342
|
-
const track = trackLayouts.value.find((item) => item.index === trackIndex)
|
|
343
|
-
if (!track) return
|
|
344
|
-
event.preventDefault()
|
|
345
|
-
resetViewport(trackIndex)
|
|
346
|
-
emit('zoom-reset', {
|
|
347
|
-
trackIndex,
|
|
348
|
-
seriesIds: track.legendSeries.map((series) => series.id),
|
|
349
|
-
})
|
|
350
|
-
return
|
|
351
|
-
}
|
|
352
|
-
event.preventDefault()
|
|
353
|
-
resetViewport()
|
|
354
|
-
emit('zoom-reset', {})
|
|
355
|
-
}
|
|
356
334
|
return {
|
|
357
335
|
selectionBox,
|
|
358
336
|
beginViewportDrag,
|
|
@@ -362,6 +340,5 @@ export function useWaveformViewport(context: ViewportContext) {
|
|
|
362
340
|
cancelViewportDrag,
|
|
363
341
|
resetViewport,
|
|
364
342
|
setViewportDomain,
|
|
365
|
-
requestViewportReset,
|
|
366
343
|
}
|
|
367
344
|
}
|