@jbrowse/bandage-core 9.0.0 → 9.2.0
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/bubbles/bubbleHalos.js +9 -5
- package/dist/bubbles/bubbleLine.js +3 -1
- package/dist/bubbles/bubblesFromGraph.js +8 -4
- package/dist/bubbles/classifyBubble.js +2 -2
- package/dist/bubbles/superbubbles.js +21 -0
- package/dist/chunks/{chunk-ISEI2FH3.js → chunk-2AWUWGAD.js} +10 -4
- package/dist/chunks/{chunk-YOYLCLIG.js → chunk-2ZT7ZF7A.js} +6 -2
- package/dist/chunks/{chunk-LLP3FXPM.js → chunk-75HR3756.js} +24 -7
- package/dist/chunks/{chunk-FFFQNQUI.js → chunk-A4MYLGKG.js} +1 -1
- package/dist/chunks/chunk-B4645SM2.js +358 -0
- package/dist/chunks/chunk-B4YH42OP.js +24 -0
- package/dist/chunks/{chunk-6VBDMZBZ.js → chunk-BLRJ6IGK.js} +1 -1
- package/dist/chunks/{chunk-SKKYLBRA.js → chunk-BV64PQ2C.js} +1 -1
- package/dist/chunks/{chunk-W253V6RR.js → chunk-CVYDR2OP.js} +1 -1
- package/dist/chunks/{chunk-AMS3MAFP.js → chunk-FM6XRXPX.js} +1 -1
- package/dist/chunks/{chunk-MFKORG3F.js → chunk-FX4RDMDN.js} +5 -1
- package/dist/chunks/{chunk-MIW3HYPE.js → chunk-GFRPX6M2.js} +2 -2
- package/dist/chunks/{chunk-4S47XRYA.js → chunk-GOZYKNQ2.js} +16 -11
- package/dist/chunks/{chunk-TLQ7F2ZT.js → chunk-HOOVOF2Y.js} +21 -21
- package/dist/chunks/{chunk-MBB7ATM6.js → chunk-LFKMASER.js} +1 -1
- package/dist/chunks/{chunk-6ZNQJ5ZD.js → chunk-OXTR3BVF.js} +1 -1
- package/dist/chunks/{chunk-WDTXW7T2.js → chunk-UD5FOCI2.js} +1 -1
- package/dist/chunks/{chunk-2WNBVB55.js → chunk-WBCOREDZ.js} +121 -74
- package/dist/chunks/{chunk-TL4B342F.js → chunk-XKM5OHQQ.js} +1 -1
- package/dist/chunks/{chunk-WOO7QYKB.js → chunk-XKWD2RY3.js} +8 -2
- package/dist/chunks/{chunk-DPV2CUIE.js → chunk-XPT66H6H.js} +1 -1
- package/dist/chunks/chunk-ZDJDM77V.js +273 -0
- package/dist/cli/figure.js +26 -26
- package/dist/cli/fold.js +72 -0
- package/dist/figure.js +16 -16
- package/dist/figureSettings.js +4 -4
- package/dist/foldVariants.js +9 -0
- package/dist/gfa/writeRgfa.js +6 -0
- package/dist/index.js +84 -74
- package/dist/layout/forceCache.js +4 -4
- package/dist/layout/trimToWindow.js +13 -0
- package/dist/layout/walkRowDraw.js +4 -4
- package/dist/layout/walkStrip.js +5 -5
- package/dist/layoutModes.js +2 -2
- package/dist/pipeline.js +3 -3
- package/dist/referenceRampCss.js +3 -3
- package/dist/referenceStrip.js +3 -3
- package/dist/renderer/Canvas2DRenderer.js +1 -1
- package/dist/renderer/GeometryBuilder.js +2 -2
- package/dist/renderer/recordingCanvas.js +12 -1
- package/dist/tubeMap/connectors.js +3 -1
- package/dist/tubeMap/draw.js +3 -1
- package/dist/tubeMap/nodeColors.js +3 -3
- package/dist/types/bubbles/bubbleHalos.d.ts +2 -0
- package/dist/types/bubbles/bubbleLine.d.ts +3 -0
- package/dist/types/bubbles/bubblesFromGraph.d.ts +12 -2
- package/dist/types/bubbles/superbubbles.d.ts +5 -0
- package/dist/types/cli/fold.d.ts +1 -0
- package/dist/types/foldVariants.d.ts +2 -0
- package/dist/types/gfa/writeRgfa.d.ts +2 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/layout/trimToWindow.d.ts +4 -0
- package/dist/types/renderer/Canvas2DRenderer.d.ts +6 -3
- package/dist/types/tubeMap/connectors.d.ts +1 -0
- package/dist/types/tubeMap/draw.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/viewport.js +4 -4
- package/dist/walkEncoding.js +3 -3
- package/dist/walkHighlight.js +4 -4
- package/package.json +3 -2
- package/dist/chunks/{chunk-HBEKUQO4.js → chunk-BR25DGIJ.js} +4 -4
- package/dist/chunks/{chunk-WNUZP3WQ.js → chunk-M7SRZJ32.js} +3 -3
- package/dist/chunks/{chunk-5OIVTEF7.js → chunk-ZGRYFUXE.js} +7 -7
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
WHOLE_FRACTION,
|
|
3
|
+
bubbleHalos,
|
|
4
|
+
coversGraph
|
|
5
|
+
} from "../chunks/chunk-2AWUWGAD.js";
|
|
6
|
+
import "../chunks/chunk-XKWD2RY3.js";
|
|
7
|
+
import "../chunks/chunk-FX4RDMDN.js";
|
|
6
8
|
import "../chunks/chunk-2CVHXFK7.js";
|
|
7
9
|
import "../chunks/chunk-CPMHA4W5.js";
|
|
8
10
|
import "../chunks/chunk-FYQWPEEY.js";
|
|
9
11
|
export {
|
|
10
|
-
|
|
12
|
+
WHOLE_FRACTION,
|
|
13
|
+
bubbleHalos,
|
|
14
|
+
coversGraph
|
|
11
15
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SATURATED_PATH_COUNT,
|
|
3
3
|
bubbleDescription,
|
|
4
|
+
bubbleKey,
|
|
4
5
|
bubbleLabel,
|
|
5
6
|
parseBubbleLine,
|
|
6
7
|
sameBubble
|
|
7
|
-
} from "../chunks/chunk-
|
|
8
|
+
} from "../chunks/chunk-FX4RDMDN.js";
|
|
8
9
|
export {
|
|
9
10
|
SATURATED_PATH_COUNT,
|
|
10
11
|
bubbleDescription,
|
|
12
|
+
bubbleKey,
|
|
11
13
|
bubbleLabel,
|
|
12
14
|
parseBubbleLine,
|
|
13
15
|
sameBubble
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
bubblesFromGraph
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
bubblesFromGraph,
|
|
3
|
+
walkIndexOf,
|
|
4
|
+
walkRoutes
|
|
5
|
+
} from "../chunks/chunk-GOZYKNQ2.js";
|
|
6
|
+
import "../chunks/chunk-FX4RDMDN.js";
|
|
5
7
|
import "../chunks/chunk-HNQPEMYE.js";
|
|
6
8
|
import "../chunks/chunk-CPMHA4W5.js";
|
|
7
9
|
import "../chunks/chunk-FYQWPEEY.js";
|
|
8
10
|
export {
|
|
9
|
-
bubblesFromGraph
|
|
11
|
+
bubblesFromGraph,
|
|
12
|
+
walkIndexOf,
|
|
13
|
+
walkRoutes
|
|
10
14
|
};
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
bubbleSegmentIds,
|
|
5
5
|
classifyBubble,
|
|
6
6
|
formatBp
|
|
7
|
-
} from "../chunks/chunk-
|
|
8
|
-
import "../chunks/chunk-
|
|
7
|
+
} from "../chunks/chunk-XKWD2RY3.js";
|
|
8
|
+
import "../chunks/chunk-FX4RDMDN.js";
|
|
9
9
|
export {
|
|
10
10
|
BUBBLE_KIND_COLORS,
|
|
11
11
|
BUBBLE_KIND_NAMES,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_SUPERBUBBLES,
|
|
3
|
+
graphBubbles,
|
|
4
|
+
superbubblesFromGraph
|
|
5
|
+
} from "../chunks/chunk-ZDJDM77V.js";
|
|
6
|
+
import "../chunks/chunk-VHW7FDHU.js";
|
|
7
|
+
import "../chunks/chunk-2AWUWGAD.js";
|
|
8
|
+
import "../chunks/chunk-XKWD2RY3.js";
|
|
9
|
+
import "../chunks/chunk-GOZYKNQ2.js";
|
|
10
|
+
import "../chunks/chunk-FX4RDMDN.js";
|
|
11
|
+
import "../chunks/chunk-HNQPEMYE.js";
|
|
12
|
+
import "../chunks/chunk-ST2IBUAN.js";
|
|
13
|
+
import "../chunks/chunk-BPLIXMH7.js";
|
|
14
|
+
import "../chunks/chunk-2CVHXFK7.js";
|
|
15
|
+
import "../chunks/chunk-CPMHA4W5.js";
|
|
16
|
+
import "../chunks/chunk-FYQWPEEY.js";
|
|
17
|
+
export {
|
|
18
|
+
MAX_SUPERBUBBLES,
|
|
19
|
+
graphBubbles,
|
|
20
|
+
superbubblesFromGraph
|
|
21
|
+
};
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
bubbleSegmentIds,
|
|
3
3
|
classifyBubble,
|
|
4
4
|
formatBp
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XKWD2RY3.js";
|
|
6
6
|
import {
|
|
7
7
|
svgPath
|
|
8
8
|
} from "./chunk-2CVHXFK7.js";
|
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
|
|
13
13
|
// src/bubbles/bubbleHalos.ts
|
|
14
14
|
var WHOLE_FRACTION = 0.9;
|
|
15
|
+
function coversGraph(members, nodes) {
|
|
16
|
+
return members + 2 >= nodes || members >= WHOLE_FRACTION * nodes;
|
|
17
|
+
}
|
|
15
18
|
var NAMED_WALKS = 2;
|
|
16
19
|
var MIN_CHIPPED_BP = 1e3;
|
|
17
20
|
function bubbleHalos(graph, bubbles, positions, walkLabel = (name) => name, repeats = []) {
|
|
@@ -24,13 +27,14 @@ function bubbleHalos(graph, bubbles, positions, walkLabel = (name) => name, repe
|
|
|
24
27
|
let minY = Infinity;
|
|
25
28
|
const nodeIds = [];
|
|
26
29
|
const ends = [];
|
|
27
|
-
|
|
30
|
+
const names = bubbleSegmentIds(bubble);
|
|
31
|
+
for (const [i, name] of names.entries()) {
|
|
28
32
|
const node = byName.get(name);
|
|
29
33
|
const line = node && positions[node.id];
|
|
30
34
|
if (!node || !line?.length) {
|
|
31
35
|
continue;
|
|
32
36
|
}
|
|
33
|
-
if (isBackbone(node) && (node.stable.start < bubble.start || node.stable.start >= bubble.end)) {
|
|
37
|
+
if (bubble.key ? i === 0 || i === names.length - 1 : isBackbone(node) && (node.stable.start < bubble.start || node.stable.start >= bubble.end)) {
|
|
34
38
|
ends.push(line[Math.floor(line.length / 2)]);
|
|
35
39
|
continue;
|
|
36
40
|
}
|
|
@@ -90,7 +94,7 @@ function bubbleHalos(graph, bubbles, positions, walkLabel = (name) => name, repe
|
|
|
90
94
|
labelAt,
|
|
91
95
|
members: nodeIds.length,
|
|
92
96
|
nodeIds,
|
|
93
|
-
whole: nodeIds.length
|
|
97
|
+
whole: coversGraph(nodeIds.length, graph.nodes.length),
|
|
94
98
|
routes
|
|
95
99
|
});
|
|
96
100
|
}
|
|
@@ -104,5 +108,7 @@ function routeText(route, walkLabel) {
|
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
export {
|
|
111
|
+
WHOLE_FRACTION,
|
|
112
|
+
coversGraph,
|
|
107
113
|
bubbleHalos
|
|
108
114
|
};
|
|
@@ -69,7 +69,10 @@ function drawTubeMapConnectors(ctx, connectors, bottom, {
|
|
|
69
69
|
ctx.strokeStyle = `rgba(${ink},0.35)`;
|
|
70
70
|
ctx.lineWidth = 0.5;
|
|
71
71
|
ctx.stroke();
|
|
72
|
-
|
|
72
|
+
drawLitConnector(ctx, connectors, bottom, highlightNode);
|
|
73
|
+
}
|
|
74
|
+
function drawLitConnector(ctx, connectors, bottom, node) {
|
|
75
|
+
const lit = bottom > 0 && node ? connectors.find((c) => c.node === node) : void 0;
|
|
73
76
|
if (lit) {
|
|
74
77
|
ctx.beginPath();
|
|
75
78
|
band(ctx, lit, bottom);
|
|
@@ -86,5 +89,6 @@ function drawTubeMapConnectors(ctx, connectors, bottom, {
|
|
|
86
89
|
export {
|
|
87
90
|
tubeMapConnectors,
|
|
88
91
|
connectorAt,
|
|
89
|
-
drawTubeMapConnectors
|
|
92
|
+
drawTubeMapConnectors,
|
|
93
|
+
drawLitConnector
|
|
90
94
|
};
|
|
@@ -192,24 +192,40 @@ function drawTubeMap(ctx, picture, frame) {
|
|
|
192
192
|
}
|
|
193
193
|
const stroke = frame.darkMode ? "#d0d0d0" : "#000000";
|
|
194
194
|
const fill = frame.darkMode ? "rgba(40,40,40,0.4)" : "rgba(255,255,255,0.4)";
|
|
195
|
-
ctx.lineWidth =
|
|
195
|
+
ctx.lineWidth = outlineWidth(frame);
|
|
196
196
|
for (const node of picture.nodes) {
|
|
197
|
-
if (visible(node)) {
|
|
198
|
-
const lit = node.name === frame.highlightNode;
|
|
197
|
+
if (visible(node) && node.name !== frame.highlightNode) {
|
|
199
198
|
ctx.beginPath();
|
|
200
199
|
trace(ctx, node.commands, x, y);
|
|
201
|
-
const tint =
|
|
202
|
-
ctx.fillStyle =
|
|
203
|
-
ctx.strokeStyle =
|
|
200
|
+
const tint = frame.nodeColors?.get(node.name);
|
|
201
|
+
ctx.fillStyle = tint ?? fill;
|
|
202
|
+
ctx.strokeStyle = stroke;
|
|
204
203
|
ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
|
|
205
204
|
ctx.fill();
|
|
206
|
-
ctx.globalAlpha =
|
|
205
|
+
ctx.globalAlpha = outlineAlpha(x(node.x1) - x(node.x0));
|
|
207
206
|
ctx.stroke();
|
|
208
207
|
ctx.globalAlpha = 1;
|
|
209
208
|
}
|
|
210
209
|
}
|
|
210
|
+
drawTubeMapHighlight(ctx, picture, frame);
|
|
211
211
|
drawMismatches(ctx, picture.mismatches, frame);
|
|
212
212
|
}
|
|
213
|
+
function outlineWidth(frame) {
|
|
214
|
+
return 2 * Math.max(0.25, Math.min(1, frame.yScale));
|
|
215
|
+
}
|
|
216
|
+
function drawTubeMapHighlight(ctx, picture, frame) {
|
|
217
|
+
const node = frame.highlightNode ? picture.nodes.find((n) => n.name === frame.highlightNode) : void 0;
|
|
218
|
+
if (node) {
|
|
219
|
+
ctx.beginPath();
|
|
220
|
+
trace(ctx, node.commands, frame.x, frame.y);
|
|
221
|
+
ctx.globalAlpha = 1;
|
|
222
|
+
ctx.lineWidth = outlineWidth(frame);
|
|
223
|
+
ctx.fillStyle = "rgba(255,192,203,0.5)";
|
|
224
|
+
ctx.strokeStyle = "#ff0000";
|
|
225
|
+
ctx.fill();
|
|
226
|
+
ctx.stroke();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
213
229
|
var MISMATCH_FONT_PX = 12;
|
|
214
230
|
var MIN_MISMATCH_FONT_PX = 6;
|
|
215
231
|
function mismatchesLegible(yScale) {
|
|
@@ -273,6 +289,7 @@ export {
|
|
|
273
289
|
trackColors,
|
|
274
290
|
tubeMapPicture,
|
|
275
291
|
drawTubeMap,
|
|
292
|
+
drawTubeMapHighlight,
|
|
276
293
|
mismatchesLegible,
|
|
277
294
|
mismatchOnScreen,
|
|
278
295
|
tubeMapTrackAt,
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ownStrand
|
|
3
|
+
} from "./chunk-2CVHXFK7.js";
|
|
4
|
+
import {
|
|
5
|
+
isBackbone
|
|
6
|
+
} from "./chunk-FYQWPEEY.js";
|
|
7
|
+
|
|
8
|
+
// src/foldVariants.ts
|
|
9
|
+
function foldVariants(graph, belowBp) {
|
|
10
|
+
if (!(belowBp > 0) || graph.paths?.length || !graph.nodes.some((node) => isBackbone(node))) {
|
|
11
|
+
return graph;
|
|
12
|
+
}
|
|
13
|
+
const { nodes, edges } = graph;
|
|
14
|
+
const index = new Map(nodes.map((node, i) => [node.id, i]));
|
|
15
|
+
const sides = edges.map(forwardSides);
|
|
16
|
+
const links = linksAt(nodes.length, edges, sides, index);
|
|
17
|
+
const allele = alleleOf(nodes, edges, sides, index);
|
|
18
|
+
const members = groupBy(allele);
|
|
19
|
+
const kept = new Uint8Array(nodes.length);
|
|
20
|
+
const big = [];
|
|
21
|
+
for (const group of members.values()) {
|
|
22
|
+
if (alleleContent(group, nodes, links, allele) >= belowBp) {
|
|
23
|
+
for (const i of group) {
|
|
24
|
+
kept[i] = 1;
|
|
25
|
+
}
|
|
26
|
+
if (!isBackbone(nodes[group[0]])) {
|
|
27
|
+
big.push(group);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const { dist, pred } = distancesFromBackbone(nodes, links);
|
|
32
|
+
for (const group of big) {
|
|
33
|
+
for (const [i, side] of alleleEnds(group, nodes, links, allele)) {
|
|
34
|
+
let next = nearestOutside(i, side, links, allele, dist, nodes);
|
|
35
|
+
while (next !== void 0 && !kept[next]) {
|
|
36
|
+
kept[next] = 1;
|
|
37
|
+
next = isBackbone(nodes[next]) ? void 0 : pred[next];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const keptEdges = edges.filter((edge, k) => {
|
|
42
|
+
const a = index.get(edge.from);
|
|
43
|
+
const b = index.get(edge.to);
|
|
44
|
+
if (a === void 0 || b === void 0) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const skip = skipped(nodes[a], sides[k].from, nodes[b], sides[k].to);
|
|
48
|
+
return kept[a] && kept[b] && !(skip > 0 && skip < belowBp);
|
|
49
|
+
});
|
|
50
|
+
return mergeAlleleRuns({ ...graph, edges: keptEdges }, kept, allele);
|
|
51
|
+
}
|
|
52
|
+
function forwardSides(edge) {
|
|
53
|
+
return {
|
|
54
|
+
from: (edge.fromStrand ?? ownStrand(edge.from)) === "+" ? "e" : "s",
|
|
55
|
+
to: (edge.toStrand ?? ownStrand(edge.to)) === "+" ? "s" : "e"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function linksAt(count, edges, sides, index) {
|
|
59
|
+
const links = Array.from({ length: count }, () => []);
|
|
60
|
+
edges.forEach((edge, k) => {
|
|
61
|
+
const a = index.get(edge.from);
|
|
62
|
+
const b = index.get(edge.to);
|
|
63
|
+
if (a !== void 0 && b !== void 0) {
|
|
64
|
+
links[a].push({ other: b, side: sides[k].from });
|
|
65
|
+
links[b].push({ other: a, side: sides[k].to });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return links;
|
|
69
|
+
}
|
|
70
|
+
function onOneSequence(a, b) {
|
|
71
|
+
const sa = a.stable;
|
|
72
|
+
const sb = b.stable;
|
|
73
|
+
return sa && sb?.refName === sa.refName && sb.rank === sa.rank ? [sa, sb] : void 0;
|
|
74
|
+
}
|
|
75
|
+
function continues(a, aSide, b, bSide) {
|
|
76
|
+
const pair = onOneSequence(a, b);
|
|
77
|
+
if (!pair || aSide === bSide) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const [sa, sb] = pair;
|
|
81
|
+
return sa.start + a.length === sb.start || sb.start + b.length === sa.start;
|
|
82
|
+
}
|
|
83
|
+
function skipped(a, aSide, b, bSide) {
|
|
84
|
+
const pair = onOneSequence(a, b);
|
|
85
|
+
if (!pair || continues(a, aSide, b, bSide)) {
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
const [sa, sb] = pair;
|
|
89
|
+
return Math.max(
|
|
90
|
+
sb.start - (sa.start + a.length),
|
|
91
|
+
sa.start - (sb.start + b.length)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function alleleOf(nodes, edges, sides, index) {
|
|
95
|
+
const parent = nodes.map((_, i) => i);
|
|
96
|
+
const find = (i) => {
|
|
97
|
+
while (parent[i] !== i) {
|
|
98
|
+
parent[i] = parent[parent[i]];
|
|
99
|
+
i = parent[i];
|
|
100
|
+
}
|
|
101
|
+
return i;
|
|
102
|
+
};
|
|
103
|
+
edges.forEach((edge, k) => {
|
|
104
|
+
const a = index.get(edge.from);
|
|
105
|
+
const b = index.get(edge.to);
|
|
106
|
+
if (a !== void 0 && b !== void 0 && continues(nodes[a], sides[k].from, nodes[b], sides[k].to)) {
|
|
107
|
+
parent[find(a)] = find(b);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return nodes.map((_, i) => find(i));
|
|
111
|
+
}
|
|
112
|
+
function groupBy(allele) {
|
|
113
|
+
const groups = /* @__PURE__ */ new Map();
|
|
114
|
+
allele.forEach((root, i) => {
|
|
115
|
+
const group = groups.get(root);
|
|
116
|
+
if (group) {
|
|
117
|
+
group.push(i);
|
|
118
|
+
} else {
|
|
119
|
+
groups.set(root, [i]);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return groups;
|
|
123
|
+
}
|
|
124
|
+
function alleleContent(group, nodes, links, allele) {
|
|
125
|
+
if (isBackbone(nodes[group[0]])) {
|
|
126
|
+
return Infinity;
|
|
127
|
+
}
|
|
128
|
+
let length = 0;
|
|
129
|
+
let refName;
|
|
130
|
+
let lo = Infinity;
|
|
131
|
+
let hi = -Infinity;
|
|
132
|
+
let oneSequence = true;
|
|
133
|
+
for (const i of group) {
|
|
134
|
+
length += nodes[i].length;
|
|
135
|
+
for (const { other } of links[i]) {
|
|
136
|
+
const node = nodes[other];
|
|
137
|
+
if (allele[other] !== allele[i] && isBackbone(node)) {
|
|
138
|
+
refName ??= node.stable.refName;
|
|
139
|
+
oneSequence &&= node.stable.refName === refName;
|
|
140
|
+
lo = Math.min(lo, node.stable.start + node.length);
|
|
141
|
+
hi = Math.max(hi, node.stable.start);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return oneSequence && hi > lo ? Math.max(length, hi - lo) : length;
|
|
146
|
+
}
|
|
147
|
+
function alleleEnds(group, nodes, links, allele) {
|
|
148
|
+
const start = (i) => nodes[i].stable?.start ?? 0;
|
|
149
|
+
const end = (i) => start(i) + nodes[i].length;
|
|
150
|
+
let first = group[0];
|
|
151
|
+
let last = group[0];
|
|
152
|
+
for (const i of group) {
|
|
153
|
+
if (start(i) < start(first)) {
|
|
154
|
+
first = i;
|
|
155
|
+
}
|
|
156
|
+
if (end(i) > end(last)) {
|
|
157
|
+
last = i;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const ends = [];
|
|
161
|
+
for (const i of /* @__PURE__ */ new Set([first, last])) {
|
|
162
|
+
for (const side of ["s", "e"]) {
|
|
163
|
+
if (!links[i].some(
|
|
164
|
+
(link) => link.side === side && allele[link.other] === allele[i]
|
|
165
|
+
)) {
|
|
166
|
+
ends.push([i, side]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return ends;
|
|
171
|
+
}
|
|
172
|
+
function nearestOutside(i, side, links, allele, dist, nodes) {
|
|
173
|
+
let best;
|
|
174
|
+
for (const link of links[i]) {
|
|
175
|
+
const j = link.other;
|
|
176
|
+
if (link.side === side && allele[j] !== allele[i] && Number.isFinite(dist[j]) && (best === void 0 || dist[j] < dist[best] || dist[j] === dist[best] && nodes[j].id < nodes[best].id)) {
|
|
177
|
+
best = j;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return best;
|
|
181
|
+
}
|
|
182
|
+
function distancesFromBackbone(nodes, links) {
|
|
183
|
+
const dist = new Float64Array(nodes.length).fill(Infinity);
|
|
184
|
+
const pred = new Int32Array(nodes.length).fill(-1);
|
|
185
|
+
const heap = new MinHeap(
|
|
186
|
+
([da, a], [db, b]) => da - db || cmp(nodes[a].id, nodes[b].id)
|
|
187
|
+
);
|
|
188
|
+
nodes.forEach((node, i) => {
|
|
189
|
+
if (isBackbone(node)) {
|
|
190
|
+
dist[i] = 0;
|
|
191
|
+
heap.push([0, i]);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
for (let top = heap.pop(); top !== void 0; top = heap.pop()) {
|
|
195
|
+
const [d0, i] = top;
|
|
196
|
+
if (d0 > dist[i]) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
for (const { other } of links[i]) {
|
|
200
|
+
if (isBackbone(nodes[other])) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const d = d0 + nodes[other].length;
|
|
204
|
+
if (d < dist[other]) {
|
|
205
|
+
dist[other] = d;
|
|
206
|
+
pred[other] = i;
|
|
207
|
+
heap.push([d, other]);
|
|
208
|
+
} else if (d === dist[other] && cmp(nodes[i].id, nodes[pred[other]].id) < 0) {
|
|
209
|
+
pred[other] = i;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return { dist, pred: Array.from(pred, (p) => p < 0 ? void 0 : p) };
|
|
214
|
+
}
|
|
215
|
+
function cmp(a, b) {
|
|
216
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
217
|
+
}
|
|
218
|
+
var MinHeap = class {
|
|
219
|
+
constructor(order) {
|
|
220
|
+
this.order = order;
|
|
221
|
+
this.items = [];
|
|
222
|
+
}
|
|
223
|
+
push(item) {
|
|
224
|
+
const { items } = this;
|
|
225
|
+
items.push(item);
|
|
226
|
+
let i = items.length - 1;
|
|
227
|
+
while (i > 0) {
|
|
228
|
+
const up = i - 1 >> 1;
|
|
229
|
+
if (this.order(items[i], items[up]) >= 0) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
;
|
|
233
|
+
[items[i], items[up]] = [items[up], items[i]];
|
|
234
|
+
i = up;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
pop() {
|
|
238
|
+
const { items } = this;
|
|
239
|
+
const top = items[0];
|
|
240
|
+
const last = items.pop();
|
|
241
|
+
if (items.length > 0 && last !== void 0) {
|
|
242
|
+
items[0] = last;
|
|
243
|
+
let i = 0;
|
|
244
|
+
for (; ; ) {
|
|
245
|
+
const l = 2 * i + 1;
|
|
246
|
+
const r = l + 1;
|
|
247
|
+
let m = i;
|
|
248
|
+
if (l < items.length && this.order(items[l], items[m]) < 0) {
|
|
249
|
+
m = l;
|
|
250
|
+
}
|
|
251
|
+
if (r < items.length && this.order(items[r], items[m]) < 0) {
|
|
252
|
+
m = r;
|
|
253
|
+
}
|
|
254
|
+
if (m === i) {
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
;
|
|
258
|
+
[items[i], items[m]] = [items[m], items[i]];
|
|
259
|
+
i = m;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return top;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
function mergeAlleleRuns(graph, kept, allele) {
|
|
266
|
+
const { nodes, edges } = graph;
|
|
267
|
+
const index = new Map(nodes.map((node, i) => [node.id, i]));
|
|
268
|
+
const sides = edges.map(forwardSides);
|
|
269
|
+
const ends = edges.map((edge, k) => [
|
|
270
|
+
{ i: index.get(edge.from), side: sides[k].from },
|
|
271
|
+
{ i: index.get(edge.to), side: sides[k].to }
|
|
272
|
+
]);
|
|
273
|
+
const degree = /* @__PURE__ */ new Map();
|
|
274
|
+
const sideKey = (i, side) => `${i}${side}`;
|
|
275
|
+
for (const pair of ends) {
|
|
276
|
+
for (const { i, side } of pair) {
|
|
277
|
+
degree.set(sideKey(i, side), (degree.get(sideKey(i, side)) ?? 0) + 1);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const next = /* @__PURE__ */ new Map();
|
|
281
|
+
const hasPrev = /* @__PURE__ */ new Set();
|
|
282
|
+
const internal = /* @__PURE__ */ new Set();
|
|
283
|
+
ends.forEach(([a, b], k) => {
|
|
284
|
+
if (a.i !== b.i && allele[a.i] === allele[b.i] && continues(nodes[a.i], a.side, nodes[b.i], b.side) && degree.get(sideKey(a.i, a.side)) === 1 && degree.get(sideKey(b.i, b.side)) === 1) {
|
|
285
|
+
const [left, right] = nodes[a.i].stable.start < nodes[b.i].stable.start ? [a, b] : [b, a];
|
|
286
|
+
next.set(left.i, { j: right.i, out: left.side, in: right.side });
|
|
287
|
+
hasPrev.add(right.i);
|
|
288
|
+
internal.add(k);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
const headOf = nodes.map((_, i) => i);
|
|
292
|
+
const outer = /* @__PURE__ */ new Map();
|
|
293
|
+
const merged = /* @__PURE__ */ new Map();
|
|
294
|
+
nodes.forEach((_, i) => {
|
|
295
|
+
if (!kept[i] || hasPrev.has(i) || !next.has(i)) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const run = [i];
|
|
299
|
+
let into = flip(next.get(i).out);
|
|
300
|
+
outer.set(sideKey(i, into), "s");
|
|
301
|
+
for (let step = next.get(i); step; step = next.get(step.j)) {
|
|
302
|
+
run.push(step.j);
|
|
303
|
+
into = step.in;
|
|
304
|
+
}
|
|
305
|
+
outer.set(sideKey(run.at(-1), flip(into)), "e");
|
|
306
|
+
for (const j of run) {
|
|
307
|
+
headOf[j] = i;
|
|
308
|
+
}
|
|
309
|
+
merged.set(i, mergedNode(run.map((j) => nodes[j])));
|
|
310
|
+
});
|
|
311
|
+
const endpoint = ({ i, side }) => ({
|
|
312
|
+
id: nodes[headOf[i]].id,
|
|
313
|
+
side: headOf[i] === i && !merged.has(i) ? side : outer.get(sideKey(i, side))
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
...graph,
|
|
317
|
+
nodes: nodes.flatMap(
|
|
318
|
+
(node, i) => !kept[i] || headOf[i] !== i ? [] : [merged.get(i) ?? node]
|
|
319
|
+
),
|
|
320
|
+
edges: edges.flatMap((edge, k) => {
|
|
321
|
+
if (internal.has(k)) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
const [a, b] = ends[k];
|
|
325
|
+
if (!merged.has(headOf[a.i]) && !merged.has(headOf[b.i])) {
|
|
326
|
+
return [edge];
|
|
327
|
+
}
|
|
328
|
+
const from = endpoint(a);
|
|
329
|
+
const to = endpoint(b);
|
|
330
|
+
return [
|
|
331
|
+
{
|
|
332
|
+
...edge,
|
|
333
|
+
from: from.id,
|
|
334
|
+
to: to.id,
|
|
335
|
+
fromStrand: from.side === "e" ? "+" : "-",
|
|
336
|
+
toStrand: to.side === "s" ? "+" : "-"
|
|
337
|
+
}
|
|
338
|
+
];
|
|
339
|
+
})
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function flip(side) {
|
|
343
|
+
return side === "s" ? "e" : "s";
|
|
344
|
+
}
|
|
345
|
+
function mergedNode(run) {
|
|
346
|
+
const { samples: firstSamples, ...first } = run[0];
|
|
347
|
+
const length = run.reduce((sum, node) => sum + node.length, 0);
|
|
348
|
+
const depth = length > 0 ? run.reduce((sum, node) => sum + node.depth * node.length, 0) / length : first.depth;
|
|
349
|
+
const samples = run.every((node) => node.samples) ? run.reduce(
|
|
350
|
+
(common, node) => common.filter((s) => node.samples.includes(s)),
|
|
351
|
+
firstSamples
|
|
352
|
+
) : void 0;
|
|
353
|
+
return samples ? { ...first, length, depth, samples } : { ...first, length, depth };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export {
|
|
357
|
+
foldVariants
|
|
358
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/gfa/writeRgfa.ts
|
|
2
|
+
function writeRgfa(graph) {
|
|
3
|
+
const nameOf = new Map(graph.nodes.map((n) => [n.id, n.name]));
|
|
4
|
+
return [
|
|
5
|
+
"H VN:Z:1.0",
|
|
6
|
+
...graph.nodes.map(segmentLine),
|
|
7
|
+
...graph.edges.map(
|
|
8
|
+
(e) => `L ${nameOf.get(e.from)} ${e.fromStrand ?? "+"} ${nameOf.get(e.to)} ${e.toStrand ?? "+"} 0M`
|
|
9
|
+
),
|
|
10
|
+
""
|
|
11
|
+
].join("\n");
|
|
12
|
+
}
|
|
13
|
+
function segmentLine(node) {
|
|
14
|
+
const { stable } = node;
|
|
15
|
+
if (!stable) {
|
|
16
|
+
throw new Error(`segment ${node.name} has no stable coordinate to file`);
|
|
17
|
+
}
|
|
18
|
+
const samples = node.samples?.length ? ` SM:Z:${node.samples.join(",")}` : "";
|
|
19
|
+
return `S ${node.name} * LN:i:${node.length} SN:Z:${stable.refName} SO:i:${stable.start} SR:i:${stable.rank}${samples}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
writeRgfa
|
|
24
|
+
};
|
|
@@ -19,7 +19,10 @@ function parseBubbleLine(line) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
function sameBubble(a, b) {
|
|
22
|
-
return a.refName === b.refName && a.start === b.start && a.end === b.end;
|
|
22
|
+
return a.key !== void 0 || b.key !== void 0 ? a.key === b.key : a.refName === b.refName && a.start === b.start && a.end === b.end;
|
|
23
|
+
}
|
|
24
|
+
function bubbleKey(b) {
|
|
25
|
+
return b.key ?? `${b.refName}:${b.start}-${b.end}`;
|
|
23
26
|
}
|
|
24
27
|
function bubbleLabel(bubble) {
|
|
25
28
|
const { shortestAlleleLength: min, longestAlleleLength: max } = bubble;
|
|
@@ -38,6 +41,7 @@ function bubbleDescription(bubble) {
|
|
|
38
41
|
export {
|
|
39
42
|
parseBubbleLine,
|
|
40
43
|
sameBubble,
|
|
44
|
+
bubbleKey,
|
|
41
45
|
bubbleLabel,
|
|
42
46
|
SATURATED_PATH_COUNT,
|
|
43
47
|
bubbleDescription
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
RAMP_GRADIENT_STOPS,
|
|
4
4
|
rampHueCss,
|
|
5
5
|
rampStops
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OXTR3BVF.js";
|
|
7
7
|
import {
|
|
8
8
|
REFERENCE_RAMP_ALT_CSS
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-BR25DGIJ.js";
|
|
10
10
|
import {
|
|
11
11
|
LABEL_CHAR_PX
|
|
12
12
|
} from "./chunk-AB35TJST.js";
|