feat(chart): improve adaptive waveform layout
Some checks failed
CI / verify (push) Failing after 36s
CI / verify (pull_request) Failing after 31s

This commit is contained in:
李启源
2026-07-20 11:27:50 +08:00
parent ea832229da
commit f535e9611a
12 changed files with 563 additions and 59 deletions

28
src/utils/domain.test.ts Normal file
View File

@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest'
import { buildMinorTicks } from './domain'
describe('buildMinorTicks', () => {
it('preserves the existing behavior when no domain is provided', () => {
expect(buildMinorTicks([0, 10, 20], 5)).toEqual([2, 4, 6, 8, 12, 14, 16, 18])
})
it('fills the partial interval after the final major tick', () => {
const majorTicks = Array.from({ length: 13 }, (_, index) => -8000 + index * 1000)
expect(buildMinorTicks(majorTicks, 5, [-8000, 4990.3]).slice(-4)).toEqual([
4200, 4400, 4600, 4800,
])
})
it('fills both edge intervals and excludes the domain endpoints', () => {
const ticks = buildMinorTicks([-7000, -6000, -5000], 5, [-7950, -4100])
expect(ticks).toEqual([
-7800, -7600, -7400, -7200, -6800, -6600, -6400, -6200, -5800, -5600, -5400,
-5200, -4800, -4600, -4400, -4200,
])
expect(ticks).not.toContain(-7950)
expect(ticks).not.toContain(-4100)
})
})

View File

@@ -17,13 +17,34 @@ export function paddedDomain(values: number[]): [number, number] {
* 在主刻度之间生成次要刻度
* @param values 主刻度值数组
* @param subdivisions 细分数量,默认 5
* @param domain 可选的可见域,用于补齐首尾不完整主刻度区间内的次要刻度
* @returns 次要刻度值数组
*/
export function buildMinorTicks(values: number[], subdivisions = 5): number[] {
return values.flatMap((value, index) => {
const nextValue = values[index + 1]
export function buildMinorTicks(
values: number[],
subdivisions = 5,
domain?: readonly [number, number],
): number[] {
let intervalBoundaries = values
if (domain && values.length >= 2) {
const first = values[0]
const second = values[1]
const last = values[values.length - 1]
const previous = values[values.length - 2]
intervalBoundaries = [first - (second - first), ...values, last + (last - previous)]
}
const minorTicks = intervalBoundaries.flatMap((value, index) => {
const nextValue = intervalBoundaries[index + 1]
if (nextValue === undefined) return []
const step = (nextValue - value) / subdivisions
return Array.from({ length: subdivisions - 1 }, (_, minorIndex) => value + step * (minorIndex + 1))
})
if (!domain) return minorTicks
const domainMinimum = Math.min(...domain)
const domainMaximum = Math.max(...domain)
return minorTicks.filter((tick) => tick > domainMinimum && tick < domainMaximum)
}