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
47 changes: 43 additions & 4 deletions __tests__/unit/scales/linear-breaks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,8 @@ describe('Linear Scale with Breaks', () => {
});

const { domain, range } = scale.getOptions();
expect(domain).toStrictEqual([0, 200, 300, 500, 600, 800, 980]);
expect(range).toStrictEqual([
0, 0.20408163265306123, 0.35816326530612247, 0.45816326530612245, 0.6892857142857143, 0.7392857142857143, 1,
]);
expect(domain).toStrictEqual([0, 200, 300, 500, 600, 800, 1000]);
expect(range).toStrictEqual([0, 0.2, 0.35000000000000003, 0.45, 0.6749999999999999, 0.725, 1]);
});

test('single break: update', () => {
Expand Down Expand Up @@ -109,4 +107,45 @@ describe('Linear Scale with Breaks', () => {
expect(scale2).toBeInstanceOf(Linear);
expect(scale2.getOptions()).toEqual(scale.getOptions());
});
test.only('breaks with nice', () => {
const scale = new Linear({
domain: [0, 3106679],
breaks: [{ start: 5000, end: 50000, gap: 0.03 }],
});

const { domain, range } = scale.getOptions();
expect(domain).toStrictEqual([0, 5000, 50000, 1000000, 1500000, 2000000, 2500000, 3000000, 3150000]);

expect(range).toStrictEqual([
1, 0.8, 0.7700000000000001, 0.6825396825396826, 0.5238095238095238, 0.3650793650793651, 0.2063492063492064,
0.04761904761904767, 0,
]);
scale.update({
domain: [0, 3106679],
nice: true,
breaks: [{ start: 5000, end: 50000, gap: 0.03 }],
});
const scaleOptions = scale.getOptions();
expect(scaleOptions.domain).toStrictEqual([0, 5000, 50000, 1000000, 1500000, 2000000, 2500000, 3000000, 3500000]);
expect(scaleOptions.range).toStrictEqual([
1, 0.8, 0.7700000000000001, 0.7142857142857143, 0.5714285714285714, 0.4285714285714286, 0.2857142857142857,
0.1428571428571429, 0,
]);
scale.update({
domain: [0, 3106679],
nice: true,
breaks: [
{ start: 5000, end: 50000, gap: 0.03 },
{
start: 105000,
end: 3100000,
gap: 0.03,
},
],
});
const scaleOptions2 = scale.getOptions();
expect(scaleOptions2.domain).toStrictEqual([0, 5000, 50000, 105000, 3100000, 3108000]);

expect(scaleOptions2.range).toStrictEqual([1, 0.8, 0.7700000000000001, 0.4993951093951094, 0.46939510939510937, 0]);
});
});
48 changes: 48 additions & 0 deletions docs/api/scales/linear.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,45 @@ x2.getTicks(); // [0, 2.5, 5, 7.5, 10, 12.5, 15, 17.5]
x3.getTicks(); // [2, 4.5, 7, 9.5, 12, 14.5]
```

- Breaks

```ts
import { Linear, LinearOptions } from '@antv/scale';

const options1: LinearOptions = {
domain: [0, 3106679],
breaks: [{ start: 5000, end: 50000, gap: 0.03 }],
};

const x1 = new Linear(options1);

x1.getTicks(); // [0, 5000, 50000, 1000000, 1500000, 2000000, 2500000, 3000000, 3150000]

// with nice
const options2: LinearOptions = {
domain: [0, 3106679],
nice: true,
breaks: [{ start: 5000, end: 50000, gap: 0.03 }],
};

const x2 = new Linear(options2);

x2.getTicks(); // [0, 5000, 50000, 1000000, 1500000, 2000000, 2500000, 3000000, 3500000]

// multi breaks
const options3:LinearOptions = {
domain: [0, 200],
breaks: [
{ start: 40, end: 100, gap: 0.1 },
{ start: 120, end: 160, gap: 0.1 },
]
}

const x3 = new Linear(options3);

x3.getTicks(); // [0, 40, 100, 120, 160, 200]
```

## Options

| Key | Description | Type | Default|
Expand All @@ -175,6 +214,15 @@ x3.getTicks(); // [2, 4.5, 7, 9.5, 12, 14.5]
| clamp | Constrains the return value of map within the scale’s range if it is true. | `boolean` | `false` |
| nice | Extends the domain so that it starts and ends on nice round values if it is true. | `boolean` | `false` |
| interpolate | Sets the scale’s range interpolator factory if it is specified. | `(a: number, b: number) => (t: number) => T` | `(a, b) => (t) => a * (1 - t) + b * t` |
| breaks | Set linear breaks display and style. | [breaks](#breaks) | - |

### breaks

| Key | Description | Type | Default Value |
| ------- | ------- | ------- | ------- |
| start | start value. | `number` | - |
| end |end value. | `number` | - |
| gap | Proportion of the broken (0 ~ 1). | `number` | 0.03 |

## Methods

Expand Down
4 changes: 2 additions & 2 deletions src/scales/continuous.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { identity } from '@antv/util';
import { identity, isArray } from '@antv/util';
import { Base } from './base';
import { ContinuousOptions, Domain, Range, NiceMethod, TickMethodOptions, CreateTransform, Transform } from '../types';
import {
Expand Down Expand Up @@ -110,7 +110,7 @@ export abstract class Continuous<O extends ContinuousOptions> extends Base<O> {
}

protected nice() {
if (!this.options.nice) return;
if (!this.options.nice || isArray(this.options.breaks)) return;
const [min, max, tickCount, ...rest] = this.getTickMethodOptions();
this.options.domain = this.chooseNice()(min, max, tickCount, ...rest);
}
Expand Down
18 changes: 15 additions & 3 deletions src/scales/linear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { LinearOptions, Transform } from '../types';
import { Base } from './base';
import { createInterpolateValue } from '../utils';
import { d3Ticks } from '../tick-methods/d3-ticks';
import { d3LinearNice } from '../utils/d3-linear-nice';

/**
* Linear 比例尺
Expand Down Expand Up @@ -58,12 +59,23 @@ export class Linear extends Continuous<LinearOptions> {
protected transformDomain(options: LinearOptions): { breaksDomain: number[]; breaksRange: number[] } {
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 { domain = [], range = [1, 0], breaks = [], tickCount = 5, nice } = options;
const [min, max] = [Math.min(...domain), Math.max(...domain)];
let niceDomainMin = min;
let niceDomainMax = max;
if (nice && breaks.length < 2) {
const niceDomain = this.chooseNice()(min, max, tickCount) as number[];
niceDomainMin = niceDomain[0];
niceDomainMax = niceDomain[niceDomain.length - 1];
}
const domainMin = Math.min(niceDomainMin, min);
let domainMax = Math.max(niceDomainMax, max);
const sortedBreaks = breaks.filter(({ end }) => end < domainMax).sort((a, b) => a.start - b.start);
const breaksDomain = d3Ticks(domainMin, domainMax, tickCount, sortedBreaks);
if (last(breaksDomain) < domainMax) {
breaksDomain.push(domainMax);
const nicest = d3LinearNice(0, domainMax - last(breaksDomain), 3);
breaksDomain.push(last(breaksDomain) + last(nicest));
domainMax = last(breaksDomain);
}
const [r0, r1] = [range[0], last(range)] as number[];
const diffDomain = domainMax - domainMin;
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ export type ContinuousOptions = {
round?: boolean;
/** 插值器的工厂函数,返回一个对归一化后的输入在值域指定范围内插值的函数 */
interpolate?: Interpolates;
/** 断轴选项 */
breaks?: BreakOptions[];
};

/** Linear 比例尺的选项 */
Expand Down