oncoprintjs 5.0.3 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +14746 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.js +14760 -0
- package/dist/index.js.map +1 -0
- package/dist/js/CachedProperty.d.ts +10 -10
- package/dist/js/binarysearch.d.ts +1 -1
- package/dist/js/bucketsort.d.ts +16 -16
- package/dist/js/clustering.d.ts +14 -14
- package/dist/js/extractrgba.d.ts +4 -4
- package/dist/js/haselementsininterval.d.ts +1 -1
- package/dist/js/heatmapcolors.d.ts +5 -4
- package/dist/js/makesvgelement.d.ts +1 -1
- package/dist/js/modelutils.d.ts +7 -7
- package/dist/js/oncoprint.d.ts +168 -170
- package/dist/js/oncoprintheaderview.d.ts +23 -22
- package/dist/js/oncoprintlabelview.d.ts +79 -78
- package/dist/js/oncoprintlegendrenderer.d.ts +32 -31
- package/dist/js/oncoprintminimapview.d.ts +69 -68
- package/dist/js/oncoprintmodel.d.ts +403 -398
- package/dist/js/oncoprintruleset.d.ts +176 -177
- package/dist/js/oncoprintshape.d.ts +67 -67
- package/dist/js/oncoprintshapetosvg.d.ts +2 -2
- package/dist/js/oncoprintshapetovertexes.d.ts +5 -5
- package/dist/js/oncoprinttooltip.d.ts +23 -22
- package/dist/js/oncoprinttrackinfoview.d.ts +40 -39
- package/dist/js/oncoprinttrackoptionsview.d.ts +58 -57
- package/dist/js/oncoprintwebglcellview.d.ts +168 -167
- package/dist/js/oncoprintzoomslider.d.ts +28 -27
- package/dist/js/polyfill.d.ts +4 -4
- package/dist/js/precomputedcomparator.d.ts +13 -13
- package/dist/js/shaders.d.ts +2 -2
- package/dist/js/svgfactory.d.ts +24 -23
- package/dist/js/utils.d.ts +16 -16
- package/dist/js/workers/clustering-worker.d.ts +19 -20
- package/dist/test/gradientCategoricalRuleset.spec.d.ts +1 -1
- package/dist/test/monolith.spec.d.ts +1 -1
- package/jest.config.ts +2 -0
- package/package.json +20 -26
- package/rollup.config.ts +14 -0
- package/rules/geneticrules.ts +344 -305
- package/server.js +11 -0
- package/src/img/menudots.svg +9 -9
- package/src/img/zoomtofit.svg +12 -12
- package/src/index.tsx +13 -0
- package/src/js/CachedProperty.ts +6 -7
- package/src/js/binarysearch.ts +8 -3
- package/src/js/bucketsort.ts +89 -47
- package/src/js/clustering.ts +22 -10
- package/src/js/extractrgba.ts +16 -12
- package/src/js/haselementsininterval.ts +8 -4
- package/src/js/heatmapcolors.ts +515 -515
- package/src/js/main.js +1 -1
- package/src/js/makesvgelement.ts +2 -2
- package/src/js/modelutils.ts +11 -8
- package/src/js/oncoprint.ts +706 -385
- package/src/js/oncoprintheaderview.ts +165 -125
- package/src/js/oncoprintlabelview.ts +388 -170
- package/src/js/oncoprintlegendrenderer.ts +203 -72
- package/src/js/oncoprintminimapview.ts +965 -423
- package/src/js/oncoprintmodel.ts +905 -532
- package/src/js/oncoprintruleset.ts +694 -379
- package/src/js/oncoprintshape.ts +240 -97
- package/src/js/oncoprintshapetosvg.ts +77 -26
- package/src/js/oncoprintshapetovertexes.ts +153 -48
- package/src/js/oncoprinttooltip.ts +58 -27
- package/src/js/oncoprinttrackinfoview.ts +115 -59
- package/src/js/oncoprinttrackoptionsview.ts +354 -187
- package/src/js/oncoprintwebglcellview.ts +951 -415
- package/src/js/oncoprintzoomslider.ts +172 -107
- package/src/js/polyfill.ts +7 -3
- package/src/js/precomputedcomparator.ts +133 -50
- package/src/js/shaders.ts +2 -4
- package/src/js/svgfactory.ts +128 -73
- package/src/js/utils.ts +51 -31
- package/src/js/workers/clustering-worker.ts +50 -42
- package/src/test/gradientCategoricalRuleset.spec.ts +55 -38
- package/src/test/monolith.spec.ts +718 -285
- package/test/generate_data.py +108 -0
- package/test/glyphmap-data.js +1041 -0
- package/test/heatmap-data.js +1027 -0
- package/test/index.html +21 -0
- package/test/oncoprint-glyphmap.js +79 -0
- package/test/oncoprint-heatmap.js +123 -0
- package/tsconfig.json +4 -10
- package/tsconfig.test.json +11 -0
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/oncoprintjs.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/.idea/workspace.xml +0 -105
- package/dist/.gitkeep +0 -0
- package/dist/js/minimaputils.d.ts +0 -0
- package/dist/oncoprint.bundle.js +0 -44313
- package/jest.config.js +0 -12
- package/src/js/minimaputils.ts +0 -0
- package/typings/custom.d.ts +0 -7
- package/typings/missing.d.ts +0 -7
- package/webpack.config.js +0 -43
|
@@ -1,61 +1,78 @@
|
|
|
1
1
|
/* Rule:
|
|
2
|
-
*
|
|
2
|
+
*
|
|
3
3
|
* condition: function from datum to boolean
|
|
4
4
|
* shapes - a list of Shapes
|
|
5
5
|
* legend_label
|
|
6
6
|
* exclude_from_legend
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* Shape:
|
|
9
9
|
* type
|
|
10
10
|
* x
|
|
11
11
|
* y
|
|
12
12
|
* ... shape-specific attrs ...
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
* Attrs by shape:
|
|
15
|
-
*
|
|
15
|
+
*
|
|
16
16
|
* rectangle: x, y, width, height, stroke, stroke-width, fill
|
|
17
17
|
* triangle: x1, y1, x2, y2, x3, y3, stroke, stroke-width, fill
|
|
18
18
|
* ellipse: x, y, width, height, stroke, stroke-width, fill
|
|
19
19
|
* line: x1, y1, x2, y2, stroke, stroke-width
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
import {
|
|
23
|
+
ComputedShapeParams,
|
|
24
|
+
Ellipse,
|
|
25
|
+
Line,
|
|
26
|
+
Rectangle,
|
|
27
|
+
Shape,
|
|
28
|
+
ShapeParams,
|
|
29
|
+
Triangle,
|
|
30
|
+
} from './oncoprintshape';
|
|
31
|
+
import heatmapColors from './heatmapcolors';
|
|
32
|
+
import binarysearch from './binarysearch';
|
|
33
|
+
import {
|
|
34
|
+
Omit,
|
|
35
|
+
cloneShallow,
|
|
36
|
+
ifndef,
|
|
37
|
+
objectValues,
|
|
38
|
+
shallowExtend,
|
|
39
|
+
z_comparator,
|
|
40
|
+
} from './utils';
|
|
41
|
+
import { ActiveRules, ColumnProp, Datum, RuleSetId } from './oncoprintmodel';
|
|
42
|
+
import _ from 'lodash';
|
|
43
|
+
import extractrgba, { hexToRGBA, rgbaToHex } from './extractrgba';
|
|
44
|
+
|
|
45
|
+
export type RuleSetParams =
|
|
46
|
+
| ILinearInterpRuleSetParams
|
|
47
|
+
| ICategoricalRuleSetParams
|
|
48
|
+
| IGradientRuleSetParams
|
|
49
|
+
| IBarRuleSetParams
|
|
50
|
+
| IStackedBarRuleSetParams
|
|
51
|
+
| IGradientAndCategoricalRuleSetParams
|
|
52
|
+
| IGeneticAlterationRuleSetParams;
|
|
36
53
|
|
|
37
54
|
interface IGeneralRuleSetParams {
|
|
38
|
-
type?:RuleSetType;
|
|
55
|
+
type?: RuleSetType;
|
|
39
56
|
legend_label?: string;
|
|
40
57
|
legend_base_color?: RGBAColor;
|
|
41
58
|
exclude_from_legend?: boolean;
|
|
42
|
-
na_z?:number; // z index of na shapes (defaults to 1)
|
|
43
|
-
na_legend_label?:string; // legend label associated to NA (defaults to 'No data')
|
|
44
|
-
na_shapes?:ShapeParams[]; // defaults to single strikethrough line
|
|
59
|
+
na_z?: number; // z index of na shapes (defaults to 1)
|
|
60
|
+
na_legend_label?: string; // legend label associated to NA (defaults to 'No data')
|
|
61
|
+
na_shapes?: ShapeParams[]; // defaults to single strikethrough line
|
|
45
62
|
}
|
|
46
63
|
|
|
47
64
|
interface ILinearInterpRuleSetParams extends IGeneralRuleSetParams {
|
|
48
|
-
log_scale?:boolean;
|
|
49
|
-
value_key:string;
|
|
50
|
-
value_range:[number, number];
|
|
65
|
+
log_scale?: boolean;
|
|
66
|
+
value_key: string;
|
|
67
|
+
value_range: [number, number];
|
|
51
68
|
}
|
|
52
69
|
|
|
53
70
|
// all colors are hex, rgb, or rgba
|
|
54
71
|
export interface ICategoricalRuleSetParams extends IGeneralRuleSetParams {
|
|
55
72
|
type: RuleSetType.CATEGORICAL;
|
|
56
73
|
category_key: string; // key into data which gives category
|
|
57
|
-
category_to_color?: {[category:string]:RGBAColor};
|
|
58
|
-
universal_rule_categories?: {[category:string]:any};
|
|
74
|
+
category_to_color?: { [category: string]: RGBAColor };
|
|
75
|
+
universal_rule_categories?: { [category: string]: any };
|
|
59
76
|
}
|
|
60
77
|
|
|
61
78
|
export interface IGradientRuleSetParams extends ILinearInterpRuleSetParams {
|
|
@@ -65,13 +82,14 @@ export interface IGradientRuleSetParams extends ILinearInterpRuleSetParams {
|
|
|
65
82
|
colormap_name?: string; // name of a colormap found in src/js/heatmapcolors.js
|
|
66
83
|
value_stop_points: number[];
|
|
67
84
|
null_color?: RGBAColor;
|
|
68
|
-
null_legend_label?:string;
|
|
85
|
+
null_legend_label?: string;
|
|
69
86
|
}
|
|
70
87
|
|
|
71
88
|
// TODO: it would be more elegant to create multiple inheritance (if possible) since
|
|
72
89
|
// IGradientAndCategoricalRuleSetParams is a IGradientRuleSetParams and
|
|
73
90
|
// ICategoricalRuleSetParams with a different `type` field.
|
|
74
|
-
export interface IGradientAndCategoricalRuleSetParams
|
|
91
|
+
export interface IGradientAndCategoricalRuleSetParams
|
|
92
|
+
extends IGeneralRuleSetParams {
|
|
75
93
|
type: RuleSetType.GRADIENT_AND_CATEGORICAL;
|
|
76
94
|
// either `colormap_name` or `colors` needs to be present
|
|
77
95
|
colors?: RGBAColor[];
|
|
@@ -79,12 +97,12 @@ export interface IGradientAndCategoricalRuleSetParams extends IGeneralRuleSetPar
|
|
|
79
97
|
value_stop_points: number[];
|
|
80
98
|
null_color?: RGBAColor;
|
|
81
99
|
|
|
82
|
-
log_scale?:boolean;
|
|
100
|
+
log_scale?: boolean;
|
|
83
101
|
value_key: string;
|
|
84
102
|
value_range: [number, number];
|
|
85
103
|
|
|
86
104
|
category_key: string; // key into data which gives category
|
|
87
|
-
category_to_color?: {[category:string]:RGBAColor};
|
|
105
|
+
category_to_color?: { [category: string]: RGBAColor };
|
|
88
106
|
}
|
|
89
107
|
|
|
90
108
|
export interface IBarRuleSetParams extends ILinearInterpRuleSetParams {
|
|
@@ -108,97 +126,119 @@ export interface IGeneticAlterationRuleSetParams extends IGeneralRuleSetParams {
|
|
|
108
126
|
type GeneticAlterationSingleRuleParams = {
|
|
109
127
|
shapes: ShapeParams[];
|
|
110
128
|
legend_label: string;
|
|
111
|
-
exclude_from_legend?:boolean;
|
|
112
|
-
legend_order?:number;
|
|
129
|
+
exclude_from_legend?: boolean;
|
|
130
|
+
legend_order?: number;
|
|
113
131
|
};
|
|
114
132
|
|
|
115
133
|
export type GeneticAlterationRuleParams = {
|
|
116
|
-
always?:GeneticAlterationSingleRuleParams
|
|
117
|
-
conditional:{
|
|
118
|
-
[datumKey:string]:{
|
|
119
|
-
[commaSeparatedDatumValues:string]: GeneticAlterationSingleRuleParams
|
|
120
|
-
}
|
|
121
|
-
}
|
|
134
|
+
always?: GeneticAlterationSingleRuleParams;
|
|
135
|
+
conditional: {
|
|
136
|
+
[datumKey: string]: {
|
|
137
|
+
[commaSeparatedDatumValues: string]: GeneticAlterationSingleRuleParams;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
122
140
|
};
|
|
123
141
|
|
|
124
|
-
|
|
125
|
-
export type RGBAColor = [number,number,number,number]; //[0,255] x [0,255] x [0,255] x [0,1]
|
|
142
|
+
export type RGBAColor = [number, number, number, number]; //[0,255] x [0,255] x [0,255] x [0,1]
|
|
126
143
|
|
|
127
144
|
type RuleParams = {
|
|
128
|
-
shapes:ShapeParams[];
|
|
129
|
-
legend_label?:string;
|
|
130
|
-
exclude_from_legend?:boolean;
|
|
131
|
-
legend_config?:RuleLegendConfig;
|
|
132
|
-
legend_order?:number;
|
|
133
|
-
legend_base_color?:RGBAColor;
|
|
145
|
+
shapes: ShapeParams[];
|
|
146
|
+
legend_label?: string;
|
|
147
|
+
exclude_from_legend?: boolean;
|
|
148
|
+
legend_config?: RuleLegendConfig;
|
|
149
|
+
legend_order?: number;
|
|
150
|
+
legend_base_color?: RGBAColor;
|
|
134
151
|
};
|
|
135
152
|
|
|
136
153
|
type RuleLegendConfig =
|
|
137
|
-
{ type:
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
{
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
| { type: 'rule'; target: any }
|
|
155
|
+
| {
|
|
156
|
+
type: 'number';
|
|
157
|
+
range: [number, number];
|
|
158
|
+
range_type: LinearInterpRangeType;
|
|
159
|
+
positive_color: RGBAColor;
|
|
160
|
+
negative_color: RGBAColor;
|
|
161
|
+
interpFn: (val: number) => number;
|
|
162
|
+
} // range: [lower, upper]
|
|
163
|
+
| {
|
|
164
|
+
type: 'gradient';
|
|
165
|
+
range: [number, number];
|
|
166
|
+
colorFn: (val: number) => RGBAColor;
|
|
167
|
+
};
|
|
152
168
|
|
|
153
169
|
export enum RuleSetType {
|
|
154
|
-
CATEGORICAL =
|
|
155
|
-
GRADIENT =
|
|
156
|
-
GRADIENT_AND_CATEGORICAL =
|
|
157
|
-
BAR =
|
|
158
|
-
STACKED_BAR =
|
|
159
|
-
GENE =
|
|
170
|
+
CATEGORICAL = 'categorical',
|
|
171
|
+
GRADIENT = 'gradient',
|
|
172
|
+
GRADIENT_AND_CATEGORICAL = 'gradient+categorical',
|
|
173
|
+
BAR = 'bar',
|
|
174
|
+
STACKED_BAR = 'stacked_bar',
|
|
175
|
+
GENE = 'gene',
|
|
160
176
|
}
|
|
161
177
|
|
|
162
178
|
export type RuleId = number;
|
|
163
179
|
|
|
164
180
|
export type RuleWithId = {
|
|
165
|
-
id:RuleId;
|
|
166
|
-
rule:Rule;
|
|
181
|
+
id: RuleId;
|
|
182
|
+
rule: Rule;
|
|
167
183
|
};
|
|
168
184
|
|
|
169
185
|
function makeIdCounter() {
|
|
170
186
|
let id = 0;
|
|
171
|
-
return function
|
|
187
|
+
return function() {
|
|
172
188
|
id += 1;
|
|
173
189
|
return id;
|
|
174
190
|
};
|
|
175
191
|
}
|
|
176
192
|
|
|
177
|
-
function intRange(length:number) {
|
|
193
|
+
function intRange(length: number) {
|
|
178
194
|
const ret = [];
|
|
179
|
-
for (let i=0; i<length; i++) {
|
|
195
|
+
for (let i = 0; i < length; i++) {
|
|
180
196
|
ret.push(i);
|
|
181
197
|
}
|
|
182
198
|
return ret;
|
|
183
199
|
}
|
|
184
200
|
|
|
185
|
-
|
|
186
|
-
function makeUniqueColorGetter(init_used_colors:string[]) {
|
|
201
|
+
function makeUniqueColorGetter(init_used_colors: string[]) {
|
|
187
202
|
init_used_colors = init_used_colors || [];
|
|
188
|
-
const colors = [
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
203
|
+
const colors = [
|
|
204
|
+
'#3366cc',
|
|
205
|
+
'#dc3912',
|
|
206
|
+
'#ff9900',
|
|
207
|
+
'#109618',
|
|
208
|
+
'#990099',
|
|
209
|
+
'#0099c6',
|
|
210
|
+
'#dd4477',
|
|
211
|
+
'#66aa00',
|
|
212
|
+
'#b82e2e',
|
|
213
|
+
'#316395',
|
|
214
|
+
'#994499',
|
|
215
|
+
'#22aa99',
|
|
216
|
+
'#aaaa11',
|
|
217
|
+
'#6633cc',
|
|
218
|
+
'#e67300',
|
|
219
|
+
'#8b0707',
|
|
220
|
+
'#651067',
|
|
221
|
+
'#329262',
|
|
222
|
+
'#5574a6',
|
|
223
|
+
'#3b3eac',
|
|
224
|
+
'#b77322',
|
|
225
|
+
'#16d620',
|
|
226
|
+
'#b91383',
|
|
227
|
+
'#f4359e',
|
|
228
|
+
'#9c5935',
|
|
229
|
+
'#a9c413',
|
|
230
|
+
'#2a778d',
|
|
231
|
+
'#668d1c',
|
|
232
|
+
'#bea413',
|
|
233
|
+
'#0c5922',
|
|
234
|
+
'#743411',
|
|
235
|
+
]; // Source: D3
|
|
196
236
|
let index = 0;
|
|
197
|
-
const used_colors:{[color:string]:boolean} = {};
|
|
198
|
-
for (let i=0; i<init_used_colors.length; i++) {
|
|
237
|
+
const used_colors: { [color: string]: boolean } = {};
|
|
238
|
+
for (let i = 0; i < init_used_colors.length; i++) {
|
|
199
239
|
used_colors[init_used_colors[i]] = true;
|
|
200
240
|
}
|
|
201
|
-
return function(color?:string) {
|
|
241
|
+
return function(color?: string) {
|
|
202
242
|
if (color) {
|
|
203
243
|
// calling with an argument adds it to the used colors record
|
|
204
244
|
used_colors[color] = true;
|
|
@@ -217,35 +257,40 @@ function makeUniqueColorGetter(init_used_colors:string[]) {
|
|
|
217
257
|
|
|
218
258
|
return hexToRGBA(next_color);
|
|
219
259
|
}
|
|
260
|
+
|
|
261
|
+
return undefined;
|
|
220
262
|
};
|
|
221
263
|
}
|
|
222
264
|
|
|
223
|
-
function makeNAShapes(z:number):ShapeParams[] {
|
|
265
|
+
function makeNAShapes(z: number): ShapeParams[] {
|
|
224
266
|
return [
|
|
225
267
|
{
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
'
|
|
268
|
+
type: 'rectangle',
|
|
269
|
+
fill: [255, 255, 255, 1],
|
|
270
|
+
z: z,
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
type: 'line',
|
|
274
|
+
stroke: [190, 190, 190, 1],
|
|
232
275
|
'stroke-width': 1,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
276
|
+
x1: 0,
|
|
277
|
+
x2: 100,
|
|
278
|
+
y1: 50,
|
|
279
|
+
y2: 50,
|
|
280
|
+
z: z,
|
|
281
|
+
},
|
|
239
282
|
];
|
|
240
283
|
}
|
|
241
|
-
const NA_STRING =
|
|
242
|
-
const NA_LABEL =
|
|
284
|
+
const NA_STRING = 'na';
|
|
285
|
+
const NA_LABEL = 'No data';
|
|
243
286
|
|
|
244
|
-
function colorToHex(color:string) {
|
|
287
|
+
function colorToHex(color: string) {
|
|
245
288
|
let r;
|
|
246
289
|
let g;
|
|
247
290
|
let b;
|
|
248
|
-
const rgba_match = color.match(
|
|
291
|
+
const rgba_match = color.match(
|
|
292
|
+
/^[\s]*rgba\([\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9.]+)[\s]*\)[\s]*$/
|
|
293
|
+
);
|
|
249
294
|
if (rgba_match && rgba_match.length === 5) {
|
|
250
295
|
r = parseInt(rgba_match[1]).toString(16);
|
|
251
296
|
g = parseInt(rgba_match[2]).toString(16);
|
|
@@ -262,7 +307,9 @@ function colorToHex(color:string) {
|
|
|
262
307
|
return '#' + r + g + b;
|
|
263
308
|
}
|
|
264
309
|
|
|
265
|
-
const rgb_match = color.match(
|
|
310
|
+
const rgb_match = color.match(
|
|
311
|
+
/^[\s]*rgb\([\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*\)[\s]*$/
|
|
312
|
+
);
|
|
266
313
|
if (rgb_match && rgb_match.length === 4) {
|
|
267
314
|
r = parseInt(rgb_match[1]).toString(16);
|
|
268
315
|
g = parseInt(rgb_match[2]).toString(16);
|
|
@@ -282,7 +329,7 @@ function colorToHex(color:string) {
|
|
|
282
329
|
return color;
|
|
283
330
|
}
|
|
284
331
|
|
|
285
|
-
function darkenHexChannel(c:string) {
|
|
332
|
+
function darkenHexChannel(c: string) {
|
|
286
333
|
let numC = parseInt(c, 16);
|
|
287
334
|
numC *= 0.95;
|
|
288
335
|
numC = Math.round(numC);
|
|
@@ -293,7 +340,7 @@ function darkenHexChannel(c:string) {
|
|
|
293
340
|
return c;
|
|
294
341
|
}
|
|
295
342
|
|
|
296
|
-
function darkenHexColor(color:string) {
|
|
343
|
+
function darkenHexColor(color: string) {
|
|
297
344
|
let r = color[1] + color[2];
|
|
298
345
|
let g = color[3] + color[4];
|
|
299
346
|
let b = color[5] + color[6];
|
|
@@ -307,15 +354,15 @@ export class RuleSet {
|
|
|
307
354
|
static getRuleSetId = makeIdCounter();
|
|
308
355
|
static getRuleId = makeIdCounter();
|
|
309
356
|
|
|
310
|
-
public rule_set_id:RuleSetId;
|
|
311
|
-
public legend_label?:string;
|
|
312
|
-
protected legend_base_color?:RGBAColor;
|
|
313
|
-
public exclude_from_legend?:boolean;
|
|
314
|
-
protected active_rule_ids:ActiveRules;
|
|
315
|
-
protected rules_with_id:RuleWithId[];
|
|
316
|
-
protected universal_rule?:RuleWithId;
|
|
357
|
+
public rule_set_id: RuleSetId;
|
|
358
|
+
public legend_label?: string;
|
|
359
|
+
protected legend_base_color?: RGBAColor;
|
|
360
|
+
public exclude_from_legend?: boolean;
|
|
361
|
+
protected active_rule_ids: ActiveRules;
|
|
362
|
+
protected rules_with_id: RuleWithId[];
|
|
363
|
+
protected universal_rule?: RuleWithId;
|
|
317
364
|
|
|
318
|
-
constructor(params:Omit<RuleSetParams,
|
|
365
|
+
constructor(params: Omit<RuleSetParams, 'type'>) {
|
|
319
366
|
/* params:
|
|
320
367
|
* - legend_label
|
|
321
368
|
* - exclude_from_legend
|
|
@@ -336,26 +383,26 @@ export class RuleSet {
|
|
|
336
383
|
return this.rule_set_id;
|
|
337
384
|
}
|
|
338
385
|
|
|
339
|
-
public addRules(list_of_params:RuleParams[]) {
|
|
386
|
+
public addRules(list_of_params: RuleParams[]) {
|
|
340
387
|
const self = this;
|
|
341
|
-
return list_of_params.map(function
|
|
388
|
+
return list_of_params.map(function(params) {
|
|
342
389
|
return self._addRule(params);
|
|
343
390
|
});
|
|
344
391
|
}
|
|
345
392
|
|
|
346
|
-
public _addRule(params:RuleParams, rule_id?:RuleId) {
|
|
347
|
-
if (typeof rule_id ===
|
|
393
|
+
public _addRule(params: RuleParams, rule_id?: RuleId) {
|
|
394
|
+
if (typeof rule_id === 'undefined') {
|
|
348
395
|
rule_id = RuleSet.getRuleId();
|
|
349
396
|
}
|
|
350
|
-
this.rules_with_id.push({id: rule_id, rule: new Rule(params)});
|
|
397
|
+
this.rules_with_id.push({ id: rule_id, rule: new Rule(params) });
|
|
351
398
|
return rule_id;
|
|
352
399
|
}
|
|
353
400
|
|
|
354
|
-
public setUniversalRule(r:RuleWithId) {
|
|
401
|
+
public setUniversalRule(r: RuleWithId) {
|
|
355
402
|
this.universal_rule = r;
|
|
356
403
|
}
|
|
357
404
|
|
|
358
|
-
public removeRule(rule_id:RuleId) {
|
|
405
|
+
public removeRule(rule_id: RuleId) {
|
|
359
406
|
var index = -1;
|
|
360
407
|
for (let i = 0; i < this.rules_with_id.length; i++) {
|
|
361
408
|
if (this.rules_with_id[i].id === rule_id) {
|
|
@@ -369,7 +416,7 @@ export class RuleSet {
|
|
|
369
416
|
delete this.active_rule_ids[rule_id];
|
|
370
417
|
}
|
|
371
418
|
|
|
372
|
-
public getRuleWithId(rule_id:RuleId) {
|
|
419
|
+
public getRuleWithId(rule_id: RuleId) {
|
|
373
420
|
let ret = null;
|
|
374
421
|
for (let i = 0; i < this.rules_with_id.length; i++) {
|
|
375
422
|
if (this.rules_with_id[i].id === rule_id) {
|
|
@@ -384,36 +431,42 @@ export class RuleSet {
|
|
|
384
431
|
return this.exclude_from_legend;
|
|
385
432
|
}
|
|
386
433
|
|
|
387
|
-
public getRule(rule_id:RuleId):Rule {
|
|
434
|
+
public getRule(rule_id: RuleId): Rule {
|
|
388
435
|
return this.getRuleWithId(rule_id).rule;
|
|
389
436
|
}
|
|
390
437
|
|
|
391
438
|
public getRecentlyUsedRules() {
|
|
392
439
|
const self = this;
|
|
393
|
-
return Object.keys(this.active_rule_ids).map(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
});
|
|
440
|
+
return Object.keys(this.active_rule_ids).map(function(rule_id) {
|
|
441
|
+
return self.getRule(parseInt(rule_id, 10));
|
|
442
|
+
});
|
|
397
443
|
}
|
|
398
444
|
|
|
399
|
-
public applyRulesToDatum(
|
|
400
|
-
|
|
445
|
+
public applyRulesToDatum(
|
|
446
|
+
rules_with_id: RuleWithId[],
|
|
447
|
+
datum: Datum,
|
|
448
|
+
cell_width: number,
|
|
449
|
+
cell_height: number
|
|
450
|
+
) {
|
|
451
|
+
let shapes: ComputedShapeParams[] = [];
|
|
401
452
|
const rules_len = rules_with_id.length;
|
|
402
453
|
for (let j = 0; j < rules_len; j++) {
|
|
403
|
-
shapes = shapes.concat(
|
|
454
|
+
shapes = shapes.concat(
|
|
455
|
+
rules_with_id[j].rule.apply(datum, cell_width, cell_height)
|
|
456
|
+
);
|
|
404
457
|
}
|
|
405
458
|
return shapes;
|
|
406
459
|
}
|
|
407
460
|
|
|
408
|
-
public getSpecificRulesForDatum(datum?:Datum):RuleWithId[] {
|
|
409
|
-
throw
|
|
461
|
+
public getSpecificRulesForDatum(datum?: Datum): RuleWithId[] {
|
|
462
|
+
throw 'Not implemented on base class';
|
|
410
463
|
}
|
|
411
464
|
|
|
412
465
|
public getUniversalRule() {
|
|
413
466
|
return this.universal_rule;
|
|
414
467
|
}
|
|
415
468
|
|
|
416
|
-
public getUniversalShapes(cell_width:number, cell_height:number) {
|
|
469
|
+
public getUniversalShapes(cell_width: number, cell_height: number) {
|
|
417
470
|
if (this.getUniversalRule()) {
|
|
418
471
|
const shapes = this.getUniversalRule().rule.apply(
|
|
419
472
|
{}, // a universal rule does not rely on anything specific to the data
|
|
@@ -427,26 +480,42 @@ export class RuleSet {
|
|
|
427
480
|
}
|
|
428
481
|
}
|
|
429
482
|
|
|
430
|
-
public getSpecificShapesForDatum(
|
|
483
|
+
public getSpecificShapesForDatum(
|
|
484
|
+
data: Datum[],
|
|
485
|
+
cell_width: number,
|
|
486
|
+
cell_height: number,
|
|
487
|
+
out_active_rules?: ActiveRules | undefined,
|
|
488
|
+
data_id_key?: string & keyof Datum,
|
|
489
|
+
important_ids?: ColumnProp<boolean>
|
|
490
|
+
) {
|
|
431
491
|
// Returns a list of lists of concrete shapes, in the same order as data
|
|
432
492
|
// optional parameter important_ids determines which ids count towards active rules (optional parameter data_id_key
|
|
433
493
|
// is used for this too)
|
|
434
494
|
const ret = [];
|
|
435
495
|
for (var i = 0; i < data.length; i++) {
|
|
436
496
|
const datum = data[i];
|
|
437
|
-
const should_mark_active =
|
|
497
|
+
const should_mark_active =
|
|
498
|
+
!important_ids || !!important_ids[datum[data_id_key!]];
|
|
438
499
|
const rules = this.getSpecificRulesForDatum(datum);
|
|
439
500
|
if (typeof out_active_rules !== 'undefined' && should_mark_active) {
|
|
440
501
|
for (let j = 0; j < rules.length; j++) {
|
|
441
502
|
out_active_rules[rules[j].id] = true;
|
|
442
503
|
}
|
|
443
504
|
}
|
|
444
|
-
const shapes = this.applyRulesToDatum(
|
|
505
|
+
const shapes = this.applyRulesToDatum(
|
|
506
|
+
rules,
|
|
507
|
+
data[i],
|
|
508
|
+
cell_width,
|
|
509
|
+
cell_height
|
|
510
|
+
);
|
|
445
511
|
shapes.sort(z_comparator);
|
|
446
512
|
ret.push(shapes);
|
|
447
513
|
}
|
|
448
514
|
// mark universal rule as active
|
|
449
|
-
if (
|
|
515
|
+
if (
|
|
516
|
+
this.getUniversalRule() &&
|
|
517
|
+
typeof out_active_rules !== 'undefined'
|
|
518
|
+
) {
|
|
450
519
|
out_active_rules[this.getUniversalRule().id] = true;
|
|
451
520
|
}
|
|
452
521
|
return ret;
|
|
@@ -454,22 +523,29 @@ export class RuleSet {
|
|
|
454
523
|
}
|
|
455
524
|
|
|
456
525
|
class LookupRuleSet extends RuleSet {
|
|
457
|
-
private lookup_map_by_key_and_value:
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
526
|
+
private lookup_map_by_key_and_value: {
|
|
527
|
+
[key: string]: { [value: string]: RuleWithId };
|
|
528
|
+
} = {};
|
|
529
|
+
private lookup_map_by_key: { [key: string]: RuleWithId } = {};
|
|
530
|
+
private rule_id_to_conditions: {
|
|
531
|
+
[ruleId: number]: { key: string; value: string }[];
|
|
532
|
+
} = {};
|
|
533
|
+
|
|
534
|
+
public getSpecificRulesForDatum(datum?: Datum) {
|
|
462
535
|
if (typeof datum === 'undefined') {
|
|
463
536
|
return this.rules_with_id;
|
|
464
537
|
}
|
|
465
|
-
let ret:RuleWithId[] = [];
|
|
538
|
+
let ret: RuleWithId[] = [];
|
|
466
539
|
for (var key in datum) {
|
|
467
|
-
if (
|
|
540
|
+
if (key in datum && typeof datum[key] !== 'undefined') {
|
|
468
541
|
var key_rule = this.lookup_map_by_key[key];
|
|
469
542
|
if (typeof key_rule !== 'undefined') {
|
|
470
543
|
ret.push(key_rule);
|
|
471
544
|
}
|
|
472
|
-
var key_and_value_rule =
|
|
545
|
+
var key_and_value_rule =
|
|
546
|
+
(this.lookup_map_by_key_and_value[key] &&
|
|
547
|
+
this.lookup_map_by_key_and_value[key][datum[key]]) ||
|
|
548
|
+
undefined;
|
|
473
549
|
if (typeof key_and_value_rule !== 'undefined') {
|
|
474
550
|
ret.push(key_and_value_rule);
|
|
475
551
|
}
|
|
@@ -478,34 +554,61 @@ class LookupRuleSet extends RuleSet {
|
|
|
478
554
|
return ret;
|
|
479
555
|
}
|
|
480
556
|
|
|
481
|
-
private indexRuleForLookup(
|
|
557
|
+
private indexRuleForLookup(
|
|
558
|
+
condition_key: string,
|
|
559
|
+
condition_value: string,
|
|
560
|
+
rule_with_id: RuleWithId
|
|
561
|
+
) {
|
|
482
562
|
if (condition_key === null) {
|
|
483
563
|
this.setUniversalRule(rule_with_id);
|
|
484
564
|
} else {
|
|
485
565
|
if (condition_value === null) {
|
|
486
566
|
this.lookup_map_by_key[condition_key] = rule_with_id;
|
|
487
567
|
} else {
|
|
488
|
-
this.lookup_map_by_key_and_value[condition_key] =
|
|
489
|
-
|
|
568
|
+
this.lookup_map_by_key_and_value[condition_key] =
|
|
569
|
+
this.lookup_map_by_key_and_value[condition_key] || {};
|
|
570
|
+
this.lookup_map_by_key_and_value[condition_key][
|
|
571
|
+
condition_value
|
|
572
|
+
] = rule_with_id;
|
|
490
573
|
}
|
|
491
574
|
}
|
|
492
|
-
this.rule_id_to_conditions[rule_with_id.id] =
|
|
493
|
-
|
|
494
|
-
|
|
575
|
+
this.rule_id_to_conditions[rule_with_id.id] =
|
|
576
|
+
this.rule_id_to_conditions[rule_with_id.id] || [];
|
|
577
|
+
this.rule_id_to_conditions[rule_with_id.id].push({
|
|
578
|
+
key: condition_key,
|
|
579
|
+
value: condition_value,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
495
582
|
|
|
496
|
-
public addRule(
|
|
583
|
+
public addRule(
|
|
584
|
+
condition_key: string,
|
|
585
|
+
condition_value: any,
|
|
586
|
+
params: RuleParams
|
|
587
|
+
) {
|
|
497
588
|
const rule_id = this._addRule(params);
|
|
498
589
|
|
|
499
|
-
this.indexRuleForLookup(
|
|
590
|
+
this.indexRuleForLookup(
|
|
591
|
+
condition_key,
|
|
592
|
+
condition_value,
|
|
593
|
+
this.getRuleWithId(rule_id)
|
|
594
|
+
);
|
|
500
595
|
|
|
501
596
|
return rule_id;
|
|
502
597
|
}
|
|
503
598
|
|
|
504
|
-
public linkExistingRule(
|
|
505
|
-
|
|
599
|
+
public linkExistingRule(
|
|
600
|
+
condition_key: string,
|
|
601
|
+
condition_value: string,
|
|
602
|
+
existing_rule_id: RuleId
|
|
603
|
+
) {
|
|
604
|
+
this.indexRuleForLookup(
|
|
605
|
+
condition_key,
|
|
606
|
+
condition_value,
|
|
607
|
+
this.getRuleWithId(existing_rule_id)
|
|
608
|
+
);
|
|
506
609
|
}
|
|
507
610
|
|
|
508
|
-
public removeRule(rule_id:RuleId) {
|
|
611
|
+
public removeRule(rule_id: RuleId) {
|
|
509
612
|
super.removeRule(rule_id);
|
|
510
613
|
|
|
511
614
|
while (this.rule_id_to_conditions[rule_id].length > 0) {
|
|
@@ -517,7 +620,9 @@ class LookupRuleSet extends RuleSet {
|
|
|
517
620
|
if (condition.value === null) {
|
|
518
621
|
delete this.lookup_map_by_key[condition.key];
|
|
519
622
|
} else {
|
|
520
|
-
delete this.lookup_map_by_key_and_value[condition.key][
|
|
623
|
+
delete this.lookup_map_by_key_and_value[condition.key][
|
|
624
|
+
condition.value
|
|
625
|
+
];
|
|
521
626
|
}
|
|
522
627
|
}
|
|
523
628
|
}
|
|
@@ -525,28 +630,33 @@ class LookupRuleSet extends RuleSet {
|
|
|
525
630
|
}
|
|
526
631
|
}
|
|
527
632
|
|
|
528
|
-
type ConditionRuleSetCondition = (d:Datum)=>boolean;
|
|
633
|
+
type ConditionRuleSetCondition = (d: Datum) => boolean;
|
|
529
634
|
class ConditionRuleSet extends RuleSet {
|
|
530
|
-
private rule_id_to_condition:
|
|
635
|
+
private rule_id_to_condition: {
|
|
636
|
+
[ruleId: number]: ConditionRuleSetCondition;
|
|
637
|
+
} = {};
|
|
531
638
|
|
|
532
|
-
constructor(params:RuleSetParams, omitNArule?:boolean) {
|
|
639
|
+
constructor(params: RuleSetParams, omitNArule?: boolean) {
|
|
533
640
|
super(params);
|
|
534
641
|
|
|
535
642
|
if (!omitNArule) {
|
|
536
|
-
this.addRule(
|
|
643
|
+
this.addRule(
|
|
644
|
+
function(d) {
|
|
537
645
|
return d[NA_STRING] === true;
|
|
538
646
|
},
|
|
539
647
|
{
|
|
540
|
-
shapes:
|
|
648
|
+
shapes:
|
|
649
|
+
params.na_shapes || makeNAShapes(params.na_z || 1000),
|
|
541
650
|
legend_label: params.na_legend_label || NA_LABEL,
|
|
542
651
|
exclude_from_legend: false,
|
|
543
|
-
legend_config: {
|
|
544
|
-
legend_order: Number.POSITIVE_INFINITY
|
|
545
|
-
}
|
|
652
|
+
legend_config: { type: 'rule', target: { na: true } },
|
|
653
|
+
legend_order: Number.POSITIVE_INFINITY,
|
|
654
|
+
}
|
|
655
|
+
);
|
|
546
656
|
}
|
|
547
657
|
}
|
|
548
658
|
|
|
549
|
-
public getSpecificRulesForDatum(datum?:Datum) {
|
|
659
|
+
public getSpecificRulesForDatum(datum?: Datum) {
|
|
550
660
|
if (typeof datum === 'undefined') {
|
|
551
661
|
return this.rules_with_id;
|
|
552
662
|
}
|
|
@@ -559,39 +669,50 @@ class ConditionRuleSet extends RuleSet {
|
|
|
559
669
|
return ret;
|
|
560
670
|
}
|
|
561
671
|
|
|
562
|
-
public addRule(
|
|
672
|
+
public addRule(
|
|
673
|
+
condition: ConditionRuleSetCondition,
|
|
674
|
+
params: RuleParams,
|
|
675
|
+
rule_id?: RuleId
|
|
676
|
+
) {
|
|
563
677
|
rule_id = this._addRule(params, rule_id);
|
|
564
678
|
this.rule_id_to_condition[rule_id] = condition;
|
|
565
679
|
return rule_id;
|
|
566
680
|
}
|
|
567
681
|
|
|
568
|
-
public removeRule(rule_id:RuleId) {
|
|
682
|
+
public removeRule(rule_id: RuleId) {
|
|
569
683
|
super.removeRule(rule_id);
|
|
570
684
|
delete this.rule_id_to_condition[rule_id];
|
|
571
685
|
}
|
|
572
686
|
}
|
|
573
687
|
|
|
574
688
|
class CategoricalRuleSet extends LookupRuleSet {
|
|
575
|
-
public readonly category_key:string;
|
|
576
|
-
private readonly category_to_color:{[category:string]:RGBAColor};
|
|
577
|
-
private readonly getUnusedColor:(color?:string)=>RGBAColor;
|
|
578
|
-
private readonly universal_rule_categories?:{[category:string]:any};
|
|
579
|
-
constructor(
|
|
689
|
+
public readonly category_key: string;
|
|
690
|
+
private readonly category_to_color: { [category: string]: RGBAColor };
|
|
691
|
+
private readonly getUnusedColor: (color?: string) => RGBAColor;
|
|
692
|
+
private readonly universal_rule_categories?: { [category: string]: any };
|
|
693
|
+
constructor(
|
|
694
|
+
params: Omit<ICategoricalRuleSetParams, 'type'>,
|
|
695
|
+
omitNArule?: boolean
|
|
696
|
+
) {
|
|
580
697
|
super(params);
|
|
581
698
|
if (!omitNArule) {
|
|
582
699
|
this.addRule(NA_STRING, true, {
|
|
583
700
|
shapes: params.na_shapes || makeNAShapes(params.na_z || 1000),
|
|
584
701
|
legend_label: params.na_legend_label || NA_LABEL,
|
|
585
702
|
exclude_from_legend: false,
|
|
586
|
-
legend_config: {
|
|
587
|
-
legend_order: Number.POSITIVE_INFINITY
|
|
703
|
+
legend_config: { type: 'rule', target: { na: true } },
|
|
704
|
+
legend_order: Number.POSITIVE_INFINITY,
|
|
588
705
|
});
|
|
589
706
|
}
|
|
590
707
|
|
|
591
708
|
this.category_key = params.category_key;
|
|
592
709
|
this.universal_rule_categories = params.universal_rule_categories;
|
|
593
|
-
this.category_to_color = cloneShallow(
|
|
594
|
-
|
|
710
|
+
this.category_to_color = cloneShallow(
|
|
711
|
+
ifndef(params.category_to_color, {})
|
|
712
|
+
);
|
|
713
|
+
this.getUnusedColor = makeUniqueColorGetter(
|
|
714
|
+
objectValues(this.category_to_color).map(rgbaToHex)
|
|
715
|
+
);
|
|
595
716
|
for (const category of Object.keys(this.category_to_color)) {
|
|
596
717
|
const color = this.category_to_color[category];
|
|
597
718
|
this.addCategoryRule(category, color);
|
|
@@ -599,19 +720,24 @@ class CategoricalRuleSet extends LookupRuleSet {
|
|
|
599
720
|
}
|
|
600
721
|
}
|
|
601
722
|
|
|
602
|
-
private addCategoryRule(category:string, color:RGBAColor) {
|
|
603
|
-
const legend_rule_target:any = {};
|
|
723
|
+
private addCategoryRule(category: string, color: RGBAColor) {
|
|
724
|
+
const legend_rule_target: any = {};
|
|
604
725
|
legend_rule_target[this.category_key] = category;
|
|
605
|
-
const rule_params:RuleParams = {
|
|
606
|
-
shapes: [
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
726
|
+
const rule_params: RuleParams = {
|
|
727
|
+
shapes: [
|
|
728
|
+
{
|
|
729
|
+
type: 'rectangle',
|
|
730
|
+
fill: color,
|
|
731
|
+
},
|
|
732
|
+
],
|
|
610
733
|
legend_label: category,
|
|
611
734
|
exclude_from_legend: false,
|
|
612
|
-
legend_config: {
|
|
735
|
+
legend_config: { type: 'rule', target: legend_rule_target },
|
|
613
736
|
};
|
|
614
|
-
if (
|
|
737
|
+
if (
|
|
738
|
+
this.universal_rule_categories &&
|
|
739
|
+
this.universal_rule_categories.hasOwnProperty(category)
|
|
740
|
+
) {
|
|
615
741
|
// add universal rule
|
|
616
742
|
this.addRule(null, category, rule_params);
|
|
617
743
|
} else {
|
|
@@ -619,7 +745,14 @@ class CategoricalRuleSet extends LookupRuleSet {
|
|
|
619
745
|
}
|
|
620
746
|
}
|
|
621
747
|
|
|
622
|
-
public getSpecificShapesForDatum(
|
|
748
|
+
public getSpecificShapesForDatum(
|
|
749
|
+
data: Datum,
|
|
750
|
+
cell_width: number,
|
|
751
|
+
cell_height: number,
|
|
752
|
+
out_active_rules: ActiveRules | undefined,
|
|
753
|
+
data_id_key: string & keyof Datum,
|
|
754
|
+
important_ids?: ColumnProp<boolean>
|
|
755
|
+
) {
|
|
623
756
|
// First ensure there is a color for all categories
|
|
624
757
|
for (let i = 0, data_len = data.length; i < data_len; i++) {
|
|
625
758
|
if (data[i][NA_STRING]) {
|
|
@@ -634,45 +767,56 @@ class CategoricalRuleSet extends LookupRuleSet {
|
|
|
634
767
|
}
|
|
635
768
|
}
|
|
636
769
|
// Then propagate the call up
|
|
637
|
-
return super.getSpecificShapesForDatum(
|
|
770
|
+
return super.getSpecificShapesForDatum(
|
|
771
|
+
data,
|
|
772
|
+
cell_width,
|
|
773
|
+
cell_height,
|
|
774
|
+
out_active_rules,
|
|
775
|
+
data_id_key,
|
|
776
|
+
important_ids
|
|
777
|
+
);
|
|
638
778
|
}
|
|
639
779
|
}
|
|
640
780
|
|
|
641
781
|
export enum LinearInterpRangeType {
|
|
642
|
-
ALL = 'ALL',
|
|
782
|
+
ALL = 'ALL', // all values positive, negative and zero
|
|
643
783
|
NON_NEGATIVE = 'NON_NEGATIVE', // value range all positive values inclusive zero (0)
|
|
644
|
-
NON_POSITIVE = 'NON_POSITIVE'
|
|
784
|
+
NON_POSITIVE = 'NON_POSITIVE', // value range all negative values inclusive zero (0)
|
|
645
785
|
}
|
|
646
786
|
|
|
647
787
|
class LinearInterpRuleSet extends ConditionRuleSet {
|
|
648
|
-
|
|
649
|
-
protected
|
|
650
|
-
protected
|
|
651
|
-
protected
|
|
652
|
-
protected
|
|
653
|
-
protected
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
constructor(params:ILinearInterpRuleSetParams) {
|
|
788
|
+
protected value_key: string;
|
|
789
|
+
protected value_range: [number, number];
|
|
790
|
+
protected log_scale?: boolean;
|
|
791
|
+
protected type: string;
|
|
792
|
+
protected makeInterpFn: () => (valToConvert: number) => number;
|
|
793
|
+
protected inferred_value_range: [number, number];
|
|
794
|
+
|
|
795
|
+
constructor(params: ILinearInterpRuleSetParams) {
|
|
657
796
|
super(params);
|
|
658
797
|
this.value_key = params.value_key;
|
|
659
798
|
this.value_range = params.value_range;
|
|
660
799
|
this.log_scale = params.log_scale; // boolean
|
|
661
800
|
this.type = params.type;
|
|
662
801
|
|
|
663
|
-
this.makeInterpFn = function
|
|
802
|
+
this.makeInterpFn = function() {
|
|
664
803
|
const range = this.getEffectiveValueRange();
|
|
665
804
|
const rangeType = this.getValueRangeType();
|
|
666
805
|
const plotType = this.type;
|
|
667
806
|
if (this.log_scale) {
|
|
668
807
|
var shift_to_make_pos = Math.abs(range[0]) + 1;
|
|
669
|
-
var log_range =
|
|
808
|
+
var log_range =
|
|
809
|
+
Math.log(range[1] + shift_to_make_pos) -
|
|
810
|
+
Math.log(range[0] + shift_to_make_pos);
|
|
670
811
|
var log_range_lower = Math.log(range[0] + shift_to_make_pos);
|
|
671
|
-
return function
|
|
672
|
-
return (
|
|
812
|
+
return function(val: number) {
|
|
813
|
+
return (
|
|
814
|
+
(Math.log(val + shift_to_make_pos) - log_range_lower) /
|
|
815
|
+
log_range
|
|
816
|
+
);
|
|
673
817
|
};
|
|
674
818
|
} else {
|
|
675
|
-
return function
|
|
819
|
+
return function(val) {
|
|
676
820
|
var range_spread = range[1] - range[0],
|
|
677
821
|
range_lower = range[0],
|
|
678
822
|
range_higher = range[1];
|
|
@@ -680,27 +824,36 @@ class LinearInterpRuleSet extends ConditionRuleSet {
|
|
|
680
824
|
if (rangeType === LinearInterpRangeType.NON_POSITIVE) {
|
|
681
825
|
// when data only contains non positive values
|
|
682
826
|
return (val - range_higher) / range_spread;
|
|
683
|
-
} else if (
|
|
827
|
+
} else if (
|
|
828
|
+
rangeType === LinearInterpRangeType.NON_NEGATIVE
|
|
829
|
+
) {
|
|
684
830
|
// when data only contains non negative values
|
|
685
831
|
return (val - range_lower) / range_spread;
|
|
686
832
|
} else if (rangeType === LinearInterpRangeType.ALL) {
|
|
687
|
-
range_spread =
|
|
833
|
+
range_spread =
|
|
834
|
+
Math.abs(range[0]) > range[1]
|
|
835
|
+
? Math.abs(range[0])
|
|
836
|
+
: range[1];
|
|
688
837
|
return val / range_spread;
|
|
689
838
|
}
|
|
690
839
|
} else {
|
|
691
840
|
return (val - range_lower) / range_spread;
|
|
692
841
|
}
|
|
842
|
+
return undefined;
|
|
693
843
|
};
|
|
694
844
|
}
|
|
695
845
|
};
|
|
696
846
|
}
|
|
697
847
|
|
|
698
|
-
protected getEffectiveValueRange():[number,number] {
|
|
699
|
-
const ret = (this.value_range && this.value_range.slice()) || [
|
|
700
|
-
|
|
848
|
+
protected getEffectiveValueRange(): [number, number] {
|
|
849
|
+
const ret = (this.value_range && this.value_range.slice()) || [
|
|
850
|
+
undefined,
|
|
851
|
+
undefined,
|
|
852
|
+
];
|
|
853
|
+
if (typeof ret[0] === 'undefined') {
|
|
701
854
|
ret[0] = this.inferred_value_range[0];
|
|
702
855
|
}
|
|
703
|
-
if (typeof ret[1] ===
|
|
856
|
+
if (typeof ret[1] === 'undefined') {
|
|
704
857
|
ret[1] = this.inferred_value_range[1];
|
|
705
858
|
}
|
|
706
859
|
if (ret[0] === ret[1]) {
|
|
@@ -708,11 +861,11 @@ class LinearInterpRuleSet extends ConditionRuleSet {
|
|
|
708
861
|
ret[0] -= ret[0] / 2;
|
|
709
862
|
ret[1] += ret[1] / 2;
|
|
710
863
|
}
|
|
711
|
-
return ret as [number,number];
|
|
864
|
+
return ret as [number, number];
|
|
712
865
|
}
|
|
713
866
|
protected getValueRangeType() {
|
|
714
867
|
var range = this.getEffectiveValueRange();
|
|
715
|
-
if (range[0] < 0 && range[1] <=0) {
|
|
868
|
+
if (range[0] < 0 && range[1] <= 0) {
|
|
716
869
|
return LinearInterpRangeType.NON_POSITIVE;
|
|
717
870
|
} else if (range[0] >= 0 && range[1] > 0) {
|
|
718
871
|
return LinearInterpRangeType.NON_NEGATIVE;
|
|
@@ -721,7 +874,14 @@ class LinearInterpRuleSet extends ConditionRuleSet {
|
|
|
721
874
|
}
|
|
722
875
|
}
|
|
723
876
|
|
|
724
|
-
public getSpecificShapesForDatum(
|
|
877
|
+
public getSpecificShapesForDatum(
|
|
878
|
+
data: Datum,
|
|
879
|
+
cell_width: number,
|
|
880
|
+
cell_height: number,
|
|
881
|
+
out_active_rules: ActiveRules | undefined,
|
|
882
|
+
data_id_key: string & keyof Datum,
|
|
883
|
+
important_ids?: ColumnProp<boolean>
|
|
884
|
+
) {
|
|
725
885
|
// First find value range
|
|
726
886
|
let value_min = Number.POSITIVE_INFINITY;
|
|
727
887
|
let value_max = Number.NEGATIVE_INFINITY;
|
|
@@ -743,21 +903,28 @@ class LinearInterpRuleSet extends ConditionRuleSet {
|
|
|
743
903
|
this.updateLinearRules();
|
|
744
904
|
|
|
745
905
|
// Then propagate the call up
|
|
746
|
-
return super.getSpecificShapesForDatum(
|
|
906
|
+
return super.getSpecificShapesForDatum(
|
|
907
|
+
data,
|
|
908
|
+
cell_width,
|
|
909
|
+
cell_height,
|
|
910
|
+
out_active_rules,
|
|
911
|
+
data_id_key,
|
|
912
|
+
important_ids
|
|
913
|
+
);
|
|
747
914
|
}
|
|
748
915
|
|
|
749
916
|
protected updateLinearRules() {
|
|
750
|
-
throw
|
|
917
|
+
throw 'Not implemented in abstract class';
|
|
751
918
|
}
|
|
752
919
|
}
|
|
753
920
|
|
|
754
921
|
class GradientRuleSet extends LinearInterpRuleSet {
|
|
755
|
-
private colors:RGBAColor[] = [];
|
|
922
|
+
private colors: RGBAColor[] = [];
|
|
756
923
|
private value_stop_points: number[];
|
|
757
|
-
private null_color?:RGBAColor;
|
|
758
|
-
private gradient_rule:RuleId;
|
|
924
|
+
private null_color?: RGBAColor;
|
|
925
|
+
private gradient_rule: RuleId;
|
|
759
926
|
|
|
760
|
-
constructor(params:Omit<IGradientRuleSetParams,
|
|
927
|
+
constructor(params: Omit<IGradientRuleSetParams, 'type'>) {
|
|
761
928
|
super(params);
|
|
762
929
|
if (params.colors) {
|
|
763
930
|
this.colors = params.colors || [];
|
|
@@ -765,68 +932,98 @@ class GradientRuleSet extends LinearInterpRuleSet {
|
|
|
765
932
|
this.colors = heatmapColors[params.colormap_name] || [];
|
|
766
933
|
}
|
|
767
934
|
if (this.colors.length === 0) {
|
|
768
|
-
this.colors.push([0,0,0,1],[255,0,0,1]);
|
|
935
|
+
this.colors.push([0, 0, 0, 1], [255, 0, 0, 1]);
|
|
769
936
|
}
|
|
770
937
|
|
|
771
938
|
this.value_stop_points = params.value_stop_points;
|
|
772
|
-
this.null_color = params.null_color || [211,211,211,1];
|
|
939
|
+
this.null_color = params.null_color || [211, 211, 211, 1];
|
|
773
940
|
|
|
774
941
|
var self = this;
|
|
775
942
|
var value_key = this.value_key;
|
|
776
|
-
this.addRule(
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
943
|
+
this.addRule(
|
|
944
|
+
function(d) {
|
|
945
|
+
return d[NA_STRING] !== true && d[value_key] === null;
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
shapes: [
|
|
949
|
+
{
|
|
950
|
+
type: 'rectangle',
|
|
951
|
+
fill: self.null_color,
|
|
952
|
+
},
|
|
953
|
+
],
|
|
954
|
+
legend_label: params.null_legend_label || 'Not a number',
|
|
955
|
+
exclude_from_legend: false,
|
|
956
|
+
legend_config: { type: 'rule', target: { [value_key]: null } },
|
|
957
|
+
}
|
|
958
|
+
);
|
|
787
959
|
}
|
|
788
960
|
|
|
789
|
-
static linInterpColors(
|
|
961
|
+
static linInterpColors(
|
|
962
|
+
t: number,
|
|
963
|
+
begin_color: RGBAColor,
|
|
964
|
+
end_color: RGBAColor
|
|
965
|
+
): RGBAColor {
|
|
790
966
|
// 0 <= t <= 1
|
|
791
967
|
return [
|
|
792
|
-
Math.round(begin_color[0]*(1-t) + end_color[0]*t),
|
|
793
|
-
Math.round(begin_color[1]*(1-t) + end_color[1]*t),
|
|
794
|
-
Math.round(begin_color[2]*(1-t) + end_color[2]*t),
|
|
795
|
-
begin_color[3]*(1-t) + end_color[3]*t
|
|
968
|
+
Math.round(begin_color[0] * (1 - t) + end_color[0] * t),
|
|
969
|
+
Math.round(begin_color[1] * (1 - t) + end_color[1] * t),
|
|
970
|
+
Math.round(begin_color[2] * (1 - t) + end_color[2] * t),
|
|
971
|
+
begin_color[3] * (1 - t) + end_color[3] * t,
|
|
796
972
|
];
|
|
797
973
|
}
|
|
798
974
|
|
|
799
|
-
private makeColorFn(
|
|
975
|
+
private makeColorFn(
|
|
976
|
+
colors: RGBAColor[],
|
|
977
|
+
interpFn: (valToConvert: number) => number
|
|
978
|
+
) {
|
|
800
979
|
const value_stop_points = this.value_stop_points;
|
|
801
|
-
let stop_points:number[];
|
|
980
|
+
let stop_points: number[];
|
|
802
981
|
if (value_stop_points) {
|
|
803
982
|
stop_points = value_stop_points.map(interpFn);
|
|
804
983
|
} else {
|
|
805
|
-
stop_points = intRange(colors.length).map(function(x) {
|
|
984
|
+
stop_points = intRange(colors.length).map(function(x) {
|
|
985
|
+
return x / (colors.length - 1);
|
|
986
|
+
});
|
|
806
987
|
}
|
|
807
|
-
return function(t:number):RGBAColor {
|
|
988
|
+
return function(t: number): RGBAColor {
|
|
808
989
|
// 0 <= t <= 1
|
|
809
|
-
var begin_interval_index = binarysearch(
|
|
990
|
+
var begin_interval_index = binarysearch(
|
|
991
|
+
stop_points,
|
|
992
|
+
t,
|
|
993
|
+
function(x) {
|
|
994
|
+
return x;
|
|
995
|
+
},
|
|
996
|
+
true
|
|
997
|
+
);
|
|
810
998
|
if (begin_interval_index === -1) {
|
|
811
|
-
return [0,0,0,1];
|
|
999
|
+
return [0, 0, 0, 1];
|
|
812
1000
|
}
|
|
813
|
-
var end_interval_index = Math.min(
|
|
814
|
-
|
|
1001
|
+
var end_interval_index = Math.min(
|
|
1002
|
+
colors.length - 1,
|
|
1003
|
+
begin_interval_index + 1
|
|
1004
|
+
);
|
|
1005
|
+
var spread =
|
|
1006
|
+
stop_points[end_interval_index] -
|
|
1007
|
+
stop_points[begin_interval_index];
|
|
815
1008
|
if (spread === 0) {
|
|
816
1009
|
return colors[end_interval_index];
|
|
817
1010
|
} else {
|
|
818
|
-
var interval_t =
|
|
1011
|
+
var interval_t =
|
|
1012
|
+
(t - stop_points[begin_interval_index]) / spread;
|
|
819
1013
|
var begin_color = colors[begin_interval_index];
|
|
820
1014
|
var end_color = colors[end_interval_index];
|
|
821
|
-
return GradientRuleSet.linInterpColors(
|
|
1015
|
+
return GradientRuleSet.linInterpColors(
|
|
1016
|
+
interval_t,
|
|
1017
|
+
begin_color,
|
|
1018
|
+
end_color
|
|
1019
|
+
);
|
|
822
1020
|
}
|
|
823
|
-
|
|
824
1021
|
};
|
|
825
1022
|
}
|
|
826
1023
|
|
|
827
1024
|
protected updateLinearRules() {
|
|
828
1025
|
let rule_id;
|
|
829
|
-
if (typeof this.gradient_rule !==
|
|
1026
|
+
if (typeof this.gradient_rule !== 'undefined') {
|
|
830
1027
|
rule_id = this.gradient_rule;
|
|
831
1028
|
this.removeRule(this.gradient_rule);
|
|
832
1029
|
}
|
|
@@ -835,37 +1032,46 @@ class GradientRuleSet extends LinearInterpRuleSet {
|
|
|
835
1032
|
const value_key = this.value_key;
|
|
836
1033
|
const null_color = this.null_color;
|
|
837
1034
|
|
|
838
|
-
this.gradient_rule = this.addRule(
|
|
1035
|
+
this.gradient_rule = this.addRule(
|
|
1036
|
+
function(d) {
|
|
839
1037
|
return d[NA_STRING] !== true && d[value_key] !== null;
|
|
840
1038
|
},
|
|
841
|
-
{
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1039
|
+
{
|
|
1040
|
+
shapes: [
|
|
1041
|
+
{
|
|
1042
|
+
type: 'rectangle',
|
|
1043
|
+
fill: function(d) {
|
|
1044
|
+
var t = interpFn(d[value_key]);
|
|
1045
|
+
return colorFn(t) as RGBAColor;
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
],
|
|
848
1049
|
exclude_from_legend: false,
|
|
849
|
-
legend_config: {
|
|
1050
|
+
legend_config: {
|
|
1051
|
+
type: 'gradient' as 'gradient',
|
|
1052
|
+
range: this.getEffectiveValueRange(),
|
|
1053
|
+
colorFn: colorFn,
|
|
1054
|
+
},
|
|
850
1055
|
},
|
|
851
|
-
rule_id
|
|
1056
|
+
rule_id
|
|
1057
|
+
);
|
|
852
1058
|
}
|
|
853
1059
|
}
|
|
854
1060
|
|
|
855
1061
|
class BarRuleSet extends LinearInterpRuleSet {
|
|
856
|
-
private fill:RGBAColor;
|
|
857
|
-
private negative_fill:RGBAColor;
|
|
858
|
-
private bar_rule?:RuleId;
|
|
1062
|
+
private fill: RGBAColor;
|
|
1063
|
+
private negative_fill: RGBAColor;
|
|
1064
|
+
private bar_rule?: RuleId;
|
|
859
1065
|
|
|
860
|
-
constructor(params:IBarRuleSetParams) {
|
|
1066
|
+
constructor(params: IBarRuleSetParams) {
|
|
861
1067
|
super(params);
|
|
862
|
-
this.fill = params.fill || [0,128,0,1]; // green
|
|
863
|
-
this.negative_fill = params.negative_fill || [255,0,0,1]; //red
|
|
1068
|
+
this.fill = params.fill || [0, 128, 0, 1]; // green
|
|
1069
|
+
this.negative_fill = params.negative_fill || [255, 0, 0, 1]; //red
|
|
864
1070
|
}
|
|
865
1071
|
|
|
866
1072
|
protected updateLinearRules() {
|
|
867
1073
|
let rule_id;
|
|
868
|
-
if (typeof this.bar_rule !==
|
|
1074
|
+
if (typeof this.bar_rule !== 'undefined') {
|
|
869
1075
|
rule_id = this.bar_rule;
|
|
870
1076
|
this.removeRule(this.bar_rule);
|
|
871
1077
|
}
|
|
@@ -875,47 +1081,60 @@ class BarRuleSet extends LinearInterpRuleSet {
|
|
|
875
1081
|
const negative_color = this.negative_fill;
|
|
876
1082
|
const yPosFn = this.getYPosPercentagesFn();
|
|
877
1083
|
const cellHeightFn = this.getCellHeightPercentagesFn();
|
|
878
|
-
this.bar_rule = this.addRule(
|
|
1084
|
+
this.bar_rule = this.addRule(
|
|
1085
|
+
function(d) {
|
|
879
1086
|
return d[NA_STRING] !== true;
|
|
880
1087
|
},
|
|
881
|
-
{
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1088
|
+
{
|
|
1089
|
+
shapes: [
|
|
1090
|
+
{
|
|
1091
|
+
type: 'rectangle',
|
|
1092
|
+
y: function(d) {
|
|
1093
|
+
var t = interpFn(d[value_key]);
|
|
1094
|
+
return yPosFn(t);
|
|
1095
|
+
},
|
|
1096
|
+
height: function(d) {
|
|
1097
|
+
var t = interpFn(d[value_key]);
|
|
1098
|
+
return cellHeightFn(t);
|
|
1099
|
+
},
|
|
1100
|
+
fill: function(d) {
|
|
1101
|
+
return d[value_key] < 0
|
|
1102
|
+
? negative_color
|
|
1103
|
+
: positive_color;
|
|
1104
|
+
},
|
|
890
1105
|
},
|
|
891
|
-
|
|
892
|
-
return d[value_key] < 0 ? negative_color : positive_color;
|
|
893
|
-
}
|
|
894
|
-
}],
|
|
1106
|
+
],
|
|
895
1107
|
exclude_from_legend: false,
|
|
896
1108
|
legend_config: {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
1109
|
+
type: 'number' as 'number',
|
|
1110
|
+
range: this.getEffectiveValueRange(),
|
|
1111
|
+
range_type: this.getValueRangeType(),
|
|
1112
|
+
positive_color: positive_color,
|
|
1113
|
+
negative_color: negative_color,
|
|
1114
|
+
interpFn: interpFn,
|
|
1115
|
+
},
|
|
904
1116
|
},
|
|
905
|
-
rule_id
|
|
1117
|
+
rule_id
|
|
1118
|
+
);
|
|
906
1119
|
}
|
|
907
1120
|
|
|
908
1121
|
public getYPosPercentagesFn() {
|
|
909
1122
|
let ret;
|
|
910
1123
|
switch (this.getValueRangeType()) {
|
|
911
1124
|
case LinearInterpRangeType.NON_POSITIVE:
|
|
912
|
-
ret =
|
|
1125
|
+
ret = function(t: number) {
|
|
1126
|
+
return 0;
|
|
1127
|
+
};
|
|
913
1128
|
break;
|
|
914
1129
|
case LinearInterpRangeType.NON_NEGATIVE:
|
|
915
|
-
ret =
|
|
1130
|
+
ret = function(t: number) {
|
|
1131
|
+
return (1 - t) * 100;
|
|
1132
|
+
};
|
|
916
1133
|
break;
|
|
917
1134
|
case LinearInterpRangeType.ALL:
|
|
918
|
-
ret =
|
|
1135
|
+
ret = function(t: number) {
|
|
1136
|
+
return Math.min(1 - t, 1) * 50;
|
|
1137
|
+
};
|
|
919
1138
|
break;
|
|
920
1139
|
}
|
|
921
1140
|
return ret;
|
|
@@ -925,13 +1144,19 @@ class BarRuleSet extends LinearInterpRuleSet {
|
|
|
925
1144
|
let ret;
|
|
926
1145
|
switch (this.getValueRangeType()) {
|
|
927
1146
|
case LinearInterpRangeType.NON_POSITIVE:
|
|
928
|
-
ret =
|
|
1147
|
+
ret = function(t: number) {
|
|
1148
|
+
return -t * 100;
|
|
1149
|
+
};
|
|
929
1150
|
break;
|
|
930
1151
|
case LinearInterpRangeType.NON_NEGATIVE:
|
|
931
|
-
ret =
|
|
1152
|
+
ret = function(t: number) {
|
|
1153
|
+
return t * 100;
|
|
1154
|
+
};
|
|
932
1155
|
break;
|
|
933
1156
|
case LinearInterpRangeType.ALL:
|
|
934
|
-
ret =
|
|
1157
|
+
ret = function(t: number) {
|
|
1158
|
+
return Math.abs(t) * 50;
|
|
1159
|
+
};
|
|
935
1160
|
break;
|
|
936
1161
|
}
|
|
937
1162
|
return ret;
|
|
@@ -939,7 +1164,7 @@ class BarRuleSet extends LinearInterpRuleSet {
|
|
|
939
1164
|
}
|
|
940
1165
|
|
|
941
1166
|
class StackedBarRuleSet extends ConditionRuleSet {
|
|
942
|
-
constructor(params:IStackedBarRuleSetParams) {
|
|
1167
|
+
constructor(params: IStackedBarRuleSetParams) {
|
|
943
1168
|
super(params);
|
|
944
1169
|
const value_key = params.value_key;
|
|
945
1170
|
const fills = params.fills || [];
|
|
@@ -952,91 +1177,143 @@ class StackedBarRuleSet extends ConditionRuleSet {
|
|
|
952
1177
|
}
|
|
953
1178
|
|
|
954
1179
|
const self = this;
|
|
955
|
-
for (let i=0; i < categories.length; i++) {
|
|
1180
|
+
for (let i = 0; i < categories.length; i++) {
|
|
956
1181
|
(function(I) {
|
|
957
|
-
const legend_target:any = {};
|
|
1182
|
+
const legend_target: any = {};
|
|
958
1183
|
legend_target[value_key] = {};
|
|
959
|
-
for (let j=0; j<categories.length; j++) {
|
|
1184
|
+
for (let j = 0; j < categories.length; j++) {
|
|
960
1185
|
legend_target[value_key][categories[j]] = 0;
|
|
961
1186
|
}
|
|
962
1187
|
legend_target[value_key][categories[I]] = 1;
|
|
963
|
-
self.addRule(
|
|
1188
|
+
self.addRule(
|
|
1189
|
+
function(d) {
|
|
964
1190
|
return d[NA_STRING] !== true;
|
|
965
1191
|
},
|
|
966
|
-
{
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
total
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
if (j < I) {
|
|
983
|
-
prev_vals_sum += new_val;
|
|
1192
|
+
{
|
|
1193
|
+
shapes: [
|
|
1194
|
+
{
|
|
1195
|
+
type: 'rectangle',
|
|
1196
|
+
fill: fills[I],
|
|
1197
|
+
width: 100,
|
|
1198
|
+
height: function(d) {
|
|
1199
|
+
var total = 0;
|
|
1200
|
+
for (
|
|
1201
|
+
var j = 0;
|
|
1202
|
+
j < categories.length;
|
|
1203
|
+
j++
|
|
1204
|
+
) {
|
|
1205
|
+
total += parseFloat(
|
|
1206
|
+
d[value_key][categories[j]]
|
|
1207
|
+
);
|
|
984
1208
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1209
|
+
return (
|
|
1210
|
+
(parseFloat(
|
|
1211
|
+
d[value_key][categories[I]]
|
|
1212
|
+
) *
|
|
1213
|
+
100) /
|
|
1214
|
+
total
|
|
1215
|
+
);
|
|
1216
|
+
},
|
|
1217
|
+
y: function(d) {
|
|
1218
|
+
var total = 0;
|
|
1219
|
+
var prev_vals_sum = 0;
|
|
1220
|
+
for (
|
|
1221
|
+
var j = 0;
|
|
1222
|
+
j < categories.length;
|
|
1223
|
+
j++
|
|
1224
|
+
) {
|
|
1225
|
+
var new_val = parseFloat(
|
|
1226
|
+
d[value_key][categories[j]]
|
|
1227
|
+
);
|
|
1228
|
+
if (j < I) {
|
|
1229
|
+
prev_vals_sum += new_val;
|
|
1230
|
+
}
|
|
1231
|
+
total += new_val;
|
|
1232
|
+
}
|
|
1233
|
+
return (prev_vals_sum * 100) / total;
|
|
1234
|
+
},
|
|
1235
|
+
},
|
|
1236
|
+
],
|
|
990
1237
|
exclude_from_legend: false,
|
|
991
|
-
legend_config: {
|
|
992
|
-
legend_label: categories[I]
|
|
1238
|
+
legend_config: { type: 'rule', target: legend_target },
|
|
1239
|
+
legend_label: categories[I],
|
|
1240
|
+
}
|
|
1241
|
+
);
|
|
993
1242
|
})(i);
|
|
994
1243
|
}
|
|
995
1244
|
}
|
|
996
1245
|
}
|
|
997
1246
|
|
|
998
1247
|
export class GeneticAlterationRuleSet extends LookupRuleSet {
|
|
999
|
-
constructor(params:IGeneticAlterationRuleSetParams) {
|
|
1248
|
+
constructor(params: IGeneticAlterationRuleSetParams) {
|
|
1000
1249
|
super(params);
|
|
1001
1250
|
this.addRulesFromParams(params);
|
|
1002
1251
|
this.addRule(NA_STRING, true, {
|
|
1003
1252
|
shapes: params.na_shapes || makeNAShapes(params.na_z || 1),
|
|
1004
1253
|
legend_label: params.na_legend_label || NA_LABEL,
|
|
1005
1254
|
exclude_from_legend: false,
|
|
1006
|
-
legend_config: {
|
|
1007
|
-
legend_order: Number.POSITIVE_INFINITY
|
|
1255
|
+
legend_config: { type: 'rule', target: { na: true } },
|
|
1256
|
+
legend_order: Number.POSITIVE_INFINITY,
|
|
1008
1257
|
});
|
|
1009
1258
|
}
|
|
1010
1259
|
|
|
1011
|
-
private addRulesFromParams(params:IGeneticAlterationRuleSetParams) {
|
|
1260
|
+
private addRulesFromParams(params: IGeneticAlterationRuleSetParams) {
|
|
1012
1261
|
const rule_params = params.rule_params;
|
|
1013
|
-
_.forEach(
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1262
|
+
_.forEach(
|
|
1263
|
+
rule_params.conditional,
|
|
1264
|
+
(
|
|
1265
|
+
datumValuesToRuleParams: GeneticAlterationRuleParams['conditional']['datumKey'],
|
|
1266
|
+
datumKey: string
|
|
1267
|
+
) => {
|
|
1268
|
+
_.forEach(
|
|
1269
|
+
datumValuesToRuleParams,
|
|
1270
|
+
(
|
|
1271
|
+
ruleParams: GeneticAlterationSingleRuleParams,
|
|
1272
|
+
commaSeparatedDatumValues: string
|
|
1273
|
+
) => {
|
|
1274
|
+
const equiv_values = commaSeparatedDatumValues.split(
|
|
1275
|
+
','
|
|
1276
|
+
);
|
|
1277
|
+
const legend_rule_target: any = {};
|
|
1278
|
+
legend_rule_target[
|
|
1279
|
+
equiv_values[0]
|
|
1280
|
+
] = commaSeparatedDatumValues;
|
|
1281
|
+
const rule_id = this.addRule(
|
|
1282
|
+
datumKey,
|
|
1283
|
+
equiv_values[0] === '*' ? null : equiv_values[0],
|
|
1284
|
+
shallowExtend(ruleParams, {
|
|
1285
|
+
shapes: ruleParams.shapes,
|
|
1286
|
+
legend_config: {
|
|
1287
|
+
type: 'rule' as 'rule',
|
|
1288
|
+
target: legend_rule_target,
|
|
1289
|
+
},
|
|
1290
|
+
legend_base_color: ifndef(
|
|
1291
|
+
this.legend_base_color,
|
|
1292
|
+
[255, 255, 255, 1]
|
|
1293
|
+
) as [number, number, number, number],
|
|
1294
|
+
})
|
|
1295
|
+
);
|
|
1296
|
+
for (let i = 1; i < equiv_values.length; i++) {
|
|
1297
|
+
this.linkExistingRule(
|
|
1298
|
+
datumKey,
|
|
1299
|
+
equiv_values[i] === '*'
|
|
1300
|
+
? null
|
|
1301
|
+
: equiv_values[i],
|
|
1302
|
+
rule_id
|
|
1303
|
+
);
|
|
1026
1304
|
}
|
|
1027
|
-
|
|
1305
|
+
}
|
|
1028
1306
|
);
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
}
|
|
1032
|
-
});
|
|
1033
|
-
});
|
|
1307
|
+
}
|
|
1308
|
+
);
|
|
1034
1309
|
|
|
1035
1310
|
if (rule_params.always) {
|
|
1036
|
-
this.addRule(
|
|
1311
|
+
this.addRule(
|
|
1312
|
+
null,
|
|
1313
|
+
null,
|
|
1037
1314
|
shallowExtend(rule_params.always, {
|
|
1038
|
-
shapes:rule_params.always.shapes,
|
|
1039
|
-
legend_config: {
|
|
1315
|
+
shapes: rule_params.always.shapes,
|
|
1316
|
+
legend_config: { type: 'rule' as 'rule', target: {} },
|
|
1040
1317
|
})
|
|
1041
1318
|
);
|
|
1042
1319
|
}
|
|
@@ -1044,15 +1321,15 @@ export class GeneticAlterationRuleSet extends LookupRuleSet {
|
|
|
1044
1321
|
}
|
|
1045
1322
|
|
|
1046
1323
|
export class Rule {
|
|
1047
|
-
private shapes:Shape[];
|
|
1048
|
-
public legend_label:string;
|
|
1049
|
-
public legend_base_color?:RGBAColor;
|
|
1050
|
-
public exclude_from_legend?:boolean;
|
|
1051
|
-
private legend_config?:RuleLegendConfig;
|
|
1052
|
-
public legend_order?:number;
|
|
1053
|
-
|
|
1054
|
-
constructor(params:RuleParams) {
|
|
1055
|
-
this.shapes = params.shapes.map(function
|
|
1324
|
+
private shapes: Shape[];
|
|
1325
|
+
public legend_label: string;
|
|
1326
|
+
public legend_base_color?: RGBAColor;
|
|
1327
|
+
public exclude_from_legend?: boolean;
|
|
1328
|
+
private legend_config?: RuleLegendConfig;
|
|
1329
|
+
public legend_order?: number;
|
|
1330
|
+
|
|
1331
|
+
constructor(params: RuleParams) {
|
|
1332
|
+
this.shapes = params.shapes.map(function(shape) {
|
|
1056
1333
|
if (shape.type === 'rectangle') {
|
|
1057
1334
|
return new Rectangle(shape);
|
|
1058
1335
|
} else if (shape.type === 'triangle') {
|
|
@@ -1062,8 +1339,12 @@ export class Rule {
|
|
|
1062
1339
|
} else if (shape.type === 'line') {
|
|
1063
1340
|
return new Line(shape);
|
|
1064
1341
|
}
|
|
1342
|
+
return undefined;
|
|
1065
1343
|
});
|
|
1066
|
-
this.legend_label =
|
|
1344
|
+
this.legend_label =
|
|
1345
|
+
typeof params.legend_label === 'undefined'
|
|
1346
|
+
? ''
|
|
1347
|
+
: params.legend_label;
|
|
1067
1348
|
this.legend_base_color = params.legend_base_color;
|
|
1068
1349
|
this.exclude_from_legend = params.exclude_from_legend;
|
|
1069
1350
|
this.legend_config = params.legend_config;
|
|
@@ -1073,12 +1354,14 @@ export class Rule {
|
|
|
1073
1354
|
return this.legend_config;
|
|
1074
1355
|
}
|
|
1075
1356
|
|
|
1076
|
-
public apply(d:Datum, cell_width:number, cell_height:number) {
|
|
1357
|
+
public apply(d: Datum, cell_width: number, cell_height: number) {
|
|
1077
1358
|
// Gets concrete shapes (i.e. computed
|
|
1078
1359
|
// real values from percentages)
|
|
1079
1360
|
const concrete_shapes = [];
|
|
1080
1361
|
for (let i = 0, shapes_len = this.shapes.length; i < shapes_len; i++) {
|
|
1081
|
-
concrete_shapes.push(
|
|
1362
|
+
concrete_shapes.push(
|
|
1363
|
+
this.shapes[i].getComputedParams(d, cell_width, cell_height)
|
|
1364
|
+
);
|
|
1082
1365
|
}
|
|
1083
1366
|
return concrete_shapes;
|
|
1084
1367
|
}
|
|
@@ -1089,9 +1372,9 @@ export class Rule {
|
|
|
1089
1372
|
}
|
|
1090
1373
|
|
|
1091
1374
|
class GradientCategoricalRuleSet extends RuleSet {
|
|
1092
|
-
private gradientRuleSet:GradientRuleSet;
|
|
1093
|
-
private categoricalRuleSet:CategoricalRuleSet;
|
|
1094
|
-
constructor(params:IGradientAndCategoricalRuleSetParams) {
|
|
1375
|
+
private gradientRuleSet: GradientRuleSet;
|
|
1376
|
+
private categoricalRuleSet: CategoricalRuleSet;
|
|
1377
|
+
constructor(params: IGradientAndCategoricalRuleSetParams) {
|
|
1095
1378
|
super(params);
|
|
1096
1379
|
// For the GradientCategoricalRuleSet a datum must always have a
|
|
1097
1380
|
// value and may have a category attribute. A datum is 'NA'
|
|
@@ -1103,40 +1386,68 @@ class GradientCategoricalRuleSet extends RuleSet {
|
|
|
1103
1386
|
}
|
|
1104
1387
|
|
|
1105
1388
|
// RuleSet API
|
|
1106
|
-
public getSpecificShapesForDatum(
|
|
1107
|
-
|
|
1389
|
+
public getSpecificShapesForDatum(
|
|
1390
|
+
data: Datum,
|
|
1391
|
+
cell_width: number,
|
|
1392
|
+
cell_height: number,
|
|
1393
|
+
out_active_rules: ActiveRules | undefined,
|
|
1394
|
+
data_id_key: string & keyof Datum,
|
|
1395
|
+
important_ids?: ColumnProp<boolean>
|
|
1396
|
+
) {
|
|
1108
1397
|
const shapes = [];
|
|
1109
1398
|
// check the type of datum (categorical or continuous) and delegate
|
|
1110
1399
|
// fetching of shapes to the appropriate RuleSet class
|
|
1111
1400
|
for (let i = 0; i < data.length; i++) {
|
|
1112
1401
|
const datum = data[i];
|
|
1113
|
-
if (
|
|
1114
|
-
shapes.push(
|
|
1402
|
+
if (this.isCategorical(datum)) {
|
|
1403
|
+
shapes.push(
|
|
1404
|
+
this.categoricalRuleSet.getSpecificShapesForDatum(
|
|
1405
|
+
[datum],
|
|
1406
|
+
cell_width,
|
|
1407
|
+
cell_height,
|
|
1408
|
+
out_active_rules,
|
|
1409
|
+
data_id_key,
|
|
1410
|
+
important_ids
|
|
1411
|
+
)[0]
|
|
1412
|
+
);
|
|
1115
1413
|
} else {
|
|
1116
|
-
shapes.push(
|
|
1414
|
+
shapes.push(
|
|
1415
|
+
this.gradientRuleSet.getSpecificShapesForDatum(
|
|
1416
|
+
[datum],
|
|
1417
|
+
cell_width,
|
|
1418
|
+
cell_height,
|
|
1419
|
+
out_active_rules,
|
|
1420
|
+
data_id_key,
|
|
1421
|
+
important_ids
|
|
1422
|
+
)[0]
|
|
1423
|
+
);
|
|
1117
1424
|
}
|
|
1118
1425
|
}
|
|
1119
1426
|
return shapes;
|
|
1120
1427
|
}
|
|
1121
1428
|
|
|
1122
1429
|
// RuleSet API
|
|
1123
|
-
public getSpecificRulesForDatum(datum?:Datum) {
|
|
1124
|
-
const categoricalRules = this.categoricalRuleSet.getSpecificRulesForDatum(
|
|
1125
|
-
|
|
1430
|
+
public getSpecificRulesForDatum(datum?: Datum) {
|
|
1431
|
+
const categoricalRules = this.categoricalRuleSet.getSpecificRulesForDatum(
|
|
1432
|
+
datum
|
|
1433
|
+
);
|
|
1434
|
+
const gradientRules = this.gradientRuleSet.getSpecificRulesForDatum(
|
|
1435
|
+
datum
|
|
1436
|
+
);
|
|
1126
1437
|
const rules = categoricalRules.concat(gradientRules);
|
|
1127
1438
|
return rules;
|
|
1128
1439
|
}
|
|
1129
1440
|
|
|
1130
1441
|
// helper function
|
|
1131
|
-
public isCategorical(datum:Datum) {
|
|
1442
|
+
public isCategorical(datum: Datum) {
|
|
1132
1443
|
// A categorical value is recognized by presence of a category attribute.
|
|
1133
1444
|
// Note: a categorical datum still requires a continuous value (used for clustering).
|
|
1134
1445
|
return datum[this.categoricalRuleSet.category_key] !== undefined;
|
|
1135
1446
|
}
|
|
1136
1447
|
}
|
|
1137
1448
|
|
|
1138
|
-
export default function
|
|
1139
|
-
let ret:RuleSet;
|
|
1449
|
+
export default function(params: RuleSetParams) {
|
|
1450
|
+
let ret: RuleSet;
|
|
1140
1451
|
switch (params.type) {
|
|
1141
1452
|
case RuleSetType.CATEGORICAL:
|
|
1142
1453
|
ret = new CategoricalRuleSet(params as ICategoricalRuleSetParams);
|
|
@@ -1145,7 +1456,9 @@ export default function (params:RuleSetParams) {
|
|
|
1145
1456
|
ret = new GradientRuleSet(params as IGradientRuleSetParams);
|
|
1146
1457
|
break;
|
|
1147
1458
|
case RuleSetType.GRADIENT_AND_CATEGORICAL:
|
|
1148
|
-
ret = new GradientCategoricalRuleSet(
|
|
1459
|
+
ret = new GradientCategoricalRuleSet(
|
|
1460
|
+
params as IGradientAndCategoricalRuleSetParams
|
|
1461
|
+
);
|
|
1149
1462
|
break;
|
|
1150
1463
|
case RuleSetType.BAR:
|
|
1151
1464
|
ret = new BarRuleSet(params as IBarRuleSetParams);
|
|
@@ -1155,8 +1468,10 @@ export default function (params:RuleSetParams) {
|
|
|
1155
1468
|
break;
|
|
1156
1469
|
case RuleSetType.GENE:
|
|
1157
1470
|
default:
|
|
1158
|
-
ret = new GeneticAlterationRuleSet(
|
|
1471
|
+
ret = new GeneticAlterationRuleSet(
|
|
1472
|
+
params as IGeneticAlterationRuleSetParams
|
|
1473
|
+
);
|
|
1159
1474
|
break;
|
|
1160
1475
|
}
|
|
1161
1476
|
return ret;
|
|
1162
|
-
}
|
|
1477
|
+
}
|