@genome-spy/core 0.43.0 → 0.43.2
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/dist/bundle/{index-d7k3kkin.js → index-noY1e-G6.js} +365 -361
- package/dist/bundle/index.es.js +3442 -3338
- package/dist/bundle/index.js +91 -79
- package/dist/src/data/sources/lazy/bigBedSource.d.ts +6 -0
- package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigBedSource.js +149 -6
- package/dist/src/data/transforms/coverage.d.ts +10 -2
- package/dist/src/data/transforms/coverage.d.ts.map +1 -1
- package/dist/src/data/transforms/coverage.js +22 -9
- package/dist/src/data/transforms/coverage.test.js +202 -87
- package/dist/src/gl/arrayBuilder.d.ts +11 -4
- package/dist/src/gl/arrayBuilder.d.ts.map +1 -1
- package/dist/src/gl/arrayBuilder.js +36 -13
- package/dist/src/gl/dataToVertices.d.ts +3 -2
- package/dist/src/gl/dataToVertices.d.ts.map +1 -1
- package/dist/src/gl/dataToVertices.js +45 -14
- package/dist/src/gl/glslScaleGenerator.d.ts +12 -0
- package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
- package/dist/src/gl/glslScaleGenerator.js +38 -4
- package/dist/src/gl/includes/scales.glsl.js +1 -1
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +2 -1
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +3 -1
- package/dist/src/utils/kWayMerge.d.ts +3 -2
- package/dist/src/utils/kWayMerge.d.ts.map +1 -1
- package/dist/src/utils/kWayMerge.js +4 -3
- package/dist/src/utils/kWayMerge.test.js +5 -1
- package/package.json +3 -3
|
@@ -8,6 +8,12 @@ export default class BigBedSource extends SingleAxisWindowedSource {
|
|
|
8
8
|
parser: import("@gmod/bed").default;
|
|
9
9
|
/** @type {import("@gmod/bbi").BigBed} */
|
|
10
10
|
bbi: import("@gmod/bbi").BigBed;
|
|
11
|
+
/** @type {(chrom: string, fields: { start: number, end: number, rest?: string }) => Record<string, any>} */
|
|
12
|
+
parseLine: (chrom: string, fields: {
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
rest?: string;
|
|
16
|
+
}) => Record<string, any>;
|
|
11
17
|
params: import("../../../spec/data.js").BigBedData;
|
|
12
18
|
initializedPromise: Promise<any>;
|
|
13
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bigBedSource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/lazy/bigBedSource.js"],"names":[],"mappings":"AAGA;
|
|
1
|
+
{"version":3,"file":"bigBedSource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/lazy/bigBedSource.js"],"names":[],"mappings":"AAGA;IAUI;;;OAGG;IACH,oBAHW,OAAO,uBAAuB,EAAE,UAAU,QAC1C,OAAO,uBAAuB,EAAE,OAAO,EAsDjD;IAjED,0CAA0C;IAC1C,QADW,OAAO,WAAW,EAAE,OAAO,CAC/B;IAEP,yCAAyC;IACzC,KADW,OAAO,WAAW,EAAE,MAAM,CACjC;IAEJ,4GAA4G;IAC5G,mBADmB,MAAM,UAAU;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,MAAM,EAAE,GAAG,CAAC,CAC9F;IAkBN,mDAAgC;IAQhC,iCA+BE;CAuBT;qCA1FoC,+BAA+B"}
|
|
@@ -8,6 +8,9 @@ export default class BigBedSource extends SingleAxisWindowedSource {
|
|
|
8
8
|
/** @type {import("@gmod/bbi").BigBed} */
|
|
9
9
|
bbi;
|
|
10
10
|
|
|
11
|
+
/** @type {(chrom: string, fields: { start: number, end: number, rest?: string }) => Record<string, any>} */
|
|
12
|
+
parseLine;
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* @param {import("../../../spec/data.js").BigBedData} params
|
|
13
16
|
* @param {import("../../../view/view.js").default} view
|
|
@@ -49,6 +52,16 @@ export default class BigBedSource extends SingleAxisWindowedSource {
|
|
|
49
52
|
this.bbi.getHeader().then(async (header) => {
|
|
50
53
|
// @ts-ignore TODO: Fix
|
|
51
54
|
this.parser = new BED({ autoSql: header.autoSql });
|
|
55
|
+
try {
|
|
56
|
+
const fastParser = makeFastParser(this.parser);
|
|
57
|
+
this.parseLine = (chrom, f) =>
|
|
58
|
+
fastParser(chrom, f.start, f.end, f.rest);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
this.parseLine = (chrom, f) =>
|
|
61
|
+
this.parser.parseLine(
|
|
62
|
+
`${chrom}\t${f.start}\t${f.end}\t${f.rest}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
52
65
|
|
|
53
66
|
resolve();
|
|
54
67
|
});
|
|
@@ -68,12 +81,7 @@ export default class BigBedSource extends SingleAxisWindowedSource {
|
|
|
68
81
|
signal,
|
|
69
82
|
})
|
|
70
83
|
.then((features) =>
|
|
71
|
-
features.map((f) =>
|
|
72
|
-
this.parser.parseLine(
|
|
73
|
-
`${d.chrom}\t${f.start}\t${f.end}\t${f.rest}`,
|
|
74
|
-
{ uniqueId: f.uniqueId }
|
|
75
|
-
)
|
|
76
|
-
)
|
|
84
|
+
features.map((f) => this.parseLine(d.chrom, f))
|
|
77
85
|
)
|
|
78
86
|
);
|
|
79
87
|
|
|
@@ -82,3 +90,138 @@ export default class BigBedSource extends SingleAxisWindowedSource {
|
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
92
|
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A specific optimization for Hautaniemi Lab's Methylation project, where
|
|
96
|
+
* we have hundreds of columns having small integers (0-100).
|
|
97
|
+
* This parser avoids generating piles of garbage to be collected by the GC.
|
|
98
|
+
* We don't split the line into an array of strings, but instead parse the
|
|
99
|
+
* integer fields directly from the original string.
|
|
100
|
+
* This parser doesn't support arrays, etc. at the moment. This could, however,
|
|
101
|
+
* be extended into a fully-featured parser.
|
|
102
|
+
*
|
|
103
|
+
* @param {import("@gmod/bed").default} bed
|
|
104
|
+
*/
|
|
105
|
+
function makeFastParser(bed) {
|
|
106
|
+
// Skip the first three fields: chrom, chromStart, chromEnd
|
|
107
|
+
const fields = bed.autoSql.fields.filter((field) => field.type).slice(3);
|
|
108
|
+
|
|
109
|
+
let i = 0;
|
|
110
|
+
let currentLine = "";
|
|
111
|
+
let lineLength = 0;
|
|
112
|
+
|
|
113
|
+
/** @type {Record<string, any>} */
|
|
114
|
+
let currentObject = {};
|
|
115
|
+
|
|
116
|
+
const delimiter = "\t";
|
|
117
|
+
const delimiterCode = delimiter.charCodeAt(0);
|
|
118
|
+
const zero = "0".charCodeAt(0);
|
|
119
|
+
const minusCode = "-".charCodeAt(0);
|
|
120
|
+
|
|
121
|
+
function parseString() {
|
|
122
|
+
let end = currentLine.indexOf(delimiter, i);
|
|
123
|
+
if (end < 0) {
|
|
124
|
+
end = lineLength;
|
|
125
|
+
}
|
|
126
|
+
const str = currentLine.substring(i, end);
|
|
127
|
+
i = end + 1;
|
|
128
|
+
return str;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseInt() {
|
|
132
|
+
let value = 0;
|
|
133
|
+
|
|
134
|
+
let charCode = currentLine.charCodeAt(i);
|
|
135
|
+
let sign = 1;
|
|
136
|
+
|
|
137
|
+
if (charCode === minusCode) {
|
|
138
|
+
sign = -1;
|
|
139
|
+
i++;
|
|
140
|
+
charCode = currentLine.charCodeAt(i);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
do {
|
|
144
|
+
if (charCode === delimiterCode) {
|
|
145
|
+
i++;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
value = value * 10 + charCode - zero;
|
|
149
|
+
charCode = currentLine.charCodeAt(++i);
|
|
150
|
+
} while (i < lineLength);
|
|
151
|
+
|
|
152
|
+
return value * sign;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const fieldParsers = fields.map((field) => {
|
|
156
|
+
const { name, type } = field;
|
|
157
|
+
|
|
158
|
+
if (["ubyte", "int", "uint"].includes(type)) {
|
|
159
|
+
return () => {
|
|
160
|
+
currentObject[name] = parseInt();
|
|
161
|
+
};
|
|
162
|
+
} else if (field.isNumeric) {
|
|
163
|
+
return () => {
|
|
164
|
+
currentObject[name] = Number(parseString());
|
|
165
|
+
};
|
|
166
|
+
} else if (["char", "string", "lstring"].includes(type)) {
|
|
167
|
+
return () => {
|
|
168
|
+
currentObject[name] = parseString();
|
|
169
|
+
};
|
|
170
|
+
} else {
|
|
171
|
+
throw new Error("Unsupported type: " + type);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const templateFields = fields.map(
|
|
176
|
+
(field) => `"${field.name}": ${field.isNumeric ? "0" : "emptyString"}`
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Make a template object with all fields to avoid the JavaScript VM's
|
|
181
|
+
* hidden class to be changed after each property assignment. Transitions
|
|
182
|
+
* between hidden classes generate plenty of garbage to be collected.
|
|
183
|
+
*
|
|
184
|
+
* Ideally, the parsed values would be assigned directly in this function,
|
|
185
|
+
* but for some reason, it results in abysmally slow performance on Chrome,
|
|
186
|
+
* but not on Firefox, where it would be super fast.
|
|
187
|
+
*/
|
|
188
|
+
const makeTemplate = new Function(`
|
|
189
|
+
const emptyString = "";
|
|
190
|
+
return function makeTemplate(chrom, chromStart, chromEnd) {
|
|
191
|
+
return {
|
|
192
|
+
chrom,
|
|
193
|
+
chromStart,
|
|
194
|
+
chromEnd,
|
|
195
|
+
${templateFields.join(",\n")}
|
|
196
|
+
}
|
|
197
|
+
};`)();
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @param {string} line
|
|
201
|
+
*/
|
|
202
|
+
function setLine(line) {
|
|
203
|
+
currentLine = line;
|
|
204
|
+
lineLength = line.length;
|
|
205
|
+
i = 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @param {string} chrom
|
|
210
|
+
* @param {number} chromStart
|
|
211
|
+
* @param {number} chromEnd
|
|
212
|
+
* @param {string} rest
|
|
213
|
+
*/
|
|
214
|
+
function parseLine(chrom, chromStart, chromEnd, rest) {
|
|
215
|
+
setLine(rest);
|
|
216
|
+
|
|
217
|
+
currentObject = makeTemplate(chrom, chromStart, chromEnd);
|
|
218
|
+
|
|
219
|
+
for (let j = 0, n = fieldParsers.length; j < n; j++) {
|
|
220
|
+
fieldParsers[j]();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return currentObject;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return parseLine;
|
|
227
|
+
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Computes coverage for sorted segments
|
|
4
4
|
*
|
|
5
|
-
* TODO: Binned coverage
|
|
5
|
+
* TODO: Binned coverage, e.g., don't emit a new segment for every
|
|
6
|
+
* coverage change, but only every n bases or so. The most straightforward
|
|
7
|
+
* way to implement it is a separate transform that bins the coverage
|
|
8
|
+
* segments and calculates weighted averages.
|
|
6
9
|
*/
|
|
7
10
|
export default class CoverageTransform extends FlowNode {
|
|
8
11
|
/**
|
|
@@ -23,7 +26,12 @@ export default class CoverageTransform extends FlowNode {
|
|
|
23
26
|
chrom: string;
|
|
24
27
|
};
|
|
25
28
|
createSegment: Function;
|
|
26
|
-
|
|
29
|
+
/**
|
|
30
|
+
* End pos as priority, weight as value
|
|
31
|
+
*
|
|
32
|
+
* @type {FlatQueue<number>}
|
|
33
|
+
*/
|
|
34
|
+
ends: FlatQueue<number>;
|
|
27
35
|
}
|
|
28
36
|
import FlowNode from "../flowNode.js";
|
|
29
37
|
import FlatQueue from "flatqueue";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/coverage.js"],"names":[],"mappings":";AAKA
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/coverage.js"],"names":[],"mappings":";AAKA;;;;;;;GAOG;AACH;IAKI;;OAEG;IACH,oBAFW,OAAO,yBAAyB,EAAE,cAAc,EAgD1D;IA5CG,yDAAoB;IAEpB,mDAAwC;IACxC,iDAAoC;IAEpC,mCAAmC;IACnC,sBADoB,GAAG,KAAE,MAAM,CAGT;IACtB,mCAAmC;IACnC,uBADoB,GAAG,KAAE,MAAM,CACsC;IAErE;;;;;MAKC;IAGD,wBAgBC;IAED;;;;OAIG;IACH,MAFU,UAAU,MAAM,CAAC,CAEA;CAiIlC;qBAhMyC,gBAAgB;sBAHpC,WAAW"}
|
|
@@ -6,7 +6,10 @@ import FlowNode, { BEHAVIOR_CLONES } from "../flowNode.js";
|
|
|
6
6
|
/**
|
|
7
7
|
* Computes coverage for sorted segments
|
|
8
8
|
*
|
|
9
|
-
* TODO: Binned coverage
|
|
9
|
+
* TODO: Binned coverage, e.g., don't emit a new segment for every
|
|
10
|
+
* coverage change, but only every n bases or so. The most straightforward
|
|
11
|
+
* way to implement it is a separate transform that bins the coverage
|
|
12
|
+
* segments and calculates weighted averages.
|
|
10
13
|
*/
|
|
11
14
|
export default class CoverageTransform extends FlowNode {
|
|
12
15
|
get behavior() {
|
|
@@ -56,7 +59,11 @@ export default class CoverageTransform extends FlowNode {
|
|
|
56
59
|
)
|
|
57
60
|
);
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
/**
|
|
63
|
+
* End pos as priority, weight as value
|
|
64
|
+
*
|
|
65
|
+
* @type {FlatQueue<number>}
|
|
66
|
+
*/
|
|
60
67
|
this.ends = new FlatQueue();
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -75,7 +82,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
75
82
|
const chromAccessor = this.chromAccessor;
|
|
76
83
|
const weightAccessor = this.weightAccessor;
|
|
77
84
|
|
|
78
|
-
/** @type {
|
|
85
|
+
/** @type {import("../flowNode.js").Datum} used for merging adjacent segment */
|
|
79
86
|
let bufferedSegment;
|
|
80
87
|
|
|
81
88
|
/** @type {string} */
|
|
@@ -91,7 +98,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
91
98
|
/** @type {number} */
|
|
92
99
|
let prevEdge;
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
/** End pos as priority, weight as value */
|
|
95
102
|
const ends = this.ends;
|
|
96
103
|
ends.clear();
|
|
97
104
|
|
|
@@ -127,9 +134,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
127
134
|
};
|
|
128
135
|
|
|
129
136
|
const flushQueue = () => {
|
|
130
|
-
|
|
131
|
-
/** @type {number} */
|
|
132
|
-
let edge;
|
|
137
|
+
let edge = 0;
|
|
133
138
|
while ((edge = ends.peekValue()) !== undefined) {
|
|
134
139
|
pushSegment(prevEdge, edge, coverage);
|
|
135
140
|
prevEdge = edge;
|
|
@@ -147,8 +152,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
147
152
|
this.handle = (datum) => {
|
|
148
153
|
const start = startAccessor(datum);
|
|
149
154
|
|
|
150
|
-
|
|
151
|
-
let edge;
|
|
155
|
+
let edge = 0;
|
|
152
156
|
while ((edge = ends.peekValue()) !== undefined && edge < start) {
|
|
153
157
|
pushSegment(prevEdge, edge, coverage);
|
|
154
158
|
prevEdge = edge;
|
|
@@ -179,5 +183,14 @@ export default class CoverageTransform extends FlowNode {
|
|
|
179
183
|
flushQueue();
|
|
180
184
|
super.complete();
|
|
181
185
|
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {import("../../types/flowBatch.js").FlowBatch} flowBatch
|
|
189
|
+
*/
|
|
190
|
+
this.beginBatch = (flowBatch) => {
|
|
191
|
+
flushQueue();
|
|
192
|
+
prevChrom = undefined;
|
|
193
|
+
super.beginBatch(flowBatch);
|
|
194
|
+
};
|
|
182
195
|
}
|
|
183
196
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
2
|
import CoverageTransform from "./coverage.js";
|
|
3
3
|
import { processData } from "../flowTestUtils.js";
|
|
4
4
|
|
|
@@ -16,108 +16,223 @@ function transform(params, data) {
|
|
|
16
16
|
return processData(t, data);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
[8, 10],
|
|
26
|
-
[11, 14],
|
|
27
|
-
[11, 13],
|
|
28
|
-
[11, 12],
|
|
29
|
-
[15, 18],
|
|
30
|
-
[16, 18],
|
|
31
|
-
[17, 18],
|
|
32
|
-
].map((d) => ({
|
|
33
|
-
start: d[0],
|
|
34
|
-
end: d[1],
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
const coverageSegments = [
|
|
38
|
-
[0, 1, 1],
|
|
39
|
-
[1, 2, 2],
|
|
40
|
-
[2, 3, 3],
|
|
41
|
-
[3, 6, 2],
|
|
42
|
-
[6, 10, 1],
|
|
43
|
-
[11, 12, 3],
|
|
44
|
-
[12, 13, 2],
|
|
45
|
-
[13, 14, 1],
|
|
46
|
-
[15, 16, 1],
|
|
47
|
-
[16, 17, 2],
|
|
48
|
-
[17, 18, 3],
|
|
49
|
-
].map((d) => ({
|
|
50
|
-
start: d[0],
|
|
51
|
-
end: d[1],
|
|
52
|
-
coverage: d[2],
|
|
53
|
-
}));
|
|
54
|
-
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param {[number, number][]} reads Start, end
|
|
22
|
+
* @param {[number, number, number][]} coverageSegments Start, end, coverage
|
|
23
|
+
*/
|
|
24
|
+
function testSimpleCoverage(reads, coverageSegments) {
|
|
55
25
|
/** @type {CoverageParams} */
|
|
56
26
|
const coverageConfig = {
|
|
57
27
|
type: "coverage",
|
|
58
28
|
start: "start",
|
|
59
29
|
end: "end",
|
|
60
30
|
};
|
|
61
|
-
expect(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
31
|
+
expect(
|
|
32
|
+
transform(
|
|
33
|
+
coverageConfig,
|
|
34
|
+
reads.map((d) => ({
|
|
35
|
+
start: d[0],
|
|
36
|
+
end: d[1],
|
|
37
|
+
}))
|
|
38
|
+
)
|
|
39
|
+
).toEqual(
|
|
40
|
+
coverageSegments.map((d) => ({
|
|
41
|
+
start: d[0],
|
|
42
|
+
end: d[1],
|
|
43
|
+
coverage: d[2],
|
|
44
|
+
}))
|
|
45
|
+
);
|
|
46
|
+
}
|
|
76
47
|
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {[number, number, number][]} reads Start, end, weight
|
|
51
|
+
* @param {[number, number, number][]} coverageSemgments Start, end, coverage
|
|
52
|
+
*/
|
|
53
|
+
function testWeightedCoverage(reads, coverageSegments) {
|
|
77
54
|
/** @type {CoverageParams} */
|
|
78
55
|
const coverageConfig = {
|
|
79
56
|
type: "coverage",
|
|
80
|
-
chrom: "chrom",
|
|
81
57
|
start: "start",
|
|
82
58
|
end: "end",
|
|
59
|
+
weight: "weight",
|
|
83
60
|
};
|
|
61
|
+
expect(
|
|
62
|
+
transform(
|
|
63
|
+
coverageConfig,
|
|
64
|
+
reads.map((d) => ({
|
|
65
|
+
start: d[0],
|
|
66
|
+
end: d[1],
|
|
67
|
+
weight: d[2],
|
|
68
|
+
}))
|
|
69
|
+
)
|
|
70
|
+
).toEqual(
|
|
71
|
+
coverageSegments.map((d) => ({
|
|
72
|
+
start: d[0],
|
|
73
|
+
end: d[1],
|
|
74
|
+
coverage: d[2],
|
|
75
|
+
}))
|
|
76
|
+
);
|
|
77
|
+
}
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
describe("Coverage transform", () => {
|
|
80
|
+
test("Typical case", () =>
|
|
81
|
+
testSimpleCoverage(
|
|
82
|
+
[
|
|
83
|
+
[0, 4],
|
|
84
|
+
[1, 3],
|
|
85
|
+
[2, 6],
|
|
86
|
+
[4, 8],
|
|
87
|
+
[8, 10],
|
|
88
|
+
[11, 14],
|
|
89
|
+
[11, 13],
|
|
90
|
+
[11, 12],
|
|
91
|
+
[15, 18],
|
|
92
|
+
[16, 18],
|
|
93
|
+
[17, 18],
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
[0, 1, 1],
|
|
97
|
+
[1, 2, 2],
|
|
98
|
+
[2, 3, 3],
|
|
99
|
+
[3, 6, 2],
|
|
100
|
+
[6, 10, 1],
|
|
101
|
+
[11, 12, 3],
|
|
102
|
+
[12, 13, 2],
|
|
103
|
+
[13, 14, 1],
|
|
104
|
+
[15, 16, 1],
|
|
105
|
+
[16, 17, 2],
|
|
106
|
+
[17, 18, 3],
|
|
107
|
+
]
|
|
108
|
+
));
|
|
87
109
|
|
|
88
|
-
test("
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
110
|
+
test("Multiple identical overlapping segments", () =>
|
|
111
|
+
testSimpleCoverage(
|
|
112
|
+
[
|
|
113
|
+
[1, 2],
|
|
114
|
+
[3, 4],
|
|
115
|
+
[3, 4],
|
|
116
|
+
[5, 6],
|
|
117
|
+
[5, 6],
|
|
118
|
+
[5, 6],
|
|
119
|
+
],
|
|
120
|
+
[
|
|
121
|
+
[1, 2, 1],
|
|
122
|
+
[3, 4, 2],
|
|
123
|
+
[5, 6, 3],
|
|
124
|
+
]
|
|
125
|
+
));
|
|
99
126
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
test("Adjacent segments with equal coverage are merged", () =>
|
|
128
|
+
testSimpleCoverage(
|
|
129
|
+
[
|
|
130
|
+
[1, 2],
|
|
131
|
+
[2, 3],
|
|
132
|
+
[3, 4],
|
|
133
|
+
[5, 6],
|
|
134
|
+
[6, 7],
|
|
135
|
+
[7, 8],
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
[1, 4, 1],
|
|
139
|
+
[5, 8, 1],
|
|
140
|
+
]
|
|
141
|
+
));
|
|
112
142
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
143
|
+
test("Chromosomes pass through", () => {
|
|
144
|
+
const reads = [
|
|
145
|
+
{ chrom: "chr1", start: 0, end: 1 },
|
|
146
|
+
{ chrom: "chr2", start: 0, end: 1 },
|
|
147
|
+
{ chrom: "chr3", start: 1, end: 3 },
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
const coverageSegments = [
|
|
151
|
+
{ chrom: "chr1", start: 0, end: 1, coverage: 1 },
|
|
152
|
+
{ chrom: "chr2", start: 0, end: 1, coverage: 1 },
|
|
153
|
+
{ chrom: "chr3", start: 1, end: 3, coverage: 1 },
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
/** @type {CoverageParams} */
|
|
157
|
+
const coverageConfig = {
|
|
158
|
+
type: "coverage",
|
|
159
|
+
chrom: "chrom",
|
|
160
|
+
start: "start",
|
|
161
|
+
end: "end",
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
expect(transform(coverageConfig, reads)).toEqual(coverageSegments);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("Typical weighted coverage", () =>
|
|
168
|
+
testWeightedCoverage(
|
|
169
|
+
[
|
|
170
|
+
[0, 4, 1],
|
|
171
|
+
[1, 3, 2],
|
|
172
|
+
[2, 6, 3],
|
|
173
|
+
[8, 10, -1],
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
[0, 1, 1],
|
|
177
|
+
[1, 2, 3],
|
|
178
|
+
[2, 3, 6],
|
|
179
|
+
[3, 4, 4],
|
|
180
|
+
[4, 6, 3],
|
|
181
|
+
[8, 10, -1],
|
|
182
|
+
]
|
|
183
|
+
));
|
|
184
|
+
|
|
185
|
+
test("Multiple weights at a single locus", () =>
|
|
186
|
+
testWeightedCoverage(
|
|
187
|
+
[
|
|
188
|
+
// -- Locus 1
|
|
189
|
+
[1, 2, 1],
|
|
190
|
+
[1, 2, 2],
|
|
191
|
+
[1, 2, 3],
|
|
192
|
+
[1, 2, 4],
|
|
193
|
+
[1, 2, 5],
|
|
194
|
+
// -- Locus 2
|
|
195
|
+
[2, 3, 2],
|
|
196
|
+
[2, 3, 3],
|
|
197
|
+
[2, 3, 4],
|
|
198
|
+
[2, 3, 5],
|
|
199
|
+
[2, 3, 6],
|
|
200
|
+
],
|
|
201
|
+
[
|
|
202
|
+
// -- Locus 1
|
|
203
|
+
[1, 2, 15],
|
|
204
|
+
// -- Locus 2
|
|
205
|
+
[2, 3, 20],
|
|
206
|
+
]
|
|
207
|
+
));
|
|
121
208
|
|
|
122
|
-
|
|
209
|
+
test("Adjacent segments with different weights produce separated segments", () =>
|
|
210
|
+
testWeightedCoverage(
|
|
211
|
+
[
|
|
212
|
+
// -- Cluster 1
|
|
213
|
+
[1, 2, 2],
|
|
214
|
+
[2, 3, 1],
|
|
215
|
+
[3, 4, 1],
|
|
216
|
+
// -- Cluster 2
|
|
217
|
+
[5, 6, 1],
|
|
218
|
+
[6, 7, 2],
|
|
219
|
+
[7, 8, 1],
|
|
220
|
+
// -- Cluster 3
|
|
221
|
+
[9, 10, 1],
|
|
222
|
+
[10, 11, 1],
|
|
223
|
+
[11, 12, 2],
|
|
224
|
+
],
|
|
225
|
+
[
|
|
226
|
+
// -- Cluster 1
|
|
227
|
+
[1, 2, 2],
|
|
228
|
+
[2, 4, 1],
|
|
229
|
+
// -- Cluster 2
|
|
230
|
+
[5, 6, 1],
|
|
231
|
+
[6, 7, 2],
|
|
232
|
+
[7, 8, 1],
|
|
233
|
+
// -- Cluster 3
|
|
234
|
+
[9, 11, 1],
|
|
235
|
+
[11, 12, 2],
|
|
236
|
+
]
|
|
237
|
+
));
|
|
123
238
|
});
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @prop {function(object):any} f The converter
|
|
6
6
|
* @prop {number[]} [arrayReference] An optimization for fp64 mainly
|
|
7
7
|
* @prop {number} [numComponents]
|
|
8
|
+
* @prop {typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array} [targetArrayType] Defaults to Float32Array
|
|
8
9
|
*/
|
|
9
10
|
export default class ArrayBuilder {
|
|
10
11
|
/**
|
|
@@ -13,10 +14,10 @@ export default class ArrayBuilder {
|
|
|
13
14
|
*/
|
|
14
15
|
constructor(size: number);
|
|
15
16
|
size: number;
|
|
16
|
-
/** @type {Object.<string, {data:
|
|
17
|
+
/** @type {Object.<string, {data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array, numComponents: number, divisor?: number}>} */
|
|
17
18
|
arrays: {
|
|
18
19
|
[x: string]: {
|
|
19
|
-
data:
|
|
20
|
+
data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array;
|
|
20
21
|
numComponents: number;
|
|
21
22
|
divisor?: number;
|
|
22
23
|
};
|
|
@@ -26,6 +27,7 @@ export default class ArrayBuilder {
|
|
|
26
27
|
/** @type {(function(any):void)[]} */
|
|
27
28
|
dataUpdaters: ((arg0: any) => void)[];
|
|
28
29
|
vertexCount: number;
|
|
30
|
+
configure(): void;
|
|
29
31
|
/**
|
|
30
32
|
*
|
|
31
33
|
* @param {string} attribute
|
|
@@ -36,13 +38,13 @@ export default class ArrayBuilder {
|
|
|
36
38
|
*
|
|
37
39
|
* @param {string} attributeName
|
|
38
40
|
* @param {number} numComponents
|
|
41
|
+
* @param {typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array} [targetArrayType]
|
|
39
42
|
* @param {number[]} [arrayReference]
|
|
40
43
|
* @return {function(number|number[])}
|
|
41
44
|
*/
|
|
42
|
-
createUpdater(attributeName: string, numComponents: number, arrayReference?: number[]): (arg0: number | number[]) => any;
|
|
45
|
+
createUpdater(attributeName: string, numComponents: number, targetArrayType?: typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array, arrayReference?: number[]): (arg0: number | number[]) => any;
|
|
43
46
|
pushAll(): void;
|
|
44
47
|
/**
|
|
45
|
-
*
|
|
46
48
|
* @param {object} datum
|
|
47
49
|
*/
|
|
48
50
|
updateFromDatum(datum: object): void;
|
|
@@ -51,6 +53,7 @@ export default class ArrayBuilder {
|
|
|
51
53
|
* @param {object} datum
|
|
52
54
|
*/
|
|
53
55
|
pushFromDatum(datum: object): void;
|
|
56
|
+
#private;
|
|
54
57
|
}
|
|
55
58
|
/**
|
|
56
59
|
* A function that extracts a raw attribute from a datum (optionally) converts
|
|
@@ -66,5 +69,9 @@ export type ConverterMetadata = {
|
|
|
66
69
|
*/
|
|
67
70
|
arrayReference?: number[];
|
|
68
71
|
numComponents?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Defaults to Float32Array
|
|
74
|
+
*/
|
|
75
|
+
targetArrayType?: typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array;
|
|
69
76
|
};
|
|
70
77
|
//# sourceMappingURL=arrayBuilder.d.ts.map
|