fix(chart): refine axis number formatting
All checks were successful
Package component / package (push) Successful in 4m2s
All checks were successful
Package component / package (push) Successful in 4m2s
This commit is contained in:
@@ -192,7 +192,7 @@ describe('multi-value Y-axis grouping', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('places left and right scientific exponents eight pixels outside their tick labels', () => {
|
||||
it('reserves label clearance for exponent-prefixed ticks on both axis sides', () => {
|
||||
const layout = buildTrackLayouts({
|
||||
cells: [
|
||||
{
|
||||
@@ -206,7 +206,7 @@ describe('multi-value Y-axis grouping', () => {
|
||||
plotHeight: 300,
|
||||
cellHeight: 330,
|
||||
xAxisBand: 30,
|
||||
series: track([series('left', 0, 254), series('right', 0, 254)]),
|
||||
series: track([series('left', 1000, 3000), series('right', 1000, 3000)]),
|
||||
},
|
||||
],
|
||||
grid: { rowCount: 1, columnCount: 1, showPagination: false, trackLines: {} },
|
||||
@@ -222,14 +222,13 @@ describe('multi-value Y-axis grouping', () => {
|
||||
})[0]
|
||||
|
||||
expect(
|
||||
layout?.yAxes.map(({ side, x, exponentX, exponentLabel }) => ({
|
||||
layout?.yAxes.map(({ side, x, labelX }) => ({
|
||||
side,
|
||||
offset: Math.abs(exponentX - x),
|
||||
exponentLabel,
|
||||
labelOffset: Math.abs(labelX - x),
|
||||
})),
|
||||
).toEqual([
|
||||
{ side: 'left', offset: 43, exponentLabel: 'E+02' },
|
||||
{ side: 'right', offset: 43, exponentLabel: 'E+02' },
|
||||
{ side: 'left', labelOffset: 67 },
|
||||
{ side: 'right', labelOffset: 67 },
|
||||
])
|
||||
})
|
||||
|
||||
@@ -237,7 +236,7 @@ describe('multi-value Y-axis grouping', () => {
|
||||
const [group] = buildYAxisSeriesGroups(track([series('long', -1e120, 1e120)]), 'multi-axis')
|
||||
|
||||
expect(group).toBeDefined()
|
||||
expect(measureYAxisGroupClearance(group!)).toBe(119)
|
||||
expect(measureYAxisGroupClearance(group!)).toBe(90)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { scaleLinear } from 'd3'
|
||||
|
||||
import type { WaveformOverlayMode } from '../../types'
|
||||
import { formatScientificAxisExponent, formatScientificAxisLabel, paddedDomain } from '../../utils'
|
||||
import { formatScientificAxisLabel, paddedDomain } from '../../utils'
|
||||
import type { DisplaySeries, DisplayTrack, TrackLayout } from './types'
|
||||
import { MAX_MULTI_Y_AXIS_COUNT, Y_AXIS_EXPONENT_GAP } from './constants'
|
||||
import { MAX_MULTI_Y_AXIS_COUNT } from './constants'
|
||||
import {
|
||||
mergeYDomains,
|
||||
resolveSeriesFixedYDomain,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from './yDomain'
|
||||
|
||||
// 导出常量供外部使用
|
||||
export { MAX_MULTI_Y_AXIS_COUNT, Y_AXIS_EXPONENT_GAP } from './constants'
|
||||
export { MAX_MULTI_Y_AXIS_COUNT } from './constants'
|
||||
|
||||
const Y_AXIS_CHARACTER_WIDTH = 7
|
||||
const Y_AXIS_TICK_PADDING = 7
|
||||
@@ -102,36 +102,27 @@ export function resolveYAxisSeriesGroups(
|
||||
export function axisTextMetrics(
|
||||
domain: [number, number],
|
||||
nice = true,
|
||||
): {
|
||||
exponentLabel: string | null
|
||||
exponentWidth: number
|
||||
tickTextWidth: number
|
||||
} {
|
||||
tickValues?: number[],
|
||||
): { tickTextWidth: number } {
|
||||
const scale = scaleLinear(domain, [1, 0])
|
||||
if (nice) scale.nice()
|
||||
const [axisMin, axisMax] = scale.domain()
|
||||
const values = scale.ticks(10)
|
||||
const values = tickValues ?? Array.from(new Set([axisMin, ...scale.ticks(10), axisMax]))
|
||||
const topTickValue = Math.max(...values)
|
||||
const maximumTickCharacters = Math.max(
|
||||
1,
|
||||
...values.map((value) => formatScientificAxisLabel(value, { axisMin, axisMax }).length),
|
||||
...values.map(
|
||||
(value) => formatScientificAxisLabel(value, { axisMin, axisMax, topTickValue }).length,
|
||||
),
|
||||
)
|
||||
const exponentLabel = formatScientificAxisExponent(axisMin, axisMax)
|
||||
return {
|
||||
exponentLabel,
|
||||
exponentWidth: exponentLabel ? exponentLabel.length * Y_AXIS_CHARACTER_WIDTH : 0,
|
||||
tickTextWidth: maximumTickCharacters * Y_AXIS_CHARACTER_WIDTH,
|
||||
}
|
||||
}
|
||||
|
||||
function axisExponentClearance(domain: [number, number], nice: boolean): number {
|
||||
const { exponentLabel, exponentWidth } = axisTextMetrics(domain, nice)
|
||||
return exponentLabel ? exponentWidth + Y_AXIS_EXPONENT_GAP : 0
|
||||
}
|
||||
|
||||
export function measureYAxisGroupClearance(group: YAxisSeriesGroup): number {
|
||||
return (
|
||||
axisTextMetrics(group.domain, !group.fixed).tickTextWidth +
|
||||
axisExponentClearance(group.domain, !group.fixed) +
|
||||
Y_AXIS_TICK_PADDING +
|
||||
Y_AXIS_LABEL_GAP +
|
||||
Y_AXIS_LABEL_BAND_WIDTH +
|
||||
@@ -142,7 +133,6 @@ export function measureYAxisGroupClearance(group: YAxisSeriesGroup): number {
|
||||
function measureYAxisGroupTickClearance(group: YAxisSeriesGroup): number {
|
||||
return (
|
||||
axisTextMetrics(group.domain, !group.fixed).tickTextWidth +
|
||||
axisExponentClearance(group.domain, !group.fixed) +
|
||||
Y_AXIS_TICK_PADDING +
|
||||
Y_AXIS_OUTER_PADDING
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
type ResolvedWaveformRenderingOptions,
|
||||
} from '../../core/rendering'
|
||||
import type { WaveformDisplayMode, WaveformOverlayMode, WaveformPoint } from '../../types'
|
||||
import { buildMinorTicks, formatAxisTimeExponent, formatEndpointTime } from '../../utils'
|
||||
import { buildMinorTicks, formatEndpointTime } from '../../utils'
|
||||
import {
|
||||
getBottomRowCellIndexes,
|
||||
type GridCellGeometry,
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
} from './grid'
|
||||
import { axisTextMetrics, resolveYAxisSeriesGroups } from './layout'
|
||||
import type { DisplaySeries, DisplayTrack, TrackLayout, WaveformYAxisLayout } from './types'
|
||||
import { Y_AXIS_EXPONENT_GAP } from './constants'
|
||||
import {
|
||||
Y_AXIS_LABEL_BAND_WIDTH,
|
||||
Y_AXIS_LABEL_GAP,
|
||||
@@ -118,31 +117,16 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
||||
const tickValues = Array.from(
|
||||
new Set([axisStart, ...visibleMajorTicks, ...(showAxisEnd ? [axisEnd] : [])]),
|
||||
)
|
||||
const { exponentLabel, exponentWidth, tickTextWidth } = axisTextMetrics(
|
||||
group.domain,
|
||||
!group.fixed,
|
||||
)
|
||||
const exponentClearance = exponentLabel ? exponentWidth + Y_AXIS_EXPONENT_GAP : 0
|
||||
const { tickTextWidth } = axisTextMetrics(group.domain, !group.fixed, tickValues)
|
||||
const clearance =
|
||||
tickTextWidth +
|
||||
Y_AXIS_TICK_PADDING +
|
||||
exponentClearance +
|
||||
Y_AXIS_LABEL_GAP +
|
||||
Y_AXIS_LABEL_BAND_WIDTH +
|
||||
Y_AXIS_OUTER_PADDING
|
||||
const x = group.side === 'left' ? -sideOffsets.left : cell.width + sideOffsets.right
|
||||
const exponentX =
|
||||
x +
|
||||
(group.side === 'left'
|
||||
? -(Y_AXIS_TICK_PADDING + tickTextWidth + Y_AXIS_EXPONENT_GAP)
|
||||
: Y_AXIS_TICK_PADDING + tickTextWidth + Y_AXIS_EXPONENT_GAP)
|
||||
const labelDistance =
|
||||
tickTextWidth +
|
||||
Y_AXIS_TICK_PADDING +
|
||||
exponentClearance +
|
||||
exponentWidth +
|
||||
Y_AXIS_LABEL_GAP +
|
||||
Y_AXIS_LABEL_BAND_WIDTH / 2
|
||||
tickTextWidth + Y_AXIS_TICK_PADDING + Y_AXIS_LABEL_GAP + Y_AXIS_LABEL_BAND_WIDTH / 2
|
||||
const labelX = x + (group.side === 'left' ? -labelDistance : labelDistance)
|
||||
sideOffsets[group.side] += clearance
|
||||
return {
|
||||
@@ -150,8 +134,6 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
||||
side: group.side,
|
||||
x,
|
||||
labelX,
|
||||
exponentX,
|
||||
exponentLabel,
|
||||
scale,
|
||||
majorTicks,
|
||||
minorTicks: buildMinorTicks(majorTicks),
|
||||
@@ -168,7 +150,6 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
||||
start: formatEndpointTime(domain[0], domain, options.timeUnit),
|
||||
end: formatEndpointTime(domain[1], domain, options.timeUnit),
|
||||
}
|
||||
const xAxisExponent = formatAxisTimeExponent(domain, options.timeUnit)
|
||||
const leftClearance = endpointLabels.start.length * 7 + 10
|
||||
const rightClearance = endpointLabels.end.length * 7 + 10
|
||||
const xAxisTickValues = xMajorTicks.filter((tick) => {
|
||||
@@ -235,7 +216,6 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
|
||||
yAxisTickValues,
|
||||
xAxisTickValues,
|
||||
endpointLabels,
|
||||
xAxisExponent,
|
||||
path: seriesPaths[0]?.path ?? null,
|
||||
seriesPaths,
|
||||
gridLines: options.grid.trackLines[displayTrack.id] ?? {
|
||||
|
||||
@@ -51,8 +51,6 @@ export interface WaveformYAxisLayout {
|
||||
side: 'left' | 'right'
|
||||
x: number
|
||||
labelX: number
|
||||
exponentX: number
|
||||
exponentLabel: string | null
|
||||
scale: ScaleLinear<number, number>
|
||||
majorTicks: number[]
|
||||
minorTicks: number[]
|
||||
@@ -109,7 +107,6 @@ export interface TrackLayout {
|
||||
yAxisTickValues: number[]
|
||||
xAxisTickValues: number[]
|
||||
endpointLabels: { start: string; end: string }
|
||||
xAxisExponent: string | null
|
||||
path: string | null
|
||||
seriesPaths: TrackSeriesPath[]
|
||||
showXAxis: boolean
|
||||
|
||||
@@ -2,7 +2,7 @@ import { scaleLinear, type ZoomTransform } from 'd3'
|
||||
import { computed, type ComputedRef, type Ref, type ShallowRef } from 'vue'
|
||||
|
||||
import { resolveWaveformRenderingOptions } from '../../core'
|
||||
import { formatScientificAxisExponent, formatScientificAxisLabel, paddedDomain } from '../../utils'
|
||||
import { paddedDomain } from '../../utils'
|
||||
import {
|
||||
layoutAnnotations,
|
||||
type AnnotationSeriesInfo,
|
||||
@@ -28,9 +28,9 @@ import {
|
||||
} from './grid'
|
||||
import {
|
||||
buildTrackLayouts,
|
||||
axisTextMetrics,
|
||||
measureTrackYAxisClearance,
|
||||
resolveYAxisSeriesGroups,
|
||||
Y_AXIS_EXPONENT_GAP,
|
||||
} from './layout'
|
||||
import type { DisplaySeries, DisplayTrack, TrackLayout } from './types'
|
||||
import type { PreparedWaveformSeries } from './useWaveformData'
|
||||
@@ -112,40 +112,18 @@ export function useWaveformLayout(context: LayoutContext) {
|
||||
.flatMap((track) =>
|
||||
resolveYAxisSeriesGroups(track, props.overlayMode, props.yDomain, props.yDomains),
|
||||
)
|
||||
.map((group) => {
|
||||
const scale = scaleLinear(group.domain, [1, 0])
|
||||
if (!group.fixed) scale.nice()
|
||||
const [axisMin, axisMax] = scale.domain()
|
||||
return {
|
||||
exponentLabel: formatScientificAxisExponent(axisMin, axisMax),
|
||||
tickLabels: scale
|
||||
.ticks(10)
|
||||
.map((value) => formatScientificAxisLabel(value, { axisMin, axisMax })),
|
||||
}
|
||||
})
|
||||
const maximumCharacters = Math.max(
|
||||
1,
|
||||
...axisText.flatMap(({ tickLabels }) => tickLabels).map((label) => label.length),
|
||||
)
|
||||
const tickTextWidth = maximumCharacters * Y_AXIS_CHARACTER_WIDTH
|
||||
const maximumExponentWidth = Math.max(
|
||||
0,
|
||||
...axisText.map(({ exponentLabel }) => (exponentLabel?.length ?? 0) * Y_AXIS_CHARACTER_WIDTH),
|
||||
)
|
||||
const exponentClearance = maximumExponentWidth ? maximumExponentWidth + Y_AXIS_EXPONENT_GAP : 0
|
||||
const tickClearance =
|
||||
tickTextWidth + Y_AXIS_TICK_PADDING + exponentClearance + Y_AXIS_OUTER_PADDING
|
||||
.map((group) => axisTextMetrics(group.domain, !group.fixed).tickTextWidth)
|
||||
const tickTextWidth = Math.max(Y_AXIS_CHARACTER_WIDTH, ...axisText)
|
||||
const tickClearance = tickTextWidth + Y_AXIS_TICK_PADDING + Y_AXIS_OUTER_PADDING
|
||||
const labelCenterX = -(
|
||||
Y_AXIS_TICK_PADDING +
|
||||
tickTextWidth +
|
||||
exponentClearance +
|
||||
Y_AXIS_LABEL_GAP +
|
||||
Y_AXIS_LABEL_BAND_WIDTH / 2
|
||||
)
|
||||
const fullClearance =
|
||||
tickTextWidth +
|
||||
Y_AXIS_TICK_PADDING +
|
||||
exponentClearance +
|
||||
Y_AXIS_LABEL_GAP +
|
||||
Y_AXIS_LABEL_BAND_WIDTH +
|
||||
Y_AXIS_OUTER_PADDING
|
||||
|
||||
Reference in New Issue
Block a user