1 | import {Transform} from 'vega-dataflow';
|
2 | import {bin} from 'vega-statistics';
|
3 | import {accessor, accessorFields, accessorName, inherits, toNumber} from 'vega-util';
|
4 |
|
5 |
|
6 | const EPSILON = 1e-14;
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | export default function Bin(params) {
|
16 | Transform.call(this, null, params);
|
17 | }
|
18 |
|
19 | Bin.Definition = {
|
20 | 'type': 'Bin',
|
21 | 'metadata': {'modifies': true},
|
22 | 'params': [
|
23 | { 'name': 'field', 'type': 'field', 'required': true },
|
24 | { 'name': 'interval', 'type': 'boolean', 'default': true },
|
25 | { 'name': 'anchor', 'type': 'number' },
|
26 | { 'name': 'maxbins', 'type': 'number', 'default': 20 },
|
27 | { 'name': 'base', 'type': 'number', 'default': 10 },
|
28 | { 'name': 'divide', 'type': 'number', 'array': true, 'default': [5, 2] },
|
29 | { 'name': 'extent', 'type': 'number', 'array': true, 'length': 2, 'required': true },
|
30 | { 'name': 'span', 'type': 'number' },
|
31 | { 'name': 'step', 'type': 'number' },
|
32 | { 'name': 'steps', 'type': 'number', 'array': true },
|
33 | { 'name': 'minstep', 'type': 'number', 'default': 0 },
|
34 | { 'name': 'nice', 'type': 'boolean', 'default': true },
|
35 | { 'name': 'name', 'type': 'string' },
|
36 | { 'name': 'as', 'type': 'string', 'array': true, 'length': 2, 'default': ['bin0', 'bin1'] }
|
37 | ]
|
38 | };
|
39 |
|
40 | inherits(Bin, Transform, {
|
41 | transform(_, pulse) {
|
42 | const band = _.interval !== false,
|
43 | bins = this._bins(_),
|
44 | start = bins.start,
|
45 | step = bins.step,
|
46 | as = _.as || ['bin0', 'bin1'],
|
47 | b0 = as[0],
|
48 | b1 = as[1];
|
49 |
|
50 | let flag;
|
51 | if (_.modified()) {
|
52 | pulse = pulse.reflow(true);
|
53 | flag = pulse.SOURCE;
|
54 | } else {
|
55 | flag = pulse.modified(accessorFields(_.field)) ? pulse.ADD_MOD : pulse.ADD;
|
56 | }
|
57 |
|
58 | pulse.visit(flag, band
|
59 | ? t => {
|
60 | const v = bins(t);
|
61 |
|
62 | t[b0] = v;
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | t[b1] = v == null ? null : start + step * (1 + (v - start) / step);
|
68 | }
|
69 | : t => t[b0] = bins(t)
|
70 | );
|
71 |
|
72 | return pulse.modifies(band ? as : b0);
|
73 | },
|
74 |
|
75 | _bins(_) {
|
76 | if (this.value && !_.modified()) {
|
77 | return this.value;
|
78 | }
|
79 |
|
80 | const field = _.field,
|
81 | bins = bin(_),
|
82 | step = bins.step;
|
83 | let start = bins.start,
|
84 | stop = start + Math.ceil((bins.stop - start) / step) * step,
|
85 | a, d;
|
86 |
|
87 | if ((a = _.anchor) != null) {
|
88 | d = a - (start + step * Math.floor((a - start) / step));
|
89 | start += d;
|
90 | stop += d;
|
91 | }
|
92 |
|
93 | const f = function(t) {
|
94 | let v = toNumber(field(t));
|
95 | return v == null ? null
|
96 | : v < start ? -Infinity
|
97 | : v > stop ? +Infinity
|
98 | : (
|
99 | v = Math.max(start, Math.min(v, stop - step)),
|
100 | start + step * Math.floor(EPSILON + (v - start) / step)
|
101 | );
|
102 | };
|
103 |
|
104 | f.start = start;
|
105 | f.stop = bins.stop;
|
106 | f.step = step;
|
107 |
|
108 | return this.value = accessor(
|
109 | f,
|
110 | accessorFields(field),
|
111 | _.name || 'bin_' + accessorName(field)
|
112 | );
|
113 | }
|
114 | });
|