Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module.exports = {
'func-names': ['error', 'never'],
'no-else-return': 'off',
'no-restricted-syntax': 'off',
'prefer-destructuring': 'off',
},
settings: {
'import/parsers': {
Expand Down
71 changes: 63 additions & 8 deletions src/scales/linear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,40 @@ export class Linear extends Continuous<LinearOptions> {
};
}

protected removeUnsortedValues(breaksDomain: number[], breaksRange: number[], reverse: boolean) {
let pre = -Infinity;
const deleteIndices = breaksRange.reduce((acc, current, i) => {
if (i === 0) return acc;
const value = pre > 0 ? pre : current;
if (pre > 0 && (reverse ? current > pre : current < pre)) {
acc.push(i);
} else {
const diff = (value - breaksRange[i - 1]) * (reverse ? -1 : 1);
if (diff < 0) {
if (pre < 0) pre = breaksRange[i - 1];
acc.push(i);
} else {
pre = -Infinity;
}
}
return acc;
}, [] as number[]);

deleteIndices
.slice()
.reverse()
.forEach((index) => {
breaksDomain.splice(index, 1);
breaksRange.splice(index, 1);
});

return { breaksDomain, breaksRange };
}

protected transformDomain(options: LinearOptions): { breaksDomain: number[]; breaksRange: number[] } {
const { domain, range = [1, 0], breaks = [], tickCount = 5 } = options;
const RANGE_LIMIT = [0.2, 0.8];
const DEFAULT_GAP = 0.03;
const { domain = [], range = [1, 0], breaks = [], tickCount = 5 } = options;
const [domainMin, domainMax] = [Math.min(...domain), Math.max(...domain)];
const sortedBreaks = breaks.filter(({ end }) => end < domainMax).sort((a, b) => a.start - b.start);
const breaksDomain = d3Ticks(domainMin, domainMax, tickCount, sortedBreaks);
Expand All @@ -43,24 +75,47 @@ export class Linear extends Continuous<LinearOptions> {
return reverse ? r0 - ratio * diffRange : r0 + ratio * diffRange;
});
// Compress the range scale according to breaks.
sortedBreaks.forEach(({ start, end, gap = 0.05 }) => {
const [MIN, MAX] = RANGE_LIMIT;
sortedBreaks.forEach(({ start, end, gap = DEFAULT_GAP, compress = 'middle' }) => {
const startIndex = breaksDomain.indexOf(start);
const endIndex = breaksDomain.indexOf(end);
const center = (breaksRange[startIndex] + breaksRange[endIndex]) / 2;
const scaledSpan = gap * diffRange;
let value = (breaksRange[startIndex] + breaksRange[endIndex]) / 2;
if (compress === 'start') value = breaksRange[startIndex];
if (compress === 'end') value = breaksRange[endIndex];
const halfSpan = (gap * diffRange) / 2;
// Calculate the new start and end values based on the center and scaled span.
const startValue = reverse ? center + scaledSpan / 2 : center - scaledSpan / 2;
const endValue = reverse ? center - scaledSpan / 2 : center + scaledSpan / 2;
let startValue = reverse ? value + halfSpan : value - halfSpan;
let endValue = reverse ? value - halfSpan : value + halfSpan;

// Ensure the new start and end values are within the defined limits.
if (startValue < MIN) {
endValue += MIN - startValue;
startValue = MIN;
}
if (endValue > MAX) {
startValue -= endValue - MAX;
endValue = MAX;
}
if (startValue > MAX) {
endValue -= startValue - MAX;
startValue = MAX;
}
if (endValue < MIN) {
startValue += MIN - endValue;
endValue = MIN;
}

breaksRange[startIndex] = startValue;
breaksRange[endIndex] = endValue;
});
return { breaksDomain, breaksRange };

return this.removeUnsortedValues(breaksDomain, breaksRange, reverse);
}

protected transformBreaks(options: LinearOptions): LinearOptions {
const { domain, breaks = [] } = options;
if (!isArray(options.breaks)) return options;
const domainMax = Math.max(...domain);
const domainMax = Math.max(...(domain as number[]));
const filteredBreaks = breaks.filter(({ end }) => end < domainMax);
const optWithFilteredBreaks = { ...options, breaks: filteredBreaks };
const { breaksDomain, breaksRange } = this.transformDomain(optWithFilteredBreaks);
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export type CreateTransform = (...args: any[]) => Transform;
export interface BreakOptions {
start: number; // 断轴开始
end: number; // 断轴结束
gap: number; // 在可视 range 中保留的间隔长度(0 ~ 1),默认为 0.05,表示 5% 的间隔
gap: number; // 在可视 range 中保留的间隔长度(0 ~ 1),默认为 0.03,表示 3% 的间隔
compress?: 'middle' | 'start' | 'end'; // 压缩方式,默认 middle
}

/** 通用的配置 */
Expand Down