@jbrowse/bandage-core 6.0.1 → 6.0.3
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 +23 -7
- package/dist/alleleProjection/projectAlleles.js +10 -0
- package/dist/anchoredNodes.js +24 -0
- package/dist/bubbleSpreads.js +11 -0
- package/dist/bubbles/bubbleHalos.js +11 -0
- package/dist/bubbles/bubbleLine.js +14 -0
- package/dist/bubbles/bubblesFromGraph.js +10 -0
- package/dist/bubbles/classifyBubble.js +15 -0
- package/dist/bubbles/popBubble.js +9 -0
- package/dist/chunks/chunk-2CVHXFK7.js +530 -0
- package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
- package/dist/chunks/chunk-47DYCZOK.js +115 -0
- package/dist/chunks/chunk-4EMPBO7S.js +117 -0
- package/dist/chunks/chunk-4S47XRYA.js +149 -0
- package/dist/chunks/chunk-5XQDMQXU.js +431 -0
- package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
- package/dist/chunks/chunk-6SBUFFTB.js +97 -0
- package/dist/chunks/chunk-6SXMCK6I.js +476 -0
- package/dist/chunks/chunk-72N5DJHZ.js +155 -0
- package/dist/chunks/chunk-73RSLGZT.js +73 -0
- package/dist/chunks/chunk-7XHHVYG7.js +13 -0
- package/dist/chunks/chunk-AB35TJST.js +67 -0
- package/dist/chunks/chunk-BPLIXMH7.js +52 -0
- package/dist/chunks/chunk-BUBZ32IC.js +101 -0
- package/dist/chunks/chunk-BXEPA4KS.js +179 -0
- package/dist/chunks/chunk-C44YRP5F.js +85 -0
- package/dist/chunks/chunk-CDLPYR5O.js +135 -0
- package/dist/chunks/chunk-CJ2EE4AF.js +40 -0
- package/dist/chunks/chunk-CPMHA4W5.js +6 -0
- package/dist/chunks/chunk-CR7EGAFE.js +241 -0
- package/dist/chunks/chunk-CS3E4ETM.js +202 -0
- package/dist/chunks/chunk-CTSBC2O6.js +90 -0
- package/dist/chunks/chunk-D3M7SBKL.js +210 -0
- package/dist/chunks/chunk-DKOB2OPC.js +177 -0
- package/dist/chunks/chunk-EJ5OZTCK.js +52 -0
- package/dist/chunks/chunk-EK4HGYO7.js +174 -0
- package/dist/chunks/chunk-EPZORFTI.js +23 -0
- package/dist/chunks/chunk-ES2N6V56.js +83 -0
- package/dist/chunks/chunk-EZPBJPK6.js +54 -0
- package/dist/chunks/chunk-FYQWPEEY.js +65 -0
- package/dist/chunks/chunk-GGQRADFS.js +170 -0
- package/dist/chunks/chunk-GOY6REMT.js +43 -0
- package/dist/chunks/chunk-GQ2AOHU4.js +112 -0
- package/dist/chunks/chunk-HNQPEMYE.js +297 -0
- package/dist/chunks/chunk-IGVV37VI.js +60 -0
- package/dist/chunks/chunk-IO2PCPZI.js +218 -0
- package/dist/chunks/chunk-ISEI2FH3.js +108 -0
- package/dist/chunks/chunk-JKJWSY4V.js +32 -0
- package/dist/chunks/chunk-KQFOCDFY.js +73 -0
- package/dist/chunks/chunk-L3O7AWLI.js +52 -0
- package/dist/chunks/chunk-LLJUG2H7.js +6 -0
- package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
- package/dist/chunks/chunk-LTUWJKHD.js +112 -0
- package/dist/chunks/chunk-MFKORG3F.js +44 -0
- package/dist/chunks/chunk-MI35EUIE.js +76 -0
- package/dist/chunks/chunk-MNUSQCLG.js +29 -0
- package/dist/chunks/chunk-MQJUG2D2.js +53 -0
- package/dist/chunks/chunk-MREQ7GIA.js +181 -0
- package/dist/chunks/chunk-N62KBLFC.js +326 -0
- package/dist/chunks/chunk-NNODIW4U.js +26 -0
- package/dist/chunks/chunk-PBENCR5L.js +60 -0
- package/dist/chunks/chunk-PCZ4PTYC.js +726 -0
- package/dist/chunks/chunk-PUG7LXDF.js +70 -0
- package/dist/chunks/chunk-QYBZZI5H.js +0 -0
- package/dist/chunks/chunk-R6ZUK4Y3.js +232 -0
- package/dist/chunks/chunk-RAKOB6IV.js +169 -0
- package/dist/chunks/chunk-ROF2R3RF.js +205 -0
- package/dist/chunks/chunk-RVS2QCOX.js +135 -0
- package/dist/chunks/chunk-ST2IBUAN.js +119 -0
- package/dist/chunks/chunk-SZNZ2T6D.js +198 -0
- package/dist/chunks/chunk-T43W7NKK.js +122 -0
- package/dist/chunks/chunk-TAMCZHLW.js +190 -0
- package/dist/chunks/chunk-TDQLINVI.js +16 -0
- package/dist/chunks/chunk-TEDQJPMI.js +131 -0
- package/dist/chunks/chunk-TZFWUT2C.js +66 -0
- package/dist/chunks/chunk-UVBGRJQX.js +50 -0
- package/dist/chunks/chunk-VHW7FDHU.js +42 -0
- package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
- package/dist/chunks/chunk-WFX3MAKA.js +102 -0
- package/dist/chunks/chunk-WOO7QYKB.js +121 -0
- package/dist/chunks/chunk-WSIFAZU7.js +132 -0
- package/dist/chunks/chunk-X5L37W4J.js +82 -0
- package/dist/chunks/chunk-Y3IOTZJA.js +61 -0
- package/dist/chunks/chunk-YMSSF2RK.js +257 -0
- package/dist/cli/figure.js +84 -17
- package/dist/colorSchemes.js +10 -0
- package/dist/deletionEdges.js +13 -0
- package/dist/el.js +8 -0
- package/dist/facetGrid.js +16 -0
- package/dist/figure.js +51 -0
- package/dist/gaf/parseGaf.js +12 -0
- package/dist/gbzCut.js +19 -0
- package/dist/gbzJoin.js +6 -0
- package/dist/gbzWindow.js +28 -0
- package/dist/genes/exonOutline.js +11 -0
- package/dist/genes/geneFiles.js +14 -0
- package/dist/genes/genePins.js +10 -0
- package/dist/gfa/gfaConverter.js +11 -0
- package/dist/gfa-core/gfaParser.js +8 -0
- package/dist/gfa-core/index.js +9 -0
- package/dist/graphLabels.js +24 -0
- package/dist/index.js +262 -1407
- package/dist/labelLayout.js +24 -0
- package/dist/layout/anchoredLayout.js +11 -0
- package/dist/layout/deletionRoutes.js +10 -0
- package/dist/layout/drawnScale.js +22 -0
- package/dist/layout/mergeRuns.js +15 -0
- package/dist/layout/orderedLayout.js +12 -0
- package/dist/layout/orientToReference.js +7 -0
- package/dist/layout/placeOffReference.js +9 -0
- package/dist/layout/referenceSeeds.js +11 -0
- package/dist/layout/rowSpacing.js +6 -0
- package/dist/layout/sampleRowLayout.js +11 -0
- package/dist/layout/trimToWindow.js +99 -0
- package/dist/layout/tubeMapLayout.js +28 -0
- package/dist/layout/walkRowDraw.js +65 -0
- package/dist/layout/walkRowLayout.js +13 -0
- package/dist/layout/walkRows.js +11 -0
- package/dist/layout/walkStrip.js +40 -0
- package/dist/layoutModes.js +35 -0
- package/dist/loadBandage.js +6 -0
- package/dist/nodeWidths.js +18 -0
- package/dist/overlayLabels.js +18 -0
- package/dist/pansn.js +18 -0
- package/dist/pathAnchoring.js +18 -0
- package/dist/pathColors.js +31 -0
- package/dist/pipeline.js +40 -0
- package/dist/reference.js +24 -0
- package/dist/referenceRampCss.js +29 -0
- package/dist/referenceSpan.js +11 -0
- package/dist/referenceStrip.js +35 -0
- package/dist/renderer/Canvas2DRenderer.js +9 -0
- package/dist/renderer/GeometryBuilder.js +42 -0
- package/dist/renderer/colorBits.js +8 -0
- package/dist/renderer/recordingCanvas.js +44 -0
- package/dist/renderer/svgCanvas.js +6 -0
- package/dist/renderer/types.js +0 -0
- package/dist/tubeMap/axis.js +25 -0
- package/dist/tubeMap/coarsen.js +361 -0
- package/dist/tubeMap/connectors.js +13 -0
- package/dist/tubeMap/deviations.js +85 -0
- package/dist/tubeMap/draw.js +15 -0
- package/dist/tubeMap/frame.js +9 -0
- package/dist/tubeMap/genes.js +20 -0
- package/dist/tubeMap/mismatches.js +6 -0
- package/dist/tubeMap/nodeColors.js +21 -0
- package/dist/tubeMap/reads.js +17 -0
- package/dist/tubeMap/warp.js +8 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types.js +0 -0
- package/dist/util/SpatialIndex.js +13 -0
- package/dist/util/edgeCurves.js +10 -0
- package/dist/util/geometry.js +39 -0
- package/dist/util/hitDetection.js +20 -0
- package/dist/util/wheelZoom.js +6 -0
- package/dist/version.js +6 -0
- package/dist/viewport.js +33 -0
- package/dist/walkEncoding.js +39 -0
- package/dist/walkHighlight.js +26 -0
- package/dist/walkKey.js +17 -0
- package/package.json +38 -5
- package/dist/chunks/chunk-EXSFRX2F.js +0 -7400
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
el
|
|
3
|
+
} from "./chunk-NNODIW4U.js";
|
|
4
|
+
|
|
5
|
+
// src/genes/exonOutline.ts
|
|
6
|
+
var EXON_COLOR = "#daa520";
|
|
7
|
+
var EXON_GAP_PX = 1;
|
|
8
|
+
var EXON_LINE_PX = 2;
|
|
9
|
+
var MIN_LANE_PX = 4;
|
|
10
|
+
function exonStretches(pins, halfWidthPx, lift, pathOf = (d) => d) {
|
|
11
|
+
const inkPx = (nodeId) => {
|
|
12
|
+
const own = halfWidthPx(nodeId) * 2;
|
|
13
|
+
return lift?.nodeIds.has(nodeId) ? Math.max(own, lift.walks.length * MIN_LANE_PX) : own;
|
|
14
|
+
};
|
|
15
|
+
return pins.flatMap(
|
|
16
|
+
(pin) => pin.exonsByNode.map(({ nodeId, d }) => ({
|
|
17
|
+
key: `${pin.gene.name}-${pin.gene.start}-${nodeId}`,
|
|
18
|
+
d: pathOf(d),
|
|
19
|
+
inner: inkPx(nodeId) + 2 * EXON_GAP_PX
|
|
20
|
+
}))
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
function exonOutlineTree(stretches, o) {
|
|
24
|
+
if (stretches.length === 0) {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
const stroke = (s, color, width) => el("path", {
|
|
28
|
+
d: s.d,
|
|
29
|
+
fill: "none",
|
|
30
|
+
stroke: color,
|
|
31
|
+
"stroke-width": width,
|
|
32
|
+
"stroke-linecap": "round",
|
|
33
|
+
"stroke-linejoin": "round",
|
|
34
|
+
"vector-effect": o.transform ? "non-scaling-stroke" : void 0
|
|
35
|
+
});
|
|
36
|
+
return el(
|
|
37
|
+
"g",
|
|
38
|
+
{},
|
|
39
|
+
el(
|
|
40
|
+
"mask",
|
|
41
|
+
{
|
|
42
|
+
id: o.id,
|
|
43
|
+
maskUnits: "userSpaceOnUse",
|
|
44
|
+
x: 0,
|
|
45
|
+
y: 0,
|
|
46
|
+
width: o.width,
|
|
47
|
+
height: o.height
|
|
48
|
+
},
|
|
49
|
+
el(
|
|
50
|
+
"g",
|
|
51
|
+
{ transform: o.transform },
|
|
52
|
+
...stretches.map((s) => stroke(s, "#fff", s.inner + 2 * EXON_LINE_PX)),
|
|
53
|
+
...stretches.map((s) => stroke(s, "#000", s.inner))
|
|
54
|
+
)
|
|
55
|
+
),
|
|
56
|
+
el("rect", {
|
|
57
|
+
width: o.width,
|
|
58
|
+
height: o.height,
|
|
59
|
+
fill: EXON_COLOR,
|
|
60
|
+
mask: `url(#${o.id})`
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
EXON_COLOR,
|
|
67
|
+
exonStretches,
|
|
68
|
+
exonOutlineTree
|
|
69
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// src/renderer/svgCanvas.ts
|
|
2
|
+
var n = (v) => +v.toFixed(1);
|
|
3
|
+
function paint(css) {
|
|
4
|
+
const m = /^rgba?\(([^,]+),([^,]+),([^,)]+)(?:,([^)]+))?\)$/.exec(
|
|
5
|
+
css.replaceAll(" ", "")
|
|
6
|
+
);
|
|
7
|
+
if (!m) {
|
|
8
|
+
return { color: css, opacity: 1 };
|
|
9
|
+
}
|
|
10
|
+
const opacity = m[4] === void 0 ? 1 : Number(m[4]);
|
|
11
|
+
return { color: `rgb(${m[1]},${m[2]},${m[3]})`, opacity };
|
|
12
|
+
}
|
|
13
|
+
function paintAttrs(kind, css) {
|
|
14
|
+
const { color, opacity } = paint(css);
|
|
15
|
+
return `${kind}="${color}"${opacity < 1 ? ` ${kind}-opacity="${n(opacity)}"` : ""}`;
|
|
16
|
+
}
|
|
17
|
+
var DOT_PX = 0.5;
|
|
18
|
+
var DOT_CELL_PX = 0.25;
|
|
19
|
+
function svgCanvas(width, height) {
|
|
20
|
+
const out = [];
|
|
21
|
+
let subpaths = [];
|
|
22
|
+
const start = (x, y) => {
|
|
23
|
+
const s = { d: `M${n(x)} ${n(y)}`, segments: 0, x0: x, x1: x, y0: y, y1: y };
|
|
24
|
+
subpaths.push(s);
|
|
25
|
+
return s;
|
|
26
|
+
};
|
|
27
|
+
const reach = (points) => {
|
|
28
|
+
const s = subpaths.at(-1) ?? start(points[0], points[1]);
|
|
29
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
30
|
+
s.x0 = Math.min(s.x0, points[i]);
|
|
31
|
+
s.x1 = Math.max(s.x1, points[i]);
|
|
32
|
+
s.y0 = Math.min(s.y0, points[i + 1]);
|
|
33
|
+
s.y1 = Math.max(s.y1, points[i + 1]);
|
|
34
|
+
}
|
|
35
|
+
s.segments++;
|
|
36
|
+
return s;
|
|
37
|
+
};
|
|
38
|
+
const pathData = (dots) => {
|
|
39
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40
|
+
return subpaths.map((s) => {
|
|
41
|
+
if (!dots || s.x1 - s.x0 >= DOT_PX || s.y1 - s.y0 >= DOT_PX) {
|
|
42
|
+
return s.d;
|
|
43
|
+
}
|
|
44
|
+
const x = (s.x0 + s.x1) / 2;
|
|
45
|
+
const y = (s.y0 + s.y1) / 2;
|
|
46
|
+
const cell = `${Math.round(x / DOT_CELL_PX)} ${Math.round(y / DOT_CELL_PX)}`;
|
|
47
|
+
if (s.segments === 0 || seen.has(cell)) {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
seen.add(cell);
|
|
51
|
+
return `M${n(x)} ${n(y)}h0`;
|
|
52
|
+
}).join("");
|
|
53
|
+
};
|
|
54
|
+
const ctx = {
|
|
55
|
+
canvas: void 0,
|
|
56
|
+
fillStyle: "#000",
|
|
57
|
+
strokeStyle: "#000",
|
|
58
|
+
lineWidth: 1,
|
|
59
|
+
lineCap: "butt",
|
|
60
|
+
lineJoin: "miter",
|
|
61
|
+
beginPath() {
|
|
62
|
+
subpaths = [];
|
|
63
|
+
},
|
|
64
|
+
moveTo(x, y) {
|
|
65
|
+
start(x, y);
|
|
66
|
+
},
|
|
67
|
+
lineTo(x, y) {
|
|
68
|
+
reach([x, y]).d += `L${n(x)} ${n(y)}`;
|
|
69
|
+
},
|
|
70
|
+
bezierCurveTo(cx0, cy0, cx1, cy1, x, y) {
|
|
71
|
+
reach([cx0, cy0, cx1, cy1, x, y]).d += `C${n(cx0)} ${n(cy0)} ${n(cx1)} ${n(cy1)} ${n(x)} ${n(y)}`;
|
|
72
|
+
},
|
|
73
|
+
closePath() {
|
|
74
|
+
const s = subpaths.at(-1);
|
|
75
|
+
if (s) {
|
|
76
|
+
s.d += "Z";
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
stroke() {
|
|
80
|
+
const d = pathData(ctx.lineCap !== "butt");
|
|
81
|
+
if (d) {
|
|
82
|
+
out.push(
|
|
83
|
+
`<path d="${d}" fill="none" ${paintAttrs("stroke", ctx.strokeStyle)} stroke-width="${n(ctx.lineWidth)}" stroke-linecap="${ctx.lineCap}" stroke-linejoin="${ctx.lineJoin}"/>`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
fill() {
|
|
88
|
+
const d = pathData(false);
|
|
89
|
+
if (d) {
|
|
90
|
+
out.push(`<path d="${d}" ${paintAttrs("fill", ctx.fillStyle)}/>`);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
fillRect(x, y, w, h) {
|
|
94
|
+
out.push(
|
|
95
|
+
`<rect x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" ${paintAttrs("fill", ctx.fillStyle)}/>`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const canvas = {
|
|
100
|
+
width,
|
|
101
|
+
height,
|
|
102
|
+
style: {},
|
|
103
|
+
getContext: () => ctx
|
|
104
|
+
};
|
|
105
|
+
ctx.canvas = canvas;
|
|
106
|
+
return {
|
|
107
|
+
canvas,
|
|
108
|
+
// the drawing so far, as SVG elements in canvas pixels
|
|
109
|
+
markup: () => out.join("")
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
svgCanvas
|
|
115
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeEdgeCurves,
|
|
3
|
+
edgeJoinOf,
|
|
4
|
+
routedEdgeCurves,
|
|
5
|
+
strandSides
|
|
6
|
+
} from "./chunk-2CVHXFK7.js";
|
|
7
|
+
import {
|
|
8
|
+
firstNodeAtOrAfter,
|
|
9
|
+
isBackbone
|
|
10
|
+
} from "./chunk-FYQWPEEY.js";
|
|
11
|
+
|
|
12
|
+
// src/deletionEdges.ts
|
|
13
|
+
var MIN_DELETION_BP = 1;
|
|
14
|
+
function bypassedPoints(nodePositions, bypassed) {
|
|
15
|
+
return bypassed.flatMap((id) => nodePositions[id] ?? []);
|
|
16
|
+
}
|
|
17
|
+
function deletionArcCurves(nodePositions, deletion, axis, route, stranded = false) {
|
|
18
|
+
const from = nodePositions[deletion.from];
|
|
19
|
+
const to = nodePositions[deletion.to];
|
|
20
|
+
if (!from?.length || !to?.length) {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
return route ? routedEdgeCurves(
|
|
24
|
+
from,
|
|
25
|
+
to,
|
|
26
|
+
route,
|
|
27
|
+
stranded ? strandSides(deletion) : void 0
|
|
28
|
+
) : computeEdgeCurves(
|
|
29
|
+
from,
|
|
30
|
+
to,
|
|
31
|
+
edgeJoinOf(deletion, stranded),
|
|
32
|
+
0,
|
|
33
|
+
0,
|
|
34
|
+
axis,
|
|
35
|
+
bypassedPoints(nodePositions, deletion.bypassed)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
function backboneBySequence(graph) {
|
|
39
|
+
const bySequence = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const node of graph.nodes) {
|
|
41
|
+
if (isBackbone(node)) {
|
|
42
|
+
const list = bySequence.get(node.stable.refName);
|
|
43
|
+
if (list) {
|
|
44
|
+
list.push(node);
|
|
45
|
+
} else {
|
|
46
|
+
bySequence.set(node.stable.refName, [node]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const list of bySequence.values()) {
|
|
51
|
+
list.sort((a, b) => a.stable.start - b.stable.start);
|
|
52
|
+
}
|
|
53
|
+
return bySequence;
|
|
54
|
+
}
|
|
55
|
+
function bypassedNodes(nodes, start, end) {
|
|
56
|
+
const bypassed = [];
|
|
57
|
+
if (nodes) {
|
|
58
|
+
for (let i = firstNodeAtOrAfter(nodes, start); i < nodes.length; i++) {
|
|
59
|
+
const node = nodes[i];
|
|
60
|
+
if (node.stable.start >= end) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
if (node.stable.start + node.length <= end) {
|
|
64
|
+
bypassed.push(node.id);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return bypassed;
|
|
69
|
+
}
|
|
70
|
+
function skipsBetween(edge, from, to, forwards) {
|
|
71
|
+
const { fromStrand, toStrand } = edge;
|
|
72
|
+
if (fromStrand === void 0 || toStrand === void 0) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
const leavesRight = fromStrand === (from.stable.strand ?? "+");
|
|
76
|
+
const arrivesLeft = toStrand === (to.stable.strand ?? "+");
|
|
77
|
+
return forwards ? leavesRight && arrivesLeft : !leavesRight && !arrivesLeft;
|
|
78
|
+
}
|
|
79
|
+
function deletionEdges(graph) {
|
|
80
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
81
|
+
const backbone = backboneBySequence(graph);
|
|
82
|
+
const found = [];
|
|
83
|
+
for (let edgeIndex = 0; edgeIndex < graph.edges.length; edgeIndex++) {
|
|
84
|
+
const edge = graph.edges[edgeIndex];
|
|
85
|
+
const from = byId.get(edge.from);
|
|
86
|
+
const to = byId.get(edge.to);
|
|
87
|
+
if (from !== void 0 && to !== void 0 && isBackbone(from) && isBackbone(to) && from.stable.refName === to.stable.refName) {
|
|
88
|
+
const forwards = from.stable.start <= to.stable.start;
|
|
89
|
+
const [left, right] = forwards ? [from, to] : [to, from];
|
|
90
|
+
const start = left.stable.start + left.length;
|
|
91
|
+
const end = right.stable.start;
|
|
92
|
+
const bp = end - start;
|
|
93
|
+
if (bp >= MIN_DELETION_BP && skipsBetween(edge, from, to, forwards)) {
|
|
94
|
+
const refName = left.stable.refName;
|
|
95
|
+
found.push({
|
|
96
|
+
edgeIndex,
|
|
97
|
+
from: edge.from,
|
|
98
|
+
to: edge.to,
|
|
99
|
+
...edge.fromStrand ? { fromStrand: edge.fromStrand } : {},
|
|
100
|
+
...edge.toStrand ? { toStrand: edge.toStrand } : {},
|
|
101
|
+
refName,
|
|
102
|
+
start,
|
|
103
|
+
end,
|
|
104
|
+
bp,
|
|
105
|
+
bypassed: bypassedNodes(backbone.get(refName), start, end)
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return found;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
bypassedPoints,
|
|
115
|
+
deletionArcCurves,
|
|
116
|
+
deletionEdges
|
|
117
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SATURATED_PATH_COUNT
|
|
3
|
+
} from "./chunk-MFKORG3F.js";
|
|
4
|
+
import {
|
|
5
|
+
referenceOrder
|
|
6
|
+
} from "./chunk-HNQPEMYE.js";
|
|
7
|
+
import {
|
|
8
|
+
isBackbone
|
|
9
|
+
} from "./chunk-FYQWPEEY.js";
|
|
10
|
+
|
|
11
|
+
// src/bubbles/bubblesFromGraph.ts
|
|
12
|
+
function bubblesFromGraph(graph) {
|
|
13
|
+
if (!graph.nodes.some(isBackbone)) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
17
|
+
const order = referenceOrder(graph);
|
|
18
|
+
const indexOf = new Map(order.map((id, i) => [id, i]));
|
|
19
|
+
const reach = new Int32Array(order.length + 1);
|
|
20
|
+
const succ = /* @__PURE__ */ new Map();
|
|
21
|
+
const linked = /* @__PURE__ */ new Set();
|
|
22
|
+
for (const e of graph.edges) {
|
|
23
|
+
const a = indexOf.get(e.from);
|
|
24
|
+
const b = indexOf.get(e.to);
|
|
25
|
+
if (a === void 0 || b === void 0 || a === b) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
linked.add(`${e.from}>${e.to}`).add(`${e.to}>${e.from}`);
|
|
29
|
+
const [lo, hi] = a < b ? [a, b] : [b, a];
|
|
30
|
+
reach[lo + 1]++;
|
|
31
|
+
reach[hi]--;
|
|
32
|
+
const [from, to] = a < b ? [e.from, e.to] : [e.to, e.from];
|
|
33
|
+
(succ.get(from) ?? succ.set(from, /* @__PURE__ */ new Set()).get(from)).add(to);
|
|
34
|
+
}
|
|
35
|
+
for (let i = 1; i < reach.length; i++) {
|
|
36
|
+
reach[i] += reach[i - 1];
|
|
37
|
+
}
|
|
38
|
+
const backbone = graph.nodes.filter(isBackbone).sort((a, b) => a.stable.start - b.stable.start);
|
|
39
|
+
const ends = /* @__PURE__ */ new Set([backbone[0].id, backbone.at(-1).id]);
|
|
40
|
+
const boundaries = backbone.map((node) => [node, indexOf.get(node.id)]).filter(([node, i]) => ends.has(node.id) || reach[i] === 0).sort(([a, ia], [b, ib]) => ia - ib || a.stable.start - b.stable.start);
|
|
41
|
+
const walkIndex = graph.paths?.map((p) => {
|
|
42
|
+
const at = /* @__PURE__ */ new Map();
|
|
43
|
+
p.nodeIds.forEach((id, i) => {
|
|
44
|
+
if (!at.has(id)) {
|
|
45
|
+
at.set(id, i);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return at;
|
|
49
|
+
});
|
|
50
|
+
const bubbles = [];
|
|
51
|
+
for (let i = 0; i + 1 < boundaries.length; i++) {
|
|
52
|
+
const [start, i0] = boundaries[i];
|
|
53
|
+
const [end, i1] = boundaries[i + 1];
|
|
54
|
+
const interior = order.slice(i0 + 1, i1);
|
|
55
|
+
if (interior.length === 0) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const best = /* @__PURE__ */ new Map([[start.id, { min: 0, max: 0, n: 1 }]]);
|
|
59
|
+
for (const id of [start.id, ...interior]) {
|
|
60
|
+
const cur = best.get(id);
|
|
61
|
+
if (!cur) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
for (const t of succ.get(id) ?? []) {
|
|
65
|
+
const ti = indexOf.get(t);
|
|
66
|
+
if (ti > i1) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const add = t === end.id ? 0 : byId.get(t).length;
|
|
70
|
+
const prev = best.get(t);
|
|
71
|
+
best.set(t, {
|
|
72
|
+
min: Math.min(prev?.min ?? Infinity, cur.min + add),
|
|
73
|
+
max: Math.max(prev?.max ?? -Infinity, cur.max + add),
|
|
74
|
+
n: Math.min(SATURATED_PATH_COUNT, cur.n + (prev?.n ?? 0))
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const walked = walkIndex && walkRoutes(graph, byId, walkIndex, start.id, end.id);
|
|
79
|
+
const crossed = walked?.n ? walked : void 0;
|
|
80
|
+
const routes = crossed ?? best.get(end.id) ?? { min: 0, max: 0, n: 0 };
|
|
81
|
+
const walksLeave = walked !== void 0 && walked.left > 0;
|
|
82
|
+
const refStart = start.stable.start + start.length;
|
|
83
|
+
const refEnd = end.stable.start;
|
|
84
|
+
const chain = [
|
|
85
|
+
start.id,
|
|
86
|
+
...interior.filter((id) => isBackbone(byId.get(id))),
|
|
87
|
+
end.id
|
|
88
|
+
];
|
|
89
|
+
const chainBroken = chain.some(
|
|
90
|
+
(id, k) => k > 0 && !linked.has(`${chain[k - 1]}>${id}`)
|
|
91
|
+
);
|
|
92
|
+
const partial = chainBroken || walksLeave;
|
|
93
|
+
const fallback = chainBroken && !crossed;
|
|
94
|
+
bubbles.push({
|
|
95
|
+
refName: start.stable.refName,
|
|
96
|
+
start: refStart,
|
|
97
|
+
end: refEnd,
|
|
98
|
+
segmentCount: interior.length + 2,
|
|
99
|
+
pathCount: routes.n,
|
|
100
|
+
// a real inversion test needs each walk's direction relative to the
|
|
101
|
+
// reference; the first visit's strand is just whichever path anchored the
|
|
102
|
+
// node
|
|
103
|
+
inversion: false,
|
|
104
|
+
shortestAlleleLength: fallback ? refEnd - refStart : routes.min,
|
|
105
|
+
longestAlleleLength: fallback ? refEnd - refStart : routes.max,
|
|
106
|
+
segments: [start, ...interior.map((id) => byId.get(id)), end].map((n) => n.name).join(","),
|
|
107
|
+
shortestAllele: void 0,
|
|
108
|
+
longestAllele: void 0,
|
|
109
|
+
partial,
|
|
110
|
+
routes: crossed?.routes
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return bubbles;
|
|
114
|
+
}
|
|
115
|
+
function walkRoutes(graph, byId, walkIndex, startId, endId) {
|
|
116
|
+
const seen = /* @__PURE__ */ new Map();
|
|
117
|
+
let min = Infinity;
|
|
118
|
+
let max = -Infinity;
|
|
119
|
+
let left = 0;
|
|
120
|
+
graph.paths.forEach((p, k) => {
|
|
121
|
+
const i0 = walkIndex[k].get(startId);
|
|
122
|
+
const i1 = walkIndex[k].get(endId);
|
|
123
|
+
if (i0 === void 0 || i1 === void 0) {
|
|
124
|
+
if (i0 !== void 0 || i1 !== void 0) {
|
|
125
|
+
left++;
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const steps = p.nodeIds.slice(Math.min(i0, i1) + 1, Math.max(i0, i1));
|
|
130
|
+
if (i1 < i0) {
|
|
131
|
+
steps.reverse();
|
|
132
|
+
}
|
|
133
|
+
let bp = 0;
|
|
134
|
+
for (const id of steps) {
|
|
135
|
+
bp += byId.get(id)?.length ?? 0;
|
|
136
|
+
}
|
|
137
|
+
const key = steps.join(",");
|
|
138
|
+
const route = seen.get(key) ?? { steps, bp, walks: [] };
|
|
139
|
+
route.walks.push(p.name);
|
|
140
|
+
seen.set(key, route);
|
|
141
|
+
min = Math.min(min, bp);
|
|
142
|
+
max = Math.max(max, bp);
|
|
143
|
+
});
|
|
144
|
+
return seen.size || left ? { min, max, n: seen.size, left, routes: [...seen.values()] } : void 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
bubblesFromGraph
|
|
149
|
+
};
|