waveform-analysis 0.1.58 → 0.1.60

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 (55) hide show
  1. package/README.md +9 -6
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +894 -817
  4. package/dist/types/components/WaveformChart.vue.d.ts +2 -0
  5. package/dist/types/components/core/layout.d.ts +9 -2
  6. package/dist/types/components/core/useWaveformChartLifecycle.d.ts +1 -2
  7. package/dist/types/components/core/waveformChartTypes.d.ts +2 -1
  8. package/dist/types/components/core/yAxisLayoutMetrics.d.ts +10 -0
  9. package/dist/types/components/data/types.d.ts +1 -1
  10. package/dist/types/components/index.d.ts +1 -1
  11. package/dist/types/components/interaction/useWaveformZoom.d.ts +2 -1
  12. package/dist/types/components/interaction/zoomEventPayload.d.ts +7 -0
  13. package/dist/types/core/rendering.d.ts +1 -1
  14. package/dist/types/core/renderingOptions.d.ts +1 -1
  15. package/dist/types/core/wasmSampling.d.ts +1 -1
  16. package/dist/types/core/workerSampling/multiresolution.d.ts +1 -1
  17. package/dist/types/core/workerSampling/protocol.d.ts +1 -1
  18. package/dist/types/core/workerSampling/repositoryDataset.d.ts +1 -1
  19. package/dist/types/core/workerSampling/repositorySupport.d.ts +1 -1
  20. package/dist/types/index.d.ts +1 -1
  21. package/dist/types/types/chart.d.ts +8 -0
  22. package/dist/types/types/index.d.ts +1 -1
  23. package/dist/types/utils/geometry.d.ts +1 -1
  24. package/dist/types/wasm/waveform_sampling_wasm.d.ts +74 -0
  25. package/dist/types/wasm/waveform_sampling_wasm_bg.wasm.d.ts +19 -0
  26. package/package.json +5 -4
  27. package/src/components/core/layout.test.ts +24 -0
  28. package/src/components/core/layout.ts +75 -13
  29. package/src/components/core/trackLayoutBuilder.ts +7 -13
  30. package/src/components/core/useWaveformChartController.ts +6 -1
  31. package/src/components/core/useWaveformChartLifecycle.ts +15 -9
  32. package/src/components/core/useWaveformLayout.ts +30 -58
  33. package/src/components/core/waveformChartTypes.ts +2 -0
  34. package/src/components/core/yAxisLayoutMetrics.ts +75 -0
  35. package/src/components/core/yAxisSlotLayout.test.ts +37 -1
  36. package/src/components/data/types.ts +1 -0
  37. package/src/components/index.ts +1 -0
  38. package/src/components/interaction/useWaveformViewport.ts +2 -0
  39. package/src/components/interaction/useWaveformZoom.ts +58 -61
  40. package/src/components/interaction/zoomEventPayload.ts +88 -0
  41. package/src/components/rendering/WaveformTrackAxes.vue +8 -4
  42. package/src/components/waveformChartCases/layoutAndDomains.test.ts +23 -24
  43. package/src/components/waveformChartCases/yAxisLayout.test.ts +103 -0
  44. package/src/components/waveformChartCases/zoomIntentAndDataRebind.test.ts +213 -0
  45. package/src/core/rendering.ts +1 -1
  46. package/src/core/renderingOptions.ts +1 -1
  47. package/src/core/workerSampling/multiresolution.ts +1 -1
  48. package/src/core/workerSampling/protocol.ts +1 -1
  49. package/src/core/workerSampling/repository.ts +1 -1
  50. package/src/core/workerSampling/repositoryDataset.ts +1 -1
  51. package/src/core/workerSampling/repositorySupport.ts +1 -1
  52. package/src/index.ts +1 -0
  53. package/src/types/chart.ts +9 -0
  54. package/src/types/index.ts +1 -0
  55. package/src/utils/geometry.ts +1 -1
@@ -4,6 +4,7 @@ declare const __VLS_export: import("vue").DefineComponent<WaveformChartProps, {
4
4
  setViewportDomain: (domain: [number, number], trackIndex?: number) => void;
5
5
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
6
  "point-hover": (point: import("..").WaveformPoint | null) => any;
7
+ "zoom-intent": (payload: import("..").WaveformZoomIntentPayload) => any;
7
8
  "zoom-change": (domain: [number, number]) => any;
8
9
  "zoom-end": (payload: import("..").WaveformZoomEndPayload) => any;
9
10
  "zoom-reset": (payload: import("..").WaveformZoomResetPayload) => any;
@@ -27,6 +28,7 @@ declare const __VLS_export: import("vue").DefineComponent<WaveformChartProps, {
27
28
  "sampling-error": (error: import("..").WaveformSamplingError) => any;
28
29
  }, string, import("vue").PublicProps, Readonly<WaveformChartProps> & Readonly<{
29
30
  "onPoint-hover"?: ((point: import("..").WaveformPoint | null) => any) | undefined;
31
+ "onZoom-intent"?: ((payload: import("..").WaveformZoomIntentPayload) => any) | undefined;
30
32
  "onZoom-change"?: ((domain: [number, number]) => any) | undefined;
31
33
  "onZoom-end"?: ((payload: import("..").WaveformZoomEndPayload) => any) | undefined;
32
34
  "onZoom-reset"?: ((payload: import("..").WaveformZoomResetPayload) => any) | undefined;
@@ -3,6 +3,12 @@ import type { DisplaySeries, DisplayTrack, TrackLayout } from './types';
3
3
  import { type WaveformYDomain } from './yDomain';
4
4
  export { MAX_MULTI_Y_AXIS_COUNT } from './constants';
5
5
  export declare function resolveYAxisTickCount(_plotHeight: number, splitNumber?: number): number;
6
+ export interface ResolvedYAxisTicks {
7
+ domain: [number, number];
8
+ values: number[];
9
+ }
10
+ export declare function resolveYAxisTicks(domain: [number, number], tickCount?: number, nice?: boolean): ResolvedYAxisTicks;
11
+ export declare function formatYAxisTickLabel(value: number, domain: [number, number], tickValues: readonly number[], unit?: string): string;
6
12
  export interface YAxisSeriesGroup {
7
13
  index: number;
8
14
  side: 'left' | 'right';
@@ -19,7 +25,8 @@ export interface YAxisSlot {
19
25
  }
20
26
  export declare function buildYAxisSeriesGroups(track: DisplayTrack, overlayMode: WaveformOverlayMode): YAxisSeriesGroup[];
21
27
  export declare function resolveYAxisSeriesGroups(track: DisplayTrack, overlayMode: WaveformOverlayMode, yDomain?: WaveformYDomain, yDomains?: Record<string, WaveformYDomain>): YAxisSeriesGroup[];
22
- export declare function axisTextMetrics(domain: [number, number], nice?: boolean, tickValues?: number[], unit?: string, tickCount?: number): {
28
+ export declare function resolveRenderedYAxisSeriesGroups(track: DisplayTrack, overlayMode: WaveformOverlayMode, yDomain?: WaveformYDomain, yDomains?: Record<string, WaveformYDomain>, viewportYDomains?: Record<string, [number, number]>): YAxisSeriesGroup[];
29
+ export declare function axisTextMetrics(domain: [number, number], nice?: boolean, tickValues?: number[], unit?: string, tickCount?: number, includeWithoutLastTick?: boolean): {
23
30
  tickTextWidth: number;
24
31
  };
25
32
  export declare function measureYAxisGroupClearance(group: YAxisSeriesGroup, tickCount?: number, nice?: boolean): number;
@@ -31,7 +38,7 @@ export declare function measureTrackYAxisClearance(track: DisplayTrack, overlayM
31
38
  * Reserves stable Y-axis positions for every visible track on the current page.
32
39
  * Slots are ordered from the plot edge outward for each side.
33
40
  */
34
- export declare function buildYAxisSlots(tracks: readonly DisplayTrack[], overlayMode: WaveformOverlayMode, yDomain?: WaveformYDomain, yDomains?: Record<string, WaveformYDomain>, tickCount?: number, nice?: boolean): {
41
+ export declare function buildYAxisSlots(tracks: readonly DisplayTrack[], overlayMode: WaveformOverlayMode, yDomain?: WaveformYDomain, yDomains?: Record<string, WaveformYDomain>, tickCount?: number, nice?: boolean, includeWithoutLastTick?: boolean, viewportYDomains?: Record<string, [number, number]>): {
35
42
  slots: YAxisSlot[];
36
43
  clearance: {
37
44
  left: number;
@@ -1,7 +1,6 @@
1
1
  import { type ZoomTransform } from 'd3';
2
2
  import { type ComputedRef, type Ref, type ShallowRef } from 'vue';
3
- import type { AnnotationSeriesCandidate } from '../annotation';
4
- import type { useWaveformAnnotationInteraction } from '../annotation';
3
+ import type { AnnotationSeriesCandidate, useWaveformAnnotationInteraction } from '../annotation';
5
4
  import type { NormalizedWaveformGridOptions } from './grid';
6
5
  import type { DisplaySeries, DisplayTrack, TrackLayout } from './types';
7
6
  import type { ResolvedWaveformChartProps, WaveformChartEmit } from './waveformChartTypes';
@@ -1,4 +1,4 @@
1
- import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformInteractionMode, WaveformLegendOptions, WaveformOverlayMode, WaveformPlotMargin, WaveformPoint, WaveformRenderingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformTitleOptions, WaveformXDomainStrategy, WaveformZeroLineOptions, WaveformZoomEndPayload, WaveformZoomResetPayload } from '../data/types';
1
+ import type { WaveformAnnotation, WaveformAxesOptions, WaveformData, WaveformDisplayMode, WaveformFrameStyle, WaveformInteractionMode, WaveformLegendOptions, WaveformOverlayMode, WaveformPlotMargin, WaveformPoint, WaveformRenderingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformTitleOptions, WaveformXDomainStrategy, WaveformZeroLineOptions, WaveformZoomIntentPayload, WaveformZoomEndPayload, WaveformZoomResetPayload } from '../data/types';
2
2
  import type { WaveformGridOptions } from './grid';
3
3
  export interface WaveformChartProps {
4
4
  data: WaveformData;
@@ -44,6 +44,7 @@ type DefaultedProp = 'displayMode' | 'overlayMode' | 'yLabel' | 'lineColor' | 's
44
44
  export type ResolvedWaveformChartProps = Readonly<WaveformChartProps & Required<Pick<WaveformChartProps, DefaultedProp>>>;
45
45
  export interface WaveformChartEmit {
46
46
  (event: 'point-hover', point: WaveformPoint | null): void;
47
+ (event: 'zoom-intent', payload: WaveformZoomIntentPayload): void;
47
48
  (event: 'zoom-change', domain: [number, number]): void;
48
49
  (event: 'zoom-end', payload: WaveformZoomEndPayload): void;
49
50
  (event: 'zoom-reset', payload: WaveformZoomResetPayload): void;
@@ -0,0 +1,10 @@
1
+ import type { WaveformOverlayMode } from '../data/types';
2
+ import type { DisplayTrack } from './types';
3
+ import type { WaveformYDomain } from './yDomain';
4
+ export interface YAxisLayoutMetrics {
5
+ tickClearance: number;
6
+ fullClearance: number;
7
+ labelCenterX: number;
8
+ }
9
+ export declare function resolveViewportYDomains(displayMode: 'independent' | 'separated' | 'compact', chartTracks: DisplayTrack[], sharedYDomains: Record<string, [number, number]>, independentYDomains: Record<number, [number, number]>): Record<string, [number, number]>;
10
+ export declare function resolveYAxisLayoutMetrics(chartTracks: DisplayTrack[], overlayMode: WaveformOverlayMode, yDomain: WaveformYDomain | undefined, yDomains: Record<string, WaveformYDomain> | undefined, viewportYDomains: Record<string, [number, number]>, nice: boolean, tickCount: number, compact: boolean): YAxisLayoutMetrics;
@@ -2,6 +2,6 @@
2
2
  * 数据系统类型定义
3
3
  * 重新导出 src/types 中的类型,方便组件内部使用
4
4
  */
5
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformTypedValues, TypedSampleData, TypedPointData, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from '../../types';
5
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomIntentPayload, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformTypedValues, TypedSampleData, TypedPointData, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from '../../types';
6
6
  export type { WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from '../core/grid';
7
7
  export { normalizeWaveformData, normalizeWaveformSeries } from '../../core';
@@ -1,5 +1,5 @@
1
1
  export { default as WaveformChart } from './WaveformChart.vue';
2
- export type { SingleWaveformData, WaveformData, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, WaveformPoint, WaveformSeries, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './data/types';
2
+ export type { SingleWaveformData, WaveformData, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformZoomIntentPayload, WaveformZoomEndPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformAxesOptions, WaveformZeroLineOptions, WaveformPoint, WaveformSeries, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './data/types';
3
3
  export type { WaveformGridOptions as WaveformGridConfig } from './core/grid';
4
4
  export { WaveformTooltip } from './interaction';
5
5
  export { WaveformTrack } from './rendering';
@@ -22,9 +22,10 @@ interface ZoomContext {
22
22
  }
23
23
  export declare function useWaveformZoom(context: ZoomContext): {
24
24
  cancelPendingZoom: () => void;
25
- clearZoomBindings: () => void;
25
+ clearZoomBindings: (preservePendingWheel?: boolean) => void;
26
26
  canZoomTrack: (track: TrackLayout) => boolean;
27
27
  canZoomSharedTracks: () => boolean;
28
28
  configureZoom: () => void;
29
+ prepareForDataChange: () => void;
29
30
  };
30
31
  export {};
@@ -0,0 +1,7 @@
1
+ import type { TrackLayout } from '../core/types';
2
+ import type { WaveformChartEmit } from '../core/waveformChartTypes';
3
+ export declare function seriesIdentity(seriesIds: readonly string[]): string;
4
+ export declare function findTrackBySeriesIds(trackLayouts: readonly TrackLayout[], seriesIds: readonly string[]): TrackLayout | undefined;
5
+ export declare function emitBoxZoomIntent(emit: WaveformChartEmit, domain: [number, number], track: TrackLayout, independent: boolean): void;
6
+ export declare function emitIndependentWheelZoomEnds(emit: WaveformChartEmit, trackLayouts: readonly TrackLayout[], pendingSeriesIds: Iterable<readonly string[]>): void;
7
+ export declare function emitSharedWheelZoomEnd(emit: WaveformChartEmit, domain: [number, number], trackLayouts: readonly TrackLayout[]): void;
@@ -1,4 +1,4 @@
1
- import type { WaveformPoint } from '@/types';
1
+ import type { WaveformPoint } from '../types';
2
2
  import type { ResolvedWaveformRenderingOptions } from './renderingOptions';
3
3
  import { type VisiblePointRange } from './renderingStrategies';
4
4
  export { DEFAULT_WAVEFORM_RENDERING_OPTIONS, resolveWaveformRenderingOptions, type ResolvedWaveformRenderingOptions, type ResolvedWaveformSamplingOptions, } from './renderingOptions';
@@ -1,4 +1,4 @@
1
- import type { WaveformRenderingOptions, WaveformSamplingMode, WaveformSamplingStrategy } from '@/types';
1
+ import type { WaveformRenderingOptions, WaveformSamplingMode, WaveformSamplingStrategy } from '../types';
2
2
  export interface ResolvedWaveformSamplingOptions {
3
3
  mode: WaveformSamplingMode;
4
4
  autoThreshold: number;
@@ -1,4 +1,4 @@
1
- import type { InitInput } from '../../wasm/pkg/waveform_sampling_wasm';
1
+ import type { InitInput } from '../wasm/waveform_sampling_wasm';
2
2
  import type { ReferenceSamplingStrategy } from './samplingReference';
3
3
  import type { WorkerSamplingBackend, WorkerSamplingOutput } from './workerSampling/protocol';
4
4
  export type WasmSamplingStrategy = ReferenceSamplingStrategy;
@@ -1,4 +1,4 @@
1
- import type { WaveformPoint } from '@/types';
1
+ import type { WaveformPoint } from '../../types';
2
2
  import type { WorkerSamplingOutput } from './protocol';
3
3
  export type IndexedSamplingStrategy = 'none' | 'peak' | 'lttb' | 'average' | 'min' | 'max' | 'minmax' | 'sum';
4
4
  export interface MultiResolutionSamplingIndexOptions {
@@ -1,4 +1,4 @@
1
- import type { WaveformPoint, WaveformLineType, WaveformPointType, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingMode, WaveformSamplingStrategy } from '@/types';
1
+ import type { WaveformPoint, WaveformLineType, WaveformPointType, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingMode, WaveformSamplingStrategy } from '../../types';
2
2
  import type { WorkerSamplingDataset } from '../waveformPointSource';
3
3
  export declare const DEFAULT_WORKER_SAMPLING_AUTO_THRESHOLD = 1000;
4
4
  export declare const DEFAULT_WORKER_SAMPLING_MAX_POINTS_PER_PIXEL = 4;
@@ -1,4 +1,4 @@
1
- import type { WaveformPoint } from '@/types';
1
+ import type { WaveformPoint } from '../../types';
2
2
  import type { WorkerSamplingDataset } from '../waveformPointSource';
3
3
  import type { WasmDatasetSamplingBackend } from '../wasmSampling';
4
4
  import { MultiResolutionSamplingIndex } from './multiresolution';
@@ -1,4 +1,4 @@
1
- import type { WaveformPoint } from '@/types';
1
+ import type { WaveformPoint } from '../../types';
2
2
  import { type WorkerSamplingBackendKind, type WorkerSamplingDatasetMetrics, type WorkerSamplingDiagnostics, type WorkerSamplingOutput, type WorkerSamplingSeriesRequest, type WorkerSamplingStatus } from './protocol';
3
3
  export declare function copyFinitePoints(points: readonly WaveformPoint[]): WaveformPoint[];
4
4
  export declare function metricsFor(points: readonly WaveformPoint[], inputPointCount: number): WorkerSamplingDatasetMetrics;
@@ -3,7 +3,7 @@
3
3
  * @packageDocumentation
4
4
  */
5
5
  export { default as WaveformChart } from './components/WaveformChart.vue';
6
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformTypedValues, TypedSampleData, TypedPointData, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
6
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomIntentPayload, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformTypedValues, TypedSampleData, TypedPointData, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './types';
7
7
  export type { WaveformGridOptions, WaveformGridLineOptions, WaveformGridTrackLines, } from './components/core/grid';
8
8
  export { normalizeWaveformData, normalizeWaveformSeries } from './core';
9
9
  export { paddedDomain, buildMinorTicks, displayTime, formatEndpointTime, formatAxisTime, formatTooltipTime, resolveTrackGeometry, clamp, type TimeUnit, type TrackGeometry, } from './utils';
@@ -43,6 +43,14 @@ export interface WaveformZoomEndPayload {
43
43
  seriesIds?: string[];
44
44
  gesture?: 'wheel' | 'box';
45
45
  }
46
+ /** Describes the X-axis viewport targeted by a user zoom gesture before rendering settles. */
47
+ export interface WaveformZoomIntentPayload {
48
+ start: number;
49
+ end: number;
50
+ gesture: 'wheel' | 'box';
51
+ trackIndex?: number;
52
+ seriesIds?: string[];
53
+ }
46
54
  /** Identifies the viewport reset by a double-click gesture. */
47
55
  export interface WaveformZoomResetPayload {
48
56
  trackIndex?: number;
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * 类型定义统一导出
3
3
  */
4
- export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
4
+ export type { WaveformPoint, WaveformDisplayMode, WaveformOverlayMode, WaveformInteractionMode, WaveformXDomainStrategy, WaveformZoomIntentPayload, WaveformZoomEndPayload, WaveformZoomResetPayload, WaveformAnnotationStyle, WaveformAnnotation, WaveformSamplingMode, WaveformSamplingStrategy, WaveformSamplingOptions, WaveformSamplingBackend, WaveformSamplingDiagnostics, WaveformSamplingError, WaveformRenderingOptions, WaveformPlotMargin, WaveformTitleTextStyle, WaveformTitleOptions, WaveformLegendPosition, WaveformLegendOrientation, WaveformLegendOptions, WaveformFrameStyle, WaveformXAxisLabelKind, WaveformXAxisLabelFormatterContext, WaveformXAxisLabelFormatter, WaveformAxesOptions, WaveformZeroLineOptions, } from './chart';
5
5
  export type { SingleWaveformData, WaveformLineType, WaveformLineStyle, WaveformPointType, WaveformErrorBarOptions, ResolvedWaveformErrorBarOptions, WaveformTypedValues, TypedSampleData, TypedPointData, WaveformSeries, WaveformData, NormalizedWaveformSeries, } from './data';
@@ -1,4 +1,4 @@
1
- import type { WaveformDisplayMode } from '@/types';
1
+ import type { WaveformDisplayMode } from '../types';
2
2
  /**
3
3
  * 轨道几何信息
4
4
  */
@@ -0,0 +1,74 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * Returns [minimum X, maximum X, minimum Y, maximum Y], or an empty array for no valid points.
6
+ */
7
+ export function calculate_range(x: Float64Array, y: Float64Array): Float64Array;
8
+
9
+ export function dataset_index_bytes(handle: number): number;
10
+
11
+ export function dispose_all_datasets(): void;
12
+
13
+ export function dispose_dataset(handle: number): boolean;
14
+
15
+ /**
16
+ * Returns the half-open [start, end) range in the normalized ascending-X point sequence.
17
+ */
18
+ export function find_visible_range(x: Float64Array, y: Float64Array, domain_start: number, domain_end: number): Uint32Array;
19
+
20
+ export function register_dataset(x: Float64Array, y: Float64Array, index_max_bytes: number): number;
21
+
22
+ export function register_sample_dataset(y: Float64Array, source_indexes: Uint32Array, start_time: number, sample_rate: number, index_max_bytes: number): number;
23
+
24
+ export function sample_aggregates(x: Float64Array, y: Float64Array, strategy: number, target: number): Float64Array;
25
+
26
+ export function sample_dataset_aggregates(handle: number, start: number, end: number, strategy: number, target: number): Float64Array;
27
+
28
+ export function sample_dataset_indexes(handle: number, start: number, end: number, strategy: number, target: number): Uint32Array;
29
+
30
+ export function sample_indexes(x: Float64Array, y: Float64Array, strategy: number, target: number): Uint32Array;
31
+
32
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
33
+
34
+ export interface InitOutput {
35
+ readonly memory: WebAssembly.Memory;
36
+ readonly calculate_range: (a: number, b: number, c: number, d: number) => [number, number, number, number];
37
+ readonly dispose_dataset: (a: number) => number;
38
+ readonly find_visible_range: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
39
+ readonly register_dataset: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
40
+ readonly register_sample_dataset: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
41
+ readonly sample_aggregates: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
42
+ readonly sample_dataset_aggregates: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
43
+ readonly sample_dataset_indexes: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
44
+ readonly sample_indexes: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
45
+ readonly dispose_all_datasets: () => void;
46
+ readonly dataset_index_bytes: (a: number) => number;
47
+ readonly __wbindgen_externrefs: WebAssembly.Table;
48
+ readonly __wbindgen_malloc: (a: number, b: number) => number;
49
+ readonly __externref_table_dealloc: (a: number) => void;
50
+ readonly __wbindgen_free: (a: number, b: number, c: number) => void;
51
+ readonly __wbindgen_start: () => void;
52
+ }
53
+
54
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
55
+
56
+ /**
57
+ * Instantiates the given `module`, which can either be bytes or
58
+ * a precompiled `WebAssembly.Module`.
59
+ *
60
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
61
+ *
62
+ * @returns {InitOutput}
63
+ */
64
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
65
+
66
+ /**
67
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
68
+ * for everything else, calls `WebAssembly.instantiate` directly.
69
+ *
70
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
71
+ *
72
+ * @returns {Promise<InitOutput>}
73
+ */
74
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
@@ -0,0 +1,19 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ export const memory: WebAssembly.Memory;
4
+ export const calculate_range: (a: number, b: number, c: number, d: number) => [number, number, number, number];
5
+ export const dispose_dataset: (a: number) => number;
6
+ export const find_visible_range: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
7
+ export const register_dataset: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
8
+ export const register_sample_dataset: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
9
+ export const sample_aggregates: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
10
+ export const sample_dataset_aggregates: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
11
+ export const sample_dataset_indexes: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
12
+ export const sample_indexes: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
13
+ export const dispose_all_datasets: () => void;
14
+ export const dataset_index_bytes: (a: number) => number;
15
+ export const __wbindgen_externrefs: WebAssembly.Table;
16
+ export const __wbindgen_malloc: (a: number, b: number) => number;
17
+ export const __externref_table_dealloc: (a: number) => void;
18
+ export const __wbindgen_free: (a: number, b: number, c: number) => void;
19
+ export const __wbindgen_start: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waveform-analysis",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -22,6 +22,7 @@
22
22
  ],
23
23
  "type": "module",
24
24
  "dependencies": {
25
+ "@types/d3": "7.4.3",
25
26
  "ant-design-vue": "3.2.20",
26
27
  "d3": "^7.9.0",
27
28
  "vue-router": "^4.6.4",
@@ -33,7 +34,6 @@
33
34
  },
34
35
  "devDependencies": {
35
36
  "@eslint/js": "10.0.1",
36
- "@types/d3": "7.4.3",
37
37
  "@types/node": "26.1.1",
38
38
  "@vitejs/plugin-vue": "6.0.8",
39
39
  "@vitest/coverage-v8": "4.1.10",
@@ -57,7 +57,7 @@
57
57
  "build": "pnpm typecheck && pnpm build:lib && pnpm build:types && pnpm build:demo",
58
58
  "build:wasm": "wasm-pack build wasm --target web --release --out-dir pkg --locked",
59
59
  "build:lib": "vite build --config vite.lib.config.ts",
60
- "build:types": "node scripts/clean-types.mjs && vue-tsc -p tsconfig.lib.json",
60
+ "build:types": "node scripts/clean-types.mjs && vue-tsc -p tsconfig.lib.json && node scripts/prepare-package-types.mjs",
61
61
  "build:demo": "vite build",
62
62
  "preview": "vite preview --outDir dist-demo",
63
63
  "typecheck": "pnpm build:wasm && vue-tsc -b",
@@ -68,6 +68,7 @@
68
68
  "format": "prettier --write .",
69
69
  "format:check": "prettier --check .",
70
70
  "test": "pnpm build:wasm && vitest run",
71
- "test:coverage": "pnpm build:wasm && vitest run --coverage"
71
+ "test:coverage": "pnpm build:wasm && vitest run --coverage",
72
+ "test:package": "node scripts/test-package.mjs"
72
73
  }
73
74
  }
@@ -4,11 +4,14 @@ import { describe, expect, it } from 'vitest'
4
4
  import { DEFAULT_WAVEFORM_RENDERING_OPTIONS } from '../../core'
5
5
  import type { DisplaySeries, DisplayTrack } from './types'
6
6
  import {
7
+ axisTextMetrics,
7
8
  buildTrackLayouts,
8
9
  buildYAxisSeriesGroups,
10
+ formatYAxisTickLabel,
9
11
  findClosestTrackAtPointer,
10
12
  MAX_MULTI_Y_AXIS_COUNT,
11
13
  measureYAxisGroupClearance,
14
+ resolveYAxisTicks,
12
15
  resolveYAxisTickCount,
13
16
  } from './layout'
14
17
 
@@ -143,6 +146,27 @@ describe('multi-value Y-axis grouping', () => {
143
146
  expect(resolveYAxisTickCount(220)).toBe(5)
144
147
  })
145
148
 
149
+ it('measures the same final endpoint label that Y-axis rendering uses', () => {
150
+ const ticks = resolveYAxisTicks([-0.7434, -0.3434], 5, false)
151
+ const labels = ticks.values.map((value) =>
152
+ formatYAxisTickLabel(value, ticks.domain, ticks.values, 'V'),
153
+ )
154
+
155
+ expect(labels).toEqual(['-0.7434', '-0.6434', '-0.5434', '-0.4434', '(V) -0.3434'])
156
+ expect(axisTextMetrics(ticks.domain, false, undefined, 'V', 5).tickTextWidth).toBe(77)
157
+ })
158
+
159
+ it('includes the compact-axis unit label after its top tick is hidden', () => {
160
+ const ticks = resolveYAxisTicks([0.12345, 100], 5, false)
161
+ const compactTicks = ticks.values.slice(0, -1)
162
+
163
+ expect(formatYAxisTickLabel(compactTicks.at(-1)!, ticks.domain, compactTicks, 'V')).toBe(
164
+ '(V) 75.031',
165
+ )
166
+ expect(axisTextMetrics(ticks.domain, false, undefined, 'V', 5).tickTextWidth).toBe(49)
167
+ expect(axisTextMetrics(ticks.domain, false, undefined, 'V', 5, true).tickTextWidth).toBe(70)
168
+ })
169
+
146
170
  it('keeps every overlaid series on one axis in single-axis mode', () => {
147
171
  const groups = buildYAxisSeriesGroups(
148
172
  track([series('a', 0, 1), series('b', 10, 20)]),
@@ -27,6 +27,47 @@ export function resolveYAxisTickCount(_plotHeight: number, splitNumber?: number)
27
27
  return 5
28
28
  }
29
29
 
30
+ export interface ResolvedYAxisTicks {
31
+ domain: [number, number]
32
+ values: number[]
33
+ }
34
+
35
+ export function resolveYAxisTicks(
36
+ domain: [number, number],
37
+ tickCount = 5,
38
+ nice = true,
39
+ ): ResolvedYAxisTicks {
40
+ const effectiveTickCount =
41
+ typeof tickCount === 'number' && Number.isFinite(tickCount)
42
+ ? Math.max(2, Math.floor(tickCount))
43
+ : 5
44
+ const scale = scaleLinear(domain, [1, 0])
45
+ if (nice) scale.nice(Math.max(1, effectiveTickCount - 1))
46
+ const resolvedDomain = scale.domain() as [number, number]
47
+ const [axisStart, axisEnd] = resolvedDomain
48
+ return {
49
+ domain: resolvedDomain,
50
+ values: Array.from(
51
+ { length: effectiveTickCount },
52
+ (_, index) => axisStart + ((axisEnd - axisStart) * index) / (effectiveTickCount - 1),
53
+ ),
54
+ }
55
+ }
56
+
57
+ export function formatYAxisTickLabel(
58
+ value: number,
59
+ domain: [number, number],
60
+ tickValues: readonly number[],
61
+ unit?: string,
62
+ ): string {
63
+ return formatScientificAxisLabel(value, {
64
+ axisMin: domain[0],
65
+ axisMax: domain[1],
66
+ topTickValue: Math.max(...tickValues),
67
+ unit,
68
+ })
69
+ }
70
+
30
71
  export interface YAxisSeriesGroup {
31
72
  index: number
32
73
  side: 'left' | 'right'
@@ -114,26 +155,38 @@ export function resolveYAxisSeriesGroups(
114
155
  })
115
156
  }
116
157
 
158
+ export function resolveRenderedYAxisSeriesGroups(
159
+ track: DisplayTrack,
160
+ overlayMode: WaveformOverlayMode,
161
+ yDomain?: WaveformYDomain,
162
+ yDomains?: Record<string, WaveformYDomain>,
163
+ viewportYDomains?: Record<string, [number, number]>,
164
+ ): YAxisSeriesGroup[] {
165
+ const viewportYDomain = viewportYDomains?.[track.id]
166
+ return resolveYAxisSeriesGroups(track, overlayMode, yDomain, yDomains).map((group) =>
167
+ !group.fixed && viewportYDomain ? { ...group, domain: viewportYDomain } : group,
168
+ )
169
+ }
170
+
117
171
  export function axisTextMetrics(
118
172
  domain: [number, number],
119
173
  nice = true,
120
174
  tickValues?: number[],
121
175
  unit?: string,
122
- tickCount = 10,
176
+ tickCount = 5,
177
+ includeWithoutLastTick = false,
123
178
  ): { tickTextWidth: number } {
124
- const effectiveTickCount =
125
- typeof tickCount === 'number' && Number.isFinite(tickCount)
126
- ? Math.max(2, Math.floor(tickCount))
127
- : 10
128
- const scale = scaleLinear(domain, [1, 0])
129
- if (nice) scale.nice(effectiveTickCount)
130
- const [axisMin, axisMax] = scale.domain()
131
- const values = tickValues ?? scale.ticks(effectiveTickCount)
132
- const topTickValue = Math.max(...values)
179
+ const resolvedTicks = tickValues
180
+ ? { domain, values: tickValues }
181
+ : resolveYAxisTicks(domain, tickCount, nice)
182
+ const tickValueSets = [resolvedTicks.values]
183
+ if (includeWithoutLastTick && resolvedTicks.values.length > 1) {
184
+ tickValueSets.push(resolvedTicks.values.slice(0, -1))
185
+ }
133
186
  const maximumTickCharacters = Math.max(
134
187
  1,
135
- ...values.map(
136
- (value) => formatScientificAxisLabel(value, { axisMin, axisMax, topTickValue, unit }).length,
188
+ ...tickValueSets.flatMap((values) =>
189
+ values.map((value) => formatYAxisTickLabel(value, resolvedTicks.domain, values, unit).length),
137
190
  ),
138
191
  )
139
192
  return {
@@ -200,11 +253,19 @@ export function buildYAxisSlots(
200
253
  yDomains?: Record<string, WaveformYDomain>,
201
254
  tickCount?: number,
202
255
  nice = true,
256
+ includeWithoutLastTick = false,
257
+ viewportYDomains?: Record<string, [number, number]>,
203
258
  ): { slots: YAxisSlot[]; clearance: { left: number; right: number } } {
204
259
  const widths = new Map<string, number>()
205
260
  tracks.forEach((track) => {
206
261
  const sideIndexes = { left: 0, right: 0 }
207
- resolveYAxisSeriesGroups(track, overlayMode, yDomain, yDomains).forEach((group) => {
262
+ resolveRenderedYAxisSeriesGroups(
263
+ track,
264
+ overlayMode,
265
+ yDomain,
266
+ yDomains,
267
+ viewportYDomains,
268
+ ).forEach((group) => {
208
269
  const sideIndex = sideIndexes[group.side]++
209
270
  const key = `${group.side}:${sideIndex}`
210
271
  const width = axisTextMetrics(
@@ -213,6 +274,7 @@ export function buildYAxisSlots(
213
274
  undefined,
214
275
  group.seriesList[0]?.unit,
215
276
  tickCount,
277
+ includeWithoutLastTick,
216
278
  ).tickTextWidth
217
279
  widths.set(key, Math.max(widths.get(key) ?? Y_AXIS_CHARACTER_WIDTH, width))
218
280
  })
@@ -27,7 +27,8 @@ import {
27
27
  } from './grid'
28
28
  import {
29
29
  axisTextMetrics,
30
- resolveYAxisSeriesGroups,
30
+ resolveRenderedYAxisSeriesGroups,
31
+ resolveYAxisTicks,
31
32
  resolveYAxisTickCount,
32
33
  type YAxisSlot,
33
34
  } from './layout'
@@ -115,28 +116,21 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
115
116
  ? (options.independentTransforms[index] ?? zoomIdentity)
116
117
  : zoomIdentity
117
118
  const xScale = transform.rescaleX(baseXScale)
118
- const configuredYDomain = options.yDomains?.[displayTrack.id]
119
- const yAxisGroups = resolveYAxisSeriesGroups(
119
+ const yAxisGroups = resolveRenderedYAxisSeriesGroups(
120
120
  displayTrack,
121
121
  options.overlayMode,
122
122
  options.fixedYDomain,
123
123
  options.fixedYDomains,
124
- ).map((group) =>
125
- !group.fixed && configuredYDomain ? { ...group, domain: configuredYDomain } : group,
124
+ options.yDomains,
126
125
  )
127
126
  const sideIndexes = { left: 0, right: 0 }
128
127
  const sideOffsets = { left: 0, right: 0 }
129
128
  const yAxes: WaveformYAxisLayout[] = yAxisGroups.map((group) => {
130
129
  const sideIndex = sideIndexes[group.side]++
131
130
  const tickCount = resolveYAxisTickCount(cell.plotHeight, options.yAxisSplitNumber)
132
- const niceCount = Math.max(1, tickCount - 1)
133
- const scale = scaleLinear(group.domain, [cell.plotHeight, 0])
134
- if (options.yAxisNice !== false) scale.nice(niceCount)
135
- const [axisStart, axisEnd] = scale.domain()
136
- const majorTicks = Array.from(
137
- { length: tickCount },
138
- (_, index) => axisStart + ((axisEnd - axisStart) * index) / (tickCount - 1),
139
- )
131
+ const resolvedTicks = resolveYAxisTicks(group.domain, tickCount, options.yAxisNice !== false)
132
+ const scale = scaleLinear(resolvedTicks.domain, [cell.plotHeight, 0])
133
+ const majorTicks = resolvedTicks.values
140
134
  const showAxisEnd = options.displayMode !== 'compact' || cell.row === 0
141
135
  const visibleMajorTicks = showAxisEnd ? majorTicks : majorTicks.slice(0, -1)
142
136
  const tickValues = visibleMajorTicks
@@ -87,9 +87,13 @@ export function useWaveformChartController(
87
87
  const pointerInsideChart = ref(false)
88
88
  let handleBeforeDataReferenceChange: () => void = () => undefined
89
89
  let handleDataReferenceChange: () => void = () => undefined
90
+ let prepareForDataChange: () => void = () => undefined
90
91
  const preparedSeries = usePreparedWaveformSeries(
91
92
  () => props.data,
92
- () => handleBeforeDataReferenceChange(),
93
+ () => {
94
+ prepareForDataChange()
95
+ handleBeforeDataReferenceChange()
96
+ },
93
97
  () => handleDataReferenceChange(),
94
98
  )
95
99
 
@@ -184,6 +188,7 @@ export function useWaveformChartController(
184
188
  resolveInitialTrackDomain,
185
189
  cancelPendingHover: () => hover.cancelPendingHover(),
186
190
  })
191
+ prepareForDataChange = zoom.prepareForDataChange
187
192
 
188
193
  const annotations = useWaveformChartAnnotations({
189
194
  props,