UNPKG

3.25 kBPlain TextView Raw
1import { ScaleConfig } from '../types';
2
3export default function d3Linear(cfg: ScaleConfig): number[] {
4 const { min, max, nice, tickCount } = cfg;
5 const linear = new D3Linear();
6 linear.domain([min, max]);
7 if (nice) {
8 linear.nice(tickCount);
9 }
10 return linear.ticks(tickCount);
11}
12
13const DEFAULT_COUNT = 5;
14const e10 = Math.sqrt(50);
15const e5 = Math.sqrt(10);
16const e2 = Math.sqrt(2);
17
18// https://github.com/d3/d3-scale
19export class D3Linear {
20 private _domain: number[] = [0, 1];
21
22 public domain(domain?: number[]): D3Linear | number[] {
23 if (domain) {
24 this._domain = Array.from(domain, Number);
25 return this;
26 }
27 return this._domain.slice();
28 }
29
30 public nice(count = DEFAULT_COUNT) {
31 const d = this._domain.slice();
32 let i0 = 0;
33 let i1 = this._domain.length - 1;
34 let start = this._domain[i0];
35 let stop = this._domain[i1];
36 let step;
37
38 if (stop < start) {
39 [start, stop] = [stop, start];
40 [i0, i1] = [i1, i0];
41 }
42 step = tickIncrement(start, stop, count);
43
44 if (step > 0) {
45 start = Math.floor(start / step) * step;
46 stop = Math.ceil(stop / step) * step;
47 step = tickIncrement(start, stop, count);
48 } else if (step < 0) {
49 start = Math.ceil(start * step) / step;
50 stop = Math.floor(stop * step) / step;
51 step = tickIncrement(start, stop, count);
52 }
53
54 if (step > 0) {
55 d[i0] = Math.floor(start / step) * step;
56 d[i1] = Math.ceil(stop / step) * step;
57 this.domain(d);
58 } else if (step < 0) {
59 d[i0] = Math.ceil(start * step) / step;
60 d[i1] = Math.floor(stop * step) / step;
61 this.domain(d);
62 }
63
64 return this;
65 }
66
67 public ticks(count = DEFAULT_COUNT): number[] {
68 return d3ArrayTicks(this._domain[0], this._domain[this._domain.length - 1], count || DEFAULT_COUNT);
69 }
70}
71
72function d3ArrayTicks(start: number, stop: number, count: number): number[] {
73 let reverse;
74 let i = -1;
75 let n;
76 let ticks;
77 let step;
78
79 (stop = +stop), (start = +start), (count = +count);
80 if (start === stop && count > 0) {
81 return [start];
82 }
83 // tslint:disable-next-line
84 if ((reverse = stop < start)) {
85 (n = start), (start = stop), (stop = n);
86 }
87 // tslint:disable-next-line
88 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) {
89 return [];
90 }
91
92 if (step > 0) {
93 start = Math.ceil(start / step);
94 stop = Math.floor(stop / step);
95 ticks = new Array((n = Math.ceil(stop - start + 1)));
96 while (++i < n) {
97 ticks[i] = (start + i) * step;
98 }
99 } else {
100 start = Math.floor(start * step);
101 stop = Math.ceil(stop * step);
102 ticks = new Array((n = Math.ceil(start - stop + 1)));
103 while (++i < n) {
104 ticks[i] = (start - i) / step;
105 }
106 }
107
108 if (reverse) {
109 ticks.reverse();
110 }
111
112 return ticks;
113}
114
115function tickIncrement(start: number, stop: number, count: number): number {
116 const step = (stop - start) / Math.max(0, count);
117 const power = Math.floor(Math.log(step) / Math.LN10);
118 const error = step / Math.pow(10, power);
119
120 return power >= 0
121 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
122 : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
123}