@genome-spy/core 0.30.0 → 0.30.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/index.es.js +16373 -0
- package/dist/index.js +43 -43
- package/package.json +10 -7
- package/src/data/collector.js +0 -183
- package/src/data/collector.test.js +0 -84
- package/src/data/dataFlow.js +0 -148
- package/src/data/dataFlow.test.js +0 -5
- package/src/data/facetNode.js +0 -17
- package/src/data/flow.test.js +0 -72
- package/src/data/flowBatch.d.ts +0 -40
- package/src/data/flowNode.js +0 -283
- package/src/data/flowNode.test.js +0 -50
- package/src/data/flowOptimizer.js +0 -123
- package/src/data/flowOptimizer.test.js +0 -193
- package/src/data/flowTestUtils.js +0 -63
- package/src/data/formats/fasta.js +0 -32
- package/src/data/formats/fasta.test.js +0 -27
- package/src/data/sources/dataSource.js +0 -22
- package/src/data/sources/dataSourceFactory.js +0 -24
- package/src/data/sources/dataUtils.js +0 -78
- package/src/data/sources/dynamicCallbackSource.js +0 -57
- package/src/data/sources/dynamicSource.js +0 -37
- package/src/data/sources/inlineSource.js +0 -67
- package/src/data/sources/inlineSource.test.js +0 -56
- package/src/data/sources/namedSource.js +0 -79
- package/src/data/sources/sequenceSource.js +0 -46
- package/src/data/sources/sequenceSource.test.js +0 -46
- package/src/data/sources/urlSource.js +0 -74
- package/src/data/transforms/aggregate.js +0 -70
- package/src/data/transforms/clone.js +0 -40
- package/src/data/transforms/clone.test.js +0 -11
- package/src/data/transforms/coverage.js +0 -187
- package/src/data/transforms/coverage.test.js +0 -123
- package/src/data/transforms/filter.js +0 -37
- package/src/data/transforms/filter.test.js +0 -18
- package/src/data/transforms/filterScoredLabels.js +0 -134
- package/src/data/transforms/flattenCompressedExons.js +0 -57
- package/src/data/transforms/flattenDelimited.js +0 -74
- package/src/data/transforms/flattenDelimited.test.js +0 -87
- package/src/data/transforms/flattenSequence.js +0 -39
- package/src/data/transforms/flattenSequence.test.js +0 -34
- package/src/data/transforms/formula.js +0 -39
- package/src/data/transforms/formula.test.js +0 -19
- package/src/data/transforms/identifier.js +0 -108
- package/src/data/transforms/identifier.test.js +0 -83
- package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
- package/src/data/transforms/measureText.js +0 -44
- package/src/data/transforms/pileup.js +0 -128
- package/src/data/transforms/pileup.test.js +0 -70
- package/src/data/transforms/project.js +0 -41
- package/src/data/transforms/project.test.js +0 -32
- package/src/data/transforms/regexExtract.js +0 -61
- package/src/data/transforms/regexExtract.test.js +0 -67
- package/src/data/transforms/regexFold.js +0 -141
- package/src/data/transforms/regexFold.test.js +0 -160
- package/src/data/transforms/sample.js +0 -101
- package/src/data/transforms/sample.test.js +0 -38
- package/src/data/transforms/stack.js +0 -137
- package/src/data/transforms/stack.test.js +0 -91
- package/src/data/transforms/transformFactory.js +0 -60
- package/src/embedApi.d.ts +0 -67
- package/src/encoder/accessor.js +0 -82
- package/src/encoder/accessor.test.js +0 -47
- package/src/encoder/encoder.js +0 -394
- package/src/encoder/encoder.test.js +0 -98
- package/src/fonts/Lato-Regular.json +0 -1267
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +0 -93
- package/src/fonts/README.md +0 -3
- package/src/fonts/bmFont.d.ts +0 -58
- package/src/fonts/bmFontManager.js +0 -357
- package/src/fonts/bmFontMetrics.js +0 -108
- package/src/genome/genome.js +0 -317
- package/src/genome/genome.test.js +0 -188
- package/src/genome/genomeStore.js +0 -54
- package/src/genome/locusFormat.js +0 -31
- package/src/genome/scaleIndex.d.ts +0 -38
- package/src/genome/scaleIndex.js +0 -166
- package/src/genome/scaleIndex.test.js +0 -78
- package/src/genome/scaleLocus.d.ts +0 -11
- package/src/genome/scaleLocus.js +0 -108
- package/src/genome/scaleLocus.test.js +0 -4
- package/src/genomeSpy.js +0 -785
- package/src/gl/arrayBuilder.js +0 -199
- package/src/gl/dataToVertices.js +0 -636
- package/src/gl/includes/common.glsl +0 -63
- package/src/gl/includes/picking.fragment.glsl +0 -1
- package/src/gl/includes/picking.vertex.glsl +0 -27
- package/src/gl/includes/sampleFacet.glsl +0 -107
- package/src/gl/includes/scales.glsl +0 -112
- package/src/gl/link.fragment.glsl +0 -18
- package/src/gl/link.vertex.glsl +0 -111
- package/src/gl/point.fragment.glsl +0 -123
- package/src/gl/point.vertex.glsl +0 -129
- package/src/gl/rect.fragment.glsl +0 -51
- package/src/gl/rect.vertex.glsl +0 -114
- package/src/gl/rule.fragment.glsl +0 -52
- package/src/gl/rule.vertex.glsl +0 -89
- package/src/gl/text.fragment.glsl +0 -31
- package/src/gl/text.vertex.glsl +0 -246
- package/src/gl/webGLHelper.js +0 -504
- package/src/img/bowtie.svg +0 -1
- package/src/img/genomespy-favicon.svg +0 -34
- package/src/index.html +0 -11
- package/src/index.js +0 -128
- package/src/marks/link.js +0 -175
- package/src/marks/mark.js +0 -975
- package/src/marks/markUtils.js +0 -125
- package/src/marks/pointMark.js +0 -251
- package/src/marks/rectMark.js +0 -241
- package/src/marks/rule.js +0 -250
- package/src/marks/text.js +0 -278
- package/src/node_modules/.vitest/results.json +0 -1
- package/src/scale/colorUtils.js +0 -184
- package/src/scale/glslScaleGenerator.js +0 -502
- package/src/scale/scale.js +0 -451
- package/src/scale/scale.test.js +0 -324
- package/src/scale/ticks.js +0 -203
- package/src/scale/ticks.test.js +0 -40
- package/src/singlePageApp.js +0 -13
- package/src/spec/axis.d.ts +0 -296
- package/src/spec/channel.d.ts +0 -430
- package/src/spec/data.d.ts +0 -196
- package/src/spec/font.d.ts +0 -15
- package/src/spec/genome.d.ts +0 -35
- package/src/spec/mark.d.ts +0 -429
- package/src/spec/root.d.ts +0 -17
- package/src/spec/sampleView.d.ts +0 -180
- package/src/spec/scale.d.ts +0 -273
- package/src/spec/title.d.ts +0 -102
- package/src/spec/tooltip.d.ts +0 -9
- package/src/spec/transform.d.ts +0 -479
- package/src/spec/view.d.ts +0 -201
- package/src/styles/genome-spy.scss +0 -153
- package/src/tooltip/dataTooltipHandler.js +0 -64
- package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
- package/src/tooltip/tooltipHandler.ts +0 -12
- package/src/types/filetypes.d.ts +0 -14
- package/src/types/flatqueue.d.ts +0 -53
- package/src/types/glsl.d.ts +0 -4
- package/src/types/internmap.d.ts +0 -22
- package/src/types/object.d.ts +0 -21
- package/src/types/vega-loader.d.ts +0 -1
- package/src/types/vega-scale.d.ts +0 -60
- package/src/utils/addBaseUrl.js +0 -19
- package/src/utils/addBaseUrl.test.js +0 -22
- package/src/utils/animator.js +0 -83
- package/src/utils/arrayUtils.js +0 -61
- package/src/utils/binnedIndex.js +0 -167
- package/src/utils/binnedIndex.test.js +0 -155
- package/src/utils/clamp.js +0 -8
- package/src/utils/cloner.js +0 -34
- package/src/utils/cloner.test.js +0 -24
- package/src/utils/coalesce.js +0 -11
- package/src/utils/coalesce.test.js +0 -16
- package/src/utils/concatIterables.js +0 -26
- package/src/utils/concatIterables.test.js +0 -8
- package/src/utils/debounce.js +0 -37
- package/src/utils/domainArray.js +0 -216
- package/src/utils/domainArray.test.js +0 -130
- package/src/utils/eerp.js +0 -13
- package/src/utils/expression.js +0 -32
- package/src/utils/field.js +0 -28
- package/src/utils/formatObject.js +0 -31
- package/src/utils/indexer.js +0 -43
- package/src/utils/indexer.test.js +0 -47
- package/src/utils/inertia.js +0 -124
- package/src/utils/interactionEvent.js +0 -33
- package/src/utils/iterateNestedMaps.js +0 -21
- package/src/utils/iterateNestedMaps.test.js +0 -33
- package/src/utils/kWayMerge.js +0 -42
- package/src/utils/kWayMerge.test.js +0 -26
- package/src/utils/layout/flexLayout.js +0 -368
- package/src/utils/layout/flexLayout.test.js +0 -311
- package/src/utils/layout/grid.js +0 -95
- package/src/utils/layout/grid.test.js +0 -71
- package/src/utils/layout/padding.js +0 -120
- package/src/utils/layout/point.js +0 -23
- package/src/utils/layout/rectangle.js +0 -288
- package/src/utils/layout/rectangle.test.js +0 -172
- package/src/utils/mergeObjects.js +0 -99
- package/src/utils/mergeObjects.test.js +0 -42
- package/src/utils/numberExtractor.js +0 -24
- package/src/utils/numberExtractor.test.js +0 -6
- package/src/utils/point.js +0 -14
- package/src/utils/propertyCacher.js +0 -70
- package/src/utils/propertyCacher.test.js +0 -85
- package/src/utils/propertyCoalescer.js +0 -42
- package/src/utils/propertyCoalescer.test.js +0 -22
- package/src/utils/reservationMap.js +0 -103
- package/src/utils/reservationMap.test.js +0 -20
- package/src/utils/scaleNull.js +0 -19
- package/src/utils/setOperations.js +0 -75
- package/src/utils/smoothstep.js +0 -10
- package/src/utils/throttle.js +0 -34
- package/src/utils/topK.js +0 -76
- package/src/utils/topK.test.js +0 -64
- package/src/utils/transition.js +0 -74
- package/src/utils/ui/tooltip.js +0 -189
- package/src/utils/url.js +0 -22
- package/src/utils/variableTools.js +0 -24
- package/src/utils/variableTools.test.js +0 -13
- package/src/view/axisResolution.js +0 -140
- package/src/view/axisResolution.test.js +0 -201
- package/src/view/axisView.js +0 -747
- package/src/view/concatView.js +0 -45
- package/src/view/containerView.js +0 -159
- package/src/view/facetView.js +0 -491
- package/src/view/flowBuilder.js +0 -367
- package/src/view/flowBuilder.test.js +0 -125
- package/src/view/gridView.js +0 -786
- package/src/view/implicitRootView.js +0 -14
- package/src/view/importView.js +0 -19
- package/src/view/layerView.js +0 -74
- package/src/view/rendering.d.ts +0 -44
- package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
- package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
- package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
- package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
- package/src/view/renderingContext/viewRenderingContext.js +0 -41
- package/src/view/scaleResolution.js +0 -797
- package/src/view/scaleResolution.test.js +0 -572
- package/src/view/scaleResolutionApi.d.ts +0 -40
- package/src/view/testUtils.js +0 -51
- package/src/view/title.js +0 -165
- package/src/view/unitView.js +0 -382
- package/src/view/view.js +0 -612
- package/src/view/view.test.js +0 -214
- package/src/view/viewContext.d.ts +0 -62
- package/src/view/viewFactory.js +0 -181
- package/src/view/viewFactory.test.js +0 -17
- package/src/view/viewUtils.js +0 -327
- package/src/view/zoom.js +0 -89
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { group as d3group } from "d3-array";
|
|
2
|
-
import FlowNode, { BEHAVIOR_CLONES } from "../flowNode";
|
|
3
|
-
import { field } from "../../utils/field";
|
|
4
|
-
import iterateNestedMaps from "../../utils/iterateNestedMaps";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A minimal aggregate transform that just counts grouped (by a single field) data items.
|
|
8
|
-
* Work in progress.
|
|
9
|
-
*
|
|
10
|
-
* Eventually this will implement the most of Vega's aggregate transform:
|
|
11
|
-
* https://vega.github.io/vega/docs/transforms/aggregate/
|
|
12
|
-
*
|
|
13
|
-
* @typedef {import("../../spec/transform").AggregateParams} AggregateParams
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export default class AggregateTransform extends FlowNode {
|
|
17
|
-
get behavior() {
|
|
18
|
-
return BEHAVIOR_CLONES;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @param {AggregateParams} params
|
|
23
|
-
*/
|
|
24
|
-
constructor(params) {
|
|
25
|
-
super();
|
|
26
|
-
this.params = params;
|
|
27
|
-
|
|
28
|
-
/** @type {any[]} */
|
|
29
|
-
this.buffer = [];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
reset() {
|
|
33
|
-
this.buffer = [];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
*
|
|
38
|
-
* @param {import("../flowNode").Datum} datum
|
|
39
|
-
*/
|
|
40
|
-
handle(datum) {
|
|
41
|
-
this.buffer.push(datum);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
complete() {
|
|
45
|
-
const params = this.params;
|
|
46
|
-
|
|
47
|
-
const groupby = params.groupby;
|
|
48
|
-
|
|
49
|
-
const groupFieldAccessors = groupby.map((f) => field(f));
|
|
50
|
-
|
|
51
|
-
// TODO: Fix case where no group fields are specified
|
|
52
|
-
// @ts-expect-error
|
|
53
|
-
const groups = d3group(this.buffer, ...groupFieldAccessors);
|
|
54
|
-
|
|
55
|
-
for (const [group, data] of iterateNestedMaps(groups)) {
|
|
56
|
-
/** @type {any} */
|
|
57
|
-
const datum = {
|
|
58
|
-
count: data.length,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
for (let i = 0; i < groupby.length; i++) {
|
|
62
|
-
datum[groupby[i]] = group[i];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
this._propagate(datum);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
super.complete();
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import createCloner from "../../utils/cloner";
|
|
2
|
-
import FlowNode, { BEHAVIOR_CLONES, isFileBatch } from "../flowNode";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Clones the data objects that pass through.
|
|
6
|
-
*/
|
|
7
|
-
export default class CloneTransform extends FlowNode {
|
|
8
|
-
get behavior() {
|
|
9
|
-
return BEHAVIOR_CLONES;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
super();
|
|
14
|
-
|
|
15
|
-
/** @param {any} datum */
|
|
16
|
-
const setupCloner = (datum) => {
|
|
17
|
-
const clone = createCloner(datum);
|
|
18
|
-
|
|
19
|
-
/** @param {any} datum */
|
|
20
|
-
this.handle = (datum) => this._propagate(clone(datum));
|
|
21
|
-
|
|
22
|
-
this.handle(datum);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
this.handle = setupCloner;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Signals that a new batch of data will be propagated.
|
|
29
|
-
*
|
|
30
|
-
* @param {import("../flowNode").FlowBatch} [flowBatch]
|
|
31
|
-
*/
|
|
32
|
-
this.beginBatch = (flowBatch) => {
|
|
33
|
-
if (isFileBatch(flowBatch)) {
|
|
34
|
-
// TODO: Only create new cloner if the props change
|
|
35
|
-
this.handle = setupCloner;
|
|
36
|
-
}
|
|
37
|
-
super.beginBatch(flowBatch);
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
2
|
-
import { processData } from "../flowTestUtils";
|
|
3
|
-
import CloneTransform from "./clone";
|
|
4
|
-
|
|
5
|
-
test("CloneTransform clones the data objects", () => {
|
|
6
|
-
const data = [{ x: 1 }, { x: 2 }];
|
|
7
|
-
const clonedData = processData(new CloneTransform(), data);
|
|
8
|
-
|
|
9
|
-
expect(clonedData).toEqual(data);
|
|
10
|
-
expect(clonedData[0]).not.toBe(data[0]);
|
|
11
|
-
});
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import FlatQueue from "flatqueue";
|
|
2
|
-
|
|
3
|
-
import { field } from "../../utils/field";
|
|
4
|
-
import FlowNode, { BEHAVIOR_CLONES } from "../flowNode";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {import("../../spec/transform").CoverageParams} CoverageParams
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Computes coverage for sorted segments
|
|
12
|
-
*
|
|
13
|
-
* TODO: Binned coverage
|
|
14
|
-
*/
|
|
15
|
-
export default class CoverageTransform extends FlowNode {
|
|
16
|
-
get behavior() {
|
|
17
|
-
return BEHAVIOR_CLONES;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @param {CoverageParams} params
|
|
22
|
-
*/
|
|
23
|
-
constructor(params) {
|
|
24
|
-
super();
|
|
25
|
-
this.params = params;
|
|
26
|
-
|
|
27
|
-
this.startAccessor = field(params.start);
|
|
28
|
-
this.endAccessor = field(params.end);
|
|
29
|
-
|
|
30
|
-
/** @type {function(any):string} */
|
|
31
|
-
this.chromAccessor = params.chrom
|
|
32
|
-
? field(params.chrom)
|
|
33
|
-
: (d) => undefined;
|
|
34
|
-
/** @type {function(any):number} */
|
|
35
|
-
this.weightAccessor = params.weight ? field(params.weight) : (d) => 1;
|
|
36
|
-
|
|
37
|
-
this.as = {
|
|
38
|
-
coverage: params.as || "coverage",
|
|
39
|
-
start: params.asStart || params.start,
|
|
40
|
-
end: params.asEnd || params.end,
|
|
41
|
-
chrom: params.asChrom || params.chrom,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// eslint-disable-next-line no-new-func
|
|
45
|
-
this.createSegment = /** @type {function} */ (
|
|
46
|
-
new Function(
|
|
47
|
-
"start",
|
|
48
|
-
"end",
|
|
49
|
-
"coverage",
|
|
50
|
-
"chrom",
|
|
51
|
-
"return {" +
|
|
52
|
-
Object.entries(this.as)
|
|
53
|
-
.filter(([param, prop]) => prop)
|
|
54
|
-
.map(
|
|
55
|
-
([param, prop]) =>
|
|
56
|
-
`${JSON.stringify(prop)}: ${param}`
|
|
57
|
-
)
|
|
58
|
-
.join(", ") +
|
|
59
|
-
"};"
|
|
60
|
-
)
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// End pos as priority, weight as value
|
|
64
|
-
this.ends = new FlatQueue();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
reset() {
|
|
68
|
-
super.reset();
|
|
69
|
-
this.initialize();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
initialize() {
|
|
73
|
-
const asCoverage = this.as.coverage;
|
|
74
|
-
const asEnd = this.as.end;
|
|
75
|
-
const asChrom = this.as.chrom;
|
|
76
|
-
|
|
77
|
-
const startAccessor = this.startAccessor;
|
|
78
|
-
const endAccessor = this.endAccessor;
|
|
79
|
-
const chromAccessor = this.chromAccessor;
|
|
80
|
-
const weightAccessor = this.weightAccessor;
|
|
81
|
-
|
|
82
|
-
/** @type {Record<string, number|string>} used for merging adjacent segment */
|
|
83
|
-
let bufferedSegment;
|
|
84
|
-
|
|
85
|
-
/** @type {string} */
|
|
86
|
-
let prevChrom;
|
|
87
|
-
|
|
88
|
-
/** @type {string} */
|
|
89
|
-
let chrom;
|
|
90
|
-
|
|
91
|
-
// TODO: Whattabout cumulative error when float weights are used?
|
|
92
|
-
// Howabout https://github.com/d3/d3-array#fsum ?
|
|
93
|
-
let coverage = 0;
|
|
94
|
-
|
|
95
|
-
/** @type {number} */
|
|
96
|
-
let prevEdge;
|
|
97
|
-
|
|
98
|
-
// End pos as priority, weight as value
|
|
99
|
-
const ends = this.ends;
|
|
100
|
-
ends.clear();
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @param {number} start
|
|
104
|
-
* @param {number} end
|
|
105
|
-
* @param {number} coverage
|
|
106
|
-
*/
|
|
107
|
-
const pushSegment = (start, end, coverage) => {
|
|
108
|
-
if (start == end) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let extended = false;
|
|
113
|
-
if (bufferedSegment) {
|
|
114
|
-
if (bufferedSegment[asCoverage] === coverage) {
|
|
115
|
-
// Extend it
|
|
116
|
-
bufferedSegment[asEnd] = end;
|
|
117
|
-
extended = true;
|
|
118
|
-
} else if (bufferedSegment[asCoverage] != 0) {
|
|
119
|
-
this._propagate(bufferedSegment);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (!extended) {
|
|
124
|
-
bufferedSegment = this.createSegment(
|
|
125
|
-
start,
|
|
126
|
-
end,
|
|
127
|
-
coverage,
|
|
128
|
-
chrom
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const flushQueue = () => {
|
|
134
|
-
// Flush queue
|
|
135
|
-
/** @type {number} */
|
|
136
|
-
let edge;
|
|
137
|
-
while ((edge = ends.peekValue()) !== undefined) {
|
|
138
|
-
pushSegment(prevEdge, edge, coverage);
|
|
139
|
-
prevEdge = edge;
|
|
140
|
-
coverage -= ends.pop();
|
|
141
|
-
}
|
|
142
|
-
prevEdge = undefined;
|
|
143
|
-
|
|
144
|
-
if (bufferedSegment) {
|
|
145
|
-
this._propagate(bufferedSegment);
|
|
146
|
-
bufferedSegment = undefined;
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
/** @param {Record<string, any>} datum */
|
|
151
|
-
this.handle = (datum) => {
|
|
152
|
-
const start = startAccessor(datum);
|
|
153
|
-
|
|
154
|
-
/** @type {number} */
|
|
155
|
-
let edge;
|
|
156
|
-
while ((edge = ends.peekValue()) !== undefined && edge < start) {
|
|
157
|
-
pushSegment(prevEdge, edge, coverage);
|
|
158
|
-
prevEdge = edge;
|
|
159
|
-
coverage -= ends.pop();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (asChrom) {
|
|
163
|
-
let newChrom = chromAccessor(datum);
|
|
164
|
-
if (newChrom !== prevChrom) {
|
|
165
|
-
flushQueue();
|
|
166
|
-
chrom = newChrom;
|
|
167
|
-
prevChrom = chrom;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (prevEdge !== undefined) {
|
|
172
|
-
pushSegment(prevEdge, start, coverage);
|
|
173
|
-
}
|
|
174
|
-
prevEdge = start;
|
|
175
|
-
|
|
176
|
-
const weight = weightAccessor(datum);
|
|
177
|
-
coverage += weight;
|
|
178
|
-
|
|
179
|
-
ends.push(weight, endAccessor(datum));
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
this.complete = () => {
|
|
183
|
-
flushQueue();
|
|
184
|
-
super.complete();
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
2
|
-
import CoverageTransform from "./coverage";
|
|
3
|
-
import { processData } from "../flowTestUtils";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {import("../../spec/transform").CoverageParams} CoverageParams
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {CoverageParams} params
|
|
11
|
-
* @param {any[]} data
|
|
12
|
-
*/
|
|
13
|
-
function transform(params, data) {
|
|
14
|
-
const t = new CoverageTransform(params);
|
|
15
|
-
t.initialize();
|
|
16
|
-
return processData(t, data);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
test("Coverage transform produces correct coverage segments", () => {
|
|
20
|
-
const reads = [
|
|
21
|
-
[0, 4],
|
|
22
|
-
[1, 3],
|
|
23
|
-
[2, 6],
|
|
24
|
-
[4, 8],
|
|
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
|
-
|
|
55
|
-
/** @type {CoverageParams} */
|
|
56
|
-
const coverageConfig = {
|
|
57
|
-
type: "coverage",
|
|
58
|
-
start: "start",
|
|
59
|
-
end: "end",
|
|
60
|
-
};
|
|
61
|
-
expect(transform(coverageConfig, reads)).toEqual(coverageSegments);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("Coverage transform handles chromosomes", () => {
|
|
65
|
-
const reads = [
|
|
66
|
-
{ chrom: "chr1", start: 0, end: 1 },
|
|
67
|
-
{ chrom: "chr2", start: 0, end: 1 },
|
|
68
|
-
{ chrom: "chr3", start: 1, end: 3 },
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
const coverageSegments = [
|
|
72
|
-
{ chrom: "chr1", start: 0, end: 1, coverage: 1 },
|
|
73
|
-
{ chrom: "chr2", start: 0, end: 1, coverage: 1 },
|
|
74
|
-
{ chrom: "chr3", start: 1, end: 3, coverage: 1 },
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
/** @type {CoverageParams} */
|
|
78
|
-
const coverageConfig = {
|
|
79
|
-
type: "coverage",
|
|
80
|
-
chrom: "chrom",
|
|
81
|
-
start: "start",
|
|
82
|
-
end: "end",
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
expect(transform(coverageConfig, reads)).toEqual(coverageSegments);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("Coverage transform handles weights", () => {
|
|
89
|
-
const reads = [
|
|
90
|
-
[0, 4, 1],
|
|
91
|
-
[1, 3, 2],
|
|
92
|
-
[2, 6, 3],
|
|
93
|
-
[8, 10, -1],
|
|
94
|
-
].map((d) => ({
|
|
95
|
-
start: d[0],
|
|
96
|
-
end: d[1],
|
|
97
|
-
weight: d[2],
|
|
98
|
-
}));
|
|
99
|
-
|
|
100
|
-
const coverageSegments = [
|
|
101
|
-
[0, 1, 1],
|
|
102
|
-
[1, 2, 3],
|
|
103
|
-
[2, 3, 6],
|
|
104
|
-
[3, 4, 4],
|
|
105
|
-
[4, 6, 3],
|
|
106
|
-
[8, 10, -1],
|
|
107
|
-
].map((d) => ({
|
|
108
|
-
start: d[0],
|
|
109
|
-
end: d[1],
|
|
110
|
-
coverage: d[2],
|
|
111
|
-
}));
|
|
112
|
-
|
|
113
|
-
/** @type {CoverageParams} */
|
|
114
|
-
const coverageConfig = {
|
|
115
|
-
type: "coverage",
|
|
116
|
-
chrom: "chrom",
|
|
117
|
-
start: "start",
|
|
118
|
-
end: "end",
|
|
119
|
-
weight: "weight",
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
expect(transform(coverageConfig, reads)).toEqual(coverageSegments);
|
|
123
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import createFunction from "../../utils/expression";
|
|
2
|
-
import FlowNode from "../flowNode";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {import("../../spec/transform").FilterParams} FilterParams
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export default class FilterTransform extends FlowNode {
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
-
* @param {FilterParams} params
|
|
12
|
-
*/
|
|
13
|
-
constructor(params) {
|
|
14
|
-
super();
|
|
15
|
-
this.params = params;
|
|
16
|
-
|
|
17
|
-
/** @type {(datum: any) => boolean} */
|
|
18
|
-
this.predicate = undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
initialize() {
|
|
22
|
-
this.predicate = createFunction(
|
|
23
|
-
this.params.expr,
|
|
24
|
-
this.getGlobalObject()
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
*
|
|
30
|
-
* @param {import("../flowNode").Datum} datum
|
|
31
|
-
*/
|
|
32
|
-
handle(datum) {
|
|
33
|
-
if (this.predicate(datum)) {
|
|
34
|
-
this._propagate(datum);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
2
|
-
import { processData } from "../flowTestUtils";
|
|
3
|
-
import FilterTransform from "./filter";
|
|
4
|
-
|
|
5
|
-
test("FilterTransform filter rows", () => {
|
|
6
|
-
const data = [1, 2, 3, 4, 5, 6].map((x) => ({ x }));
|
|
7
|
-
|
|
8
|
-
/** @type {import("../../spec/transform").FilterParams} */
|
|
9
|
-
const filterParams = {
|
|
10
|
-
type: "filter",
|
|
11
|
-
expr: "datum.x > 3 && datum.x != 5",
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const t = new FilterTransform(filterParams);
|
|
15
|
-
t.initialize();
|
|
16
|
-
|
|
17
|
-
expect(processData(t, data)).toEqual([4, 6].map((x) => ({ x })));
|
|
18
|
-
});
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { bisector } from "d3-array";
|
|
2
|
-
import FlowNode from "../flowNode";
|
|
3
|
-
import { topKSlice } from "../../utils/topK";
|
|
4
|
-
import ReservationMap from "../../utils/reservationMap";
|
|
5
|
-
import { field } from "../../utils/field";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {import("../../spec/transform").FilterScoredLabelsParams} Params
|
|
9
|
-
* @typedef {import("../../view/view").default} View
|
|
10
|
-
*/
|
|
11
|
-
export default class FilterScoredLabelsTransform extends FlowNode {
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
* @param {Params} params
|
|
15
|
-
* @param {View} view
|
|
16
|
-
*/
|
|
17
|
-
constructor(params, view) {
|
|
18
|
-
super();
|
|
19
|
-
|
|
20
|
-
this.params = params;
|
|
21
|
-
|
|
22
|
-
/** @type {any[]} */
|
|
23
|
-
this._data = [];
|
|
24
|
-
|
|
25
|
-
this.channel = params.channel ?? "x";
|
|
26
|
-
|
|
27
|
-
if (!["x", "y"].includes(this.channel)) {
|
|
28
|
-
throw new Error("Invalid channel: " + this.channel);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
this.posAccessor = field(this.params.pos);
|
|
32
|
-
this.posBisector = bisector(this.posAccessor);
|
|
33
|
-
this.scoreAccessor = field(this.params.score);
|
|
34
|
-
this.widthAccessor = field(this.params.width);
|
|
35
|
-
/** @type {function(any):any} */
|
|
36
|
-
this.laneAccessor = this.params.lane
|
|
37
|
-
? field(this.params.lane)
|
|
38
|
-
: (d) => 0;
|
|
39
|
-
this.padding = this.params.padding ?? 0;
|
|
40
|
-
|
|
41
|
-
/** @type {Map<any, ReservationMap>} */
|
|
42
|
-
this.reservationMaps = new Map();
|
|
43
|
-
|
|
44
|
-
this.resolution = view.getScaleResolution(this.channel);
|
|
45
|
-
|
|
46
|
-
// Synchronize propagation with rendering because we need both the domain and the range (length of the axis).
|
|
47
|
-
const callback = () => this._filterAndPropagate();
|
|
48
|
-
this.schedule = () => view.context.animator.requestTransition(callback);
|
|
49
|
-
|
|
50
|
-
// Propagate when the domain changes
|
|
51
|
-
this.resolution.addEventListener("domain", (scale) => this.schedule());
|
|
52
|
-
|
|
53
|
-
// Propagate when layout changes. Abusing a "private" method.
|
|
54
|
-
// TODO: Provide another attachment point, in view context for example
|
|
55
|
-
view._addBroadcastHandler("layoutComputed", () => this.schedule());
|
|
56
|
-
|
|
57
|
-
// TODO: Remove observers when this FlowNode is thrown away.
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
complete() {
|
|
61
|
-
const posAccessor = this.posAccessor;
|
|
62
|
-
this._data.sort((a, b) => posAccessor(a) - posAccessor(b));
|
|
63
|
-
|
|
64
|
-
this._scores = this._data.map(this.scoreAccessor);
|
|
65
|
-
|
|
66
|
-
for (const lane of new Set(this._data.map(this.laneAccessor))) {
|
|
67
|
-
this.reservationMaps.set(lane, new ReservationMap(200));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
this.schedule();
|
|
71
|
-
|
|
72
|
-
super.complete();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
_filterAndPropagate() {
|
|
76
|
-
super.reset();
|
|
77
|
-
|
|
78
|
-
const scale = this.resolution.getScale();
|
|
79
|
-
const rangeSpan =
|
|
80
|
-
this.resolution.members[0].view.coords?.[
|
|
81
|
-
this.channel == "x" ? "width" : "height"
|
|
82
|
-
];
|
|
83
|
-
if (!rangeSpan) {
|
|
84
|
-
// The view size is not (yet) available
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
for (const reservationMap of this.reservationMaps.values()) {
|
|
89
|
-
reservationMap.reset();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const domain = scale.domain();
|
|
93
|
-
const k = 70; // TODO: Configurable
|
|
94
|
-
|
|
95
|
-
// Find the maximum of k elements from the visible domain in priority order
|
|
96
|
-
const topIndices = topKSlice(
|
|
97
|
-
this._scores,
|
|
98
|
-
k,
|
|
99
|
-
this.posBisector.left(this._data, domain[0]),
|
|
100
|
-
this.posBisector.right(this._data, domain[1])
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
// Try to fit the elements on the available lanes and propagate if there was room
|
|
104
|
-
for (const i of topIndices) {
|
|
105
|
-
const datum = this._data[i];
|
|
106
|
-
const pos = scale(this.posAccessor(datum)) * rangeSpan;
|
|
107
|
-
const halfWidth = this.widthAccessor(datum) / 2 + this.padding;
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
this.reservationMaps
|
|
111
|
-
.get(this.laneAccessor(datum))
|
|
112
|
-
.reserve(pos - halfWidth, pos + halfWidth)
|
|
113
|
-
) {
|
|
114
|
-
this._propagate(datum);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
super.complete();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
reset() {
|
|
122
|
-
super.reset();
|
|
123
|
-
this._data = [];
|
|
124
|
-
this.groups = new Map();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
*
|
|
129
|
-
* @param {import("../flowNode").Datum} datum
|
|
130
|
-
*/
|
|
131
|
-
handle(datum) {
|
|
132
|
-
this._data.push(datum);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { field } from "../../utils/field";
|
|
2
|
-
import numberExtractor from "../../utils/numberExtractor";
|
|
3
|
-
import FlowNode, { BEHAVIOR_CLONES } from "../flowNode";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {import("../../spec/transform").FlattenCompressedExonsParams} FlattenCompressedExonsParams
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Flattens "run-length encoded" exons. The transforms inputs the start
|
|
11
|
-
* coordinate of the gene body and a comma-delimited string of alternating
|
|
12
|
-
* intron and exon lengths. A new datum is created for each exon.
|
|
13
|
-
*/
|
|
14
|
-
export default class FlattenCompressedExonsTransform extends FlowNode {
|
|
15
|
-
get behavior() {
|
|
16
|
-
return BEHAVIOR_CLONES;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
* @param {FlattenCompressedExonsParams} params
|
|
22
|
-
*/
|
|
23
|
-
constructor(params) {
|
|
24
|
-
super();
|
|
25
|
-
|
|
26
|
-
const exonsAccessor = field(params.exons ?? "exons");
|
|
27
|
-
const startAccessor = field(params.start ?? "start");
|
|
28
|
-
const [exonStart, exonEnd] = params.as || ["exonStart", "exonEnd"];
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
* @param {any} datum
|
|
33
|
-
*/
|
|
34
|
-
this.handle = (datum) => {
|
|
35
|
-
let upper = startAccessor(datum);
|
|
36
|
-
let lower = upper;
|
|
37
|
-
|
|
38
|
-
let inExon = true;
|
|
39
|
-
const exons = exonsAccessor(datum);
|
|
40
|
-
for (const token of numberExtractor(exons)) {
|
|
41
|
-
if (inExon) {
|
|
42
|
-
lower = upper + token;
|
|
43
|
-
} else {
|
|
44
|
-
upper = lower + token;
|
|
45
|
-
|
|
46
|
-
const newRow = Object.assign({}, datum);
|
|
47
|
-
newRow[exonStart] = lower;
|
|
48
|
-
newRow[exonEnd] = upper;
|
|
49
|
-
|
|
50
|
-
this._propagate(newRow);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
inExon = !inExon;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
}
|