@jbrowse/bandage-core 6.0.1 → 6.0.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/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-3C2LGJKY.js +108 -0
- package/dist/chunks/chunk-3PLYZLYV.js +513 -0
- package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
- package/dist/chunks/chunk-3WHKR5TY.js +726 -0
- package/dist/chunks/chunk-47DYCZOK.js +115 -0
- package/dist/chunks/chunk-4S47XRYA.js +149 -0
- package/dist/chunks/chunk-527AI7CP.js +122 -0
- package/dist/chunks/chunk-57ZIXHZA.js +90 -0
- package/dist/chunks/chunk-5QT6NPSO.js +40 -0
- package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
- package/dist/chunks/chunk-6Q6HFBJT.js +170 -0
- package/dist/chunks/chunk-6SBUFFTB.js +97 -0
- package/dist/chunks/chunk-6YRRNX2P.js +61 -0
- package/dist/chunks/chunk-72N5DJHZ.js +155 -0
- package/dist/chunks/chunk-73RSLGZT.js +73 -0
- package/dist/chunks/chunk-7CHJKABZ.js +82 -0
- package/dist/chunks/chunk-7XHHVYG7.js +13 -0
- package/dist/chunks/chunk-AB35TJST.js +67 -0
- package/dist/chunks/chunk-AE3UQZY3.js +43 -0
- package/dist/chunks/chunk-BJV2W4KZ.js +257 -0
- package/dist/chunks/chunk-BPLIXMH7.js +52 -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-CPMHA4W5.js +6 -0
- package/dist/chunks/chunk-CR7EGAFE.js +241 -0
- package/dist/chunks/chunk-CZ54VG2H.js +431 -0
- package/dist/chunks/chunk-D3M7SBKL.js +210 -0
- package/dist/chunks/chunk-EJ5OZTCK.js +52 -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-GQ2AOHU4.js +112 -0
- package/dist/chunks/chunk-HB6PDCOU.js +177 -0
- package/dist/chunks/chunk-HCANBJDD.js +326 -0
- package/dist/chunks/chunk-HIJVRRYE.js +174 -0
- package/dist/chunks/chunk-HNQPEMYE.js +297 -0
- package/dist/chunks/chunk-IGVV37VI.js +60 -0
- package/dist/chunks/chunk-IMW3AK27.js +102 -0
- package/dist/chunks/chunk-JKJWSY4V.js +32 -0
- package/dist/chunks/chunk-KCW7L3NV.js +6 -0
- package/dist/chunks/chunk-KQFOCDFY.js +73 -0
- package/dist/chunks/chunk-L3O7AWLI.js +52 -0
- package/dist/chunks/chunk-L5RYUPZI.js +53 -0
- package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
- package/dist/chunks/chunk-LTUWJKHD.js +112 -0
- package/dist/chunks/chunk-LVZZH2NI.js +181 -0
- package/dist/chunks/chunk-MFKORG3F.js +44 -0
- package/dist/chunks/chunk-MHGHYHTL.js +205 -0
- package/dist/chunks/chunk-MI35EUIE.js +76 -0
- package/dist/chunks/chunk-MNUSQCLG.js +29 -0
- package/dist/chunks/chunk-NNODIW4U.js +26 -0
- package/dist/chunks/chunk-P7B3GDAY.js +232 -0
- package/dist/chunks/chunk-PBENCR5L.js +60 -0
- package/dist/chunks/chunk-PUG7LXDF.js +70 -0
- package/dist/chunks/chunk-Q3KCTA4N.js +202 -0
- package/dist/chunks/chunk-QCYM44UL.js +476 -0
- package/dist/chunks/chunk-QYBZZI5H.js +0 -0
- package/dist/chunks/chunk-RAKOB6IV.js +169 -0
- package/dist/chunks/chunk-RVS2QCOX.js +135 -0
- package/dist/chunks/chunk-SDGM243E.js +190 -0
- package/dist/chunks/chunk-ST2IBUAN.js +119 -0
- package/dist/chunks/chunk-SV27AJCC.js +101 -0
- package/dist/chunks/chunk-SZNZ2T6D.js +198 -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-VKB57ZYE.js +218 -0
- package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
- package/dist/chunks/chunk-WOO7QYKB.js +121 -0
- package/dist/chunks/chunk-WSIFAZU7.js +132 -0
- package/dist/chunks/chunk-X32PABB7.js +117 -0
- package/dist/chunks/chunk-YJQ7CT7F.js +23 -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,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildNeighbors
|
|
3
|
+
} from "./chunk-ES2N6V56.js";
|
|
4
|
+
import {
|
|
5
|
+
REFERENCE_RANK,
|
|
6
|
+
isAnchored
|
|
7
|
+
} from "./chunk-FYQWPEEY.js";
|
|
8
|
+
|
|
9
|
+
// src/alleleProjection/projectAlleles.ts
|
|
10
|
+
function parsePanSN(refName) {
|
|
11
|
+
const parts = refName.split("#");
|
|
12
|
+
const haplotype = parts.length >= 2 ? Number(parts[1]) : Number.NaN;
|
|
13
|
+
return {
|
|
14
|
+
sample: parts[0] ?? refName,
|
|
15
|
+
haplotype: Number.isFinite(haplotype) ? haplotype : void 0
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function collectRun(seed, byId, adj, claimed) {
|
|
19
|
+
const members = [];
|
|
20
|
+
const anchors = [];
|
|
21
|
+
const queue = [seed];
|
|
22
|
+
claimed.add(seed);
|
|
23
|
+
while (queue.length > 0) {
|
|
24
|
+
const id = queue.pop();
|
|
25
|
+
const node = byId.get(id);
|
|
26
|
+
if (node) {
|
|
27
|
+
members.push(node);
|
|
28
|
+
for (const neighborId of adj.get(id) ?? []) {
|
|
29
|
+
const neighbor = byId.get(neighborId);
|
|
30
|
+
if (neighbor) {
|
|
31
|
+
if (neighbor.stable.rank === REFERENCE_RANK) {
|
|
32
|
+
anchors.push(neighbor);
|
|
33
|
+
} else if (!claimed.has(neighborId)) {
|
|
34
|
+
claimed.add(neighborId);
|
|
35
|
+
queue.push(neighborId);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { members, anchors };
|
|
42
|
+
}
|
|
43
|
+
function anchorSpan(anchors) {
|
|
44
|
+
const refNames = new Set(anchors.map((a) => a.stable.refName));
|
|
45
|
+
if (refNames.size !== 1 || anchors.length < 2) {
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
const sorted = [...anchors].sort((a, b) => a.stable.start - b.stable.start);
|
|
49
|
+
const first = sorted[0];
|
|
50
|
+
const last = sorted.at(-1);
|
|
51
|
+
const start = first.stable.start + first.length;
|
|
52
|
+
const end = last.stable.start;
|
|
53
|
+
return end >= start ? { start, end, entry: first } : void 0;
|
|
54
|
+
}
|
|
55
|
+
function runDistances(members, entry, adj) {
|
|
56
|
+
const byId = new Map(members.map((m) => [m.id, m]));
|
|
57
|
+
const dist = /* @__PURE__ */ new Map();
|
|
58
|
+
const queue = [];
|
|
59
|
+
for (const neighborId of adj.get(entry.id) ?? []) {
|
|
60
|
+
if (byId.has(neighborId)) {
|
|
61
|
+
dist.set(neighborId, 0);
|
|
62
|
+
queue.push(neighborId);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (queue.length === 0) {
|
|
66
|
+
for (const member of members) {
|
|
67
|
+
dist.set(member.id, 0);
|
|
68
|
+
queue.push(member.id);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const id of queue) {
|
|
72
|
+
const reach = dist.get(id) + byId.get(id).length;
|
|
73
|
+
for (const neighborId of adj.get(id) ?? []) {
|
|
74
|
+
if (byId.has(neighborId)) {
|
|
75
|
+
const known = dist.get(neighborId);
|
|
76
|
+
if (known === void 0 || reach < known) {
|
|
77
|
+
dist.set(neighborId, reach);
|
|
78
|
+
queue.push(neighborId);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return dist;
|
|
84
|
+
}
|
|
85
|
+
function runOffsets(members, entry, adj) {
|
|
86
|
+
const dist = runDistances(members, entry, adj);
|
|
87
|
+
const ordered = [...members].sort((a, b) => dist.get(a.id) - dist.get(b.id));
|
|
88
|
+
const rank = new Map(ordered.map((m, i) => [m.id, i]));
|
|
89
|
+
const byId = new Map(members.map((m) => [m.id, m]));
|
|
90
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const [i, member] of ordered.entries()) {
|
|
92
|
+
let offset = 0;
|
|
93
|
+
for (const neighborId of adj.get(member.id) ?? []) {
|
|
94
|
+
const neighbor = byId.get(neighborId);
|
|
95
|
+
if (neighbor && rank.get(neighborId) < i) {
|
|
96
|
+
offset = Math.max(offset, offsets.get(neighborId) + neighbor.length);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
offsets.set(member.id, offset);
|
|
100
|
+
}
|
|
101
|
+
return { offsets, ordered };
|
|
102
|
+
}
|
|
103
|
+
function projectAlleles(graph) {
|
|
104
|
+
const byId = new Map(
|
|
105
|
+
graph.nodes.filter(isAnchored).map((n) => [n.id, n])
|
|
106
|
+
);
|
|
107
|
+
const adj = buildNeighbors(graph);
|
|
108
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
109
|
+
const alleles = [];
|
|
110
|
+
for (const node of byId.values()) {
|
|
111
|
+
if (node.stable.rank !== REFERENCE_RANK && !claimed.has(node.id)) {
|
|
112
|
+
const { members, anchors } = collectRun(node.id, byId, adj, claimed);
|
|
113
|
+
const span = anchorSpan(anchors);
|
|
114
|
+
if (span) {
|
|
115
|
+
const { offsets, ordered } = runOffsets(members, span.entry, adj);
|
|
116
|
+
alleles.push({
|
|
117
|
+
start: span.start,
|
|
118
|
+
refSpan: span.end - span.start,
|
|
119
|
+
nodeIds: ordered.map((m) => m.id),
|
|
120
|
+
nodeOffsets: ordered.map((m) => offsets.get(m.id)),
|
|
121
|
+
pathLength: ordered.reduce(
|
|
122
|
+
(max, m) => Math.max(max, offsets.get(m.id) + m.length),
|
|
123
|
+
0
|
|
124
|
+
)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return alleles.sort((a, b) => a.start - b.start);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
parsePanSN,
|
|
134
|
+
projectAlleles
|
|
135
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ownStrand,
|
|
3
|
+
strandSides
|
|
4
|
+
} from "./chunk-3PLYZLYV.js";
|
|
5
|
+
import {
|
|
6
|
+
isBackbone
|
|
7
|
+
} from "./chunk-FYQWPEEY.js";
|
|
8
|
+
|
|
9
|
+
// src/layout/mergeRuns.ts
|
|
10
|
+
function flip(strand) {
|
|
11
|
+
return strand === "+" ? "-" : "+";
|
|
12
|
+
}
|
|
13
|
+
function mergeRuns(graph, alone = /* @__PURE__ */ new Set()) {
|
|
14
|
+
const sides = graph.edges.map(strandSides);
|
|
15
|
+
const at = /* @__PURE__ */ new Map();
|
|
16
|
+
const attach = (id, side, ei) => {
|
|
17
|
+
const key = `${id}|${side}`;
|
|
18
|
+
const list = at.get(key);
|
|
19
|
+
if (list) {
|
|
20
|
+
list.push(ei);
|
|
21
|
+
} else {
|
|
22
|
+
at.set(key, [ei]);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
graph.edges.forEach((edge, ei) => {
|
|
26
|
+
attach(edge.from, sides[ei].from, ei);
|
|
27
|
+
attach(edge.to, sides[ei].to, ei);
|
|
28
|
+
});
|
|
29
|
+
const joinAfter = (a) => {
|
|
30
|
+
const out = at.get(`${a}|end`);
|
|
31
|
+
if (out?.length !== 1) {
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
const ei = out[0];
|
|
35
|
+
const edge = graph.edges[ei];
|
|
36
|
+
return edge.from === a && edge.to !== a && !alone.has(a) && !alone.has(edge.to) && sides[ei].from === "end" && sides[ei].to === "start" && at.get(`${edge.to}|start`)?.length === 1 ? ei : void 0;
|
|
37
|
+
};
|
|
38
|
+
const joined = /* @__PURE__ */ new Set();
|
|
39
|
+
const runOf = /* @__PURE__ */ new Map();
|
|
40
|
+
const runs = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const node of graph.nodes) {
|
|
42
|
+
const into = at.get(`${node.id}|start`);
|
|
43
|
+
const pred = into?.length === 1 ? graph.edges[into[0]].from : void 0;
|
|
44
|
+
if (runOf.has(node.id) || pred !== void 0 && joinAfter(pred) === into[0]) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const run = [node.id];
|
|
48
|
+
for (; ; ) {
|
|
49
|
+
const ei = joinAfter(run.at(-1));
|
|
50
|
+
const next = ei === void 0 ? void 0 : graph.edges[ei].to;
|
|
51
|
+
if (next === void 0 || runOf.has(next)) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
joined.add(ei);
|
|
55
|
+
run.push(next);
|
|
56
|
+
}
|
|
57
|
+
for (const id of run) {
|
|
58
|
+
runOf.set(id, node.id);
|
|
59
|
+
}
|
|
60
|
+
if (run.length > 1) {
|
|
61
|
+
runs.set(node.id, run);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const node of graph.nodes) {
|
|
65
|
+
if (!runOf.has(node.id)) {
|
|
66
|
+
runOf.set(node.id, node.id);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
70
|
+
const nodes = [];
|
|
71
|
+
for (const node of graph.nodes) {
|
|
72
|
+
if (runOf.get(node.id) !== node.id) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const run = runs.get(node.id);
|
|
76
|
+
if (!run) {
|
|
77
|
+
nodes.push(node);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const members = run.map((id) => byId.get(id));
|
|
81
|
+
const backbone = members.filter(isBackbone);
|
|
82
|
+
const first = backbone.length ? backbone.reduce((a, b) => a.stable.start <= b.stable.start ? a : b) : members[0];
|
|
83
|
+
nodes.push({
|
|
84
|
+
id: node.id,
|
|
85
|
+
name: node.name,
|
|
86
|
+
length: members.reduce((sum, n) => sum + n.length, 0),
|
|
87
|
+
depth: node.depth,
|
|
88
|
+
stable: first.stable
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const seen = /* @__PURE__ */ new Set();
|
|
92
|
+
const edges = [];
|
|
93
|
+
graph.edges.forEach((edge, ei) => {
|
|
94
|
+
const from = runOf.get(edge.from);
|
|
95
|
+
const to = runOf.get(edge.to);
|
|
96
|
+
const side = sides[ei];
|
|
97
|
+
const key = `${from}|${side.from}>${to}|${side.to}`;
|
|
98
|
+
if (joined.has(ei) || seen.has(key)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
seen.add(key);
|
|
102
|
+
edges.push({
|
|
103
|
+
from,
|
|
104
|
+
to,
|
|
105
|
+
...side.from === "start" ? { fromStrand: flip(ownStrand(from)) } : {},
|
|
106
|
+
...side.to === "end" ? { toStrand: flip(ownStrand(to)) } : {}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
graph: { ...graph, nodes, edges, paths: void 0, pathVisits: void 0 },
|
|
111
|
+
runs
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function splitRuns(positions, runs, weightOf) {
|
|
115
|
+
const out = { ...positions };
|
|
116
|
+
for (const [id, members] of runs) {
|
|
117
|
+
const line = positions[id];
|
|
118
|
+
if (!line?.length) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
delete out[id];
|
|
122
|
+
const arc = arcPrefix(line);
|
|
123
|
+
const total = arc.at(-1);
|
|
124
|
+
const weights = members.map((m) => Math.max(weightOf(m), 0));
|
|
125
|
+
const weightSum = weights.reduce((a, b) => a + b, 0) || members.length;
|
|
126
|
+
let from = 0;
|
|
127
|
+
members.forEach((member, k) => {
|
|
128
|
+
const share = weightSum ? weights[k] / weightSum * total : total / members.length;
|
|
129
|
+
const to = k === members.length - 1 ? total : from + share;
|
|
130
|
+
out[member] = slice(line, arc, from, to);
|
|
131
|
+
from = to;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
function polylineSlice(line, from, to) {
|
|
137
|
+
if (line.length === 0) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const arc = arcPrefix(line);
|
|
141
|
+
const total = arc.at(-1);
|
|
142
|
+
return slice(line, arc, from * total, to * total);
|
|
143
|
+
}
|
|
144
|
+
function polylineMidpoint(line) {
|
|
145
|
+
const arc = arcPrefix(line);
|
|
146
|
+
return pointAt(line, arc, arc.at(-1) / 2);
|
|
147
|
+
}
|
|
148
|
+
function arcPrefix(line) {
|
|
149
|
+
const arc = [0];
|
|
150
|
+
for (let i = 1; i < line.length; i++) {
|
|
151
|
+
arc.push(
|
|
152
|
+
arc[i - 1] + Math.hypot(line[i].x - line[i - 1].x, line[i].y - line[i - 1].y)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return arc;
|
|
156
|
+
}
|
|
157
|
+
function pointAt(line, arc, s) {
|
|
158
|
+
if (s <= 0) {
|
|
159
|
+
return { ...line[0] };
|
|
160
|
+
}
|
|
161
|
+
let i = 1;
|
|
162
|
+
while (i < arc.length && arc[i] < s) {
|
|
163
|
+
i++;
|
|
164
|
+
}
|
|
165
|
+
if (i >= arc.length) {
|
|
166
|
+
return { ...line.at(-1) };
|
|
167
|
+
}
|
|
168
|
+
const span = arc[i] - arc[i - 1];
|
|
169
|
+
const t = span > 0 ? (s - arc[i - 1]) / span : 0;
|
|
170
|
+
const a = line[i - 1];
|
|
171
|
+
const b = line[i];
|
|
172
|
+
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
|
|
173
|
+
}
|
|
174
|
+
function slice(line, arc, from, to) {
|
|
175
|
+
const points = [pointAt(line, arc, from)];
|
|
176
|
+
for (let i = 0; i < line.length; i++) {
|
|
177
|
+
if (arc[i] > from && arc[i] < to) {
|
|
178
|
+
points.push({ ...line[i] });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
points.push(pointAt(line, arc, to));
|
|
182
|
+
return points;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export {
|
|
186
|
+
mergeRuns,
|
|
187
|
+
splitRuns,
|
|
188
|
+
polylineSlice,
|
|
189
|
+
polylineMidpoint
|
|
190
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
panSNSample
|
|
3
|
+
} from "./chunk-BPLIXMH7.js";
|
|
4
|
+
import {
|
|
5
|
+
REFERENCE_RANK
|
|
6
|
+
} from "./chunk-FYQWPEEY.js";
|
|
7
|
+
|
|
8
|
+
// src/pathAnchoring.ts
|
|
9
|
+
var RANGE_SUFFIX = /:(\d+)-\d+$/;
|
|
10
|
+
function pathOrigin(pathName) {
|
|
11
|
+
const match = RANGE_SUFFIX.exec(pathName);
|
|
12
|
+
return match ? { name: pathName.slice(0, match.index), start: +match[1] } : { name: pathName, start: 0 };
|
|
13
|
+
}
|
|
14
|
+
function surveyPaths(paths, lengthOf) {
|
|
15
|
+
const anchorPaths = [];
|
|
16
|
+
const pathVisits = /* @__PURE__ */ new Map();
|
|
17
|
+
for (const path of paths) {
|
|
18
|
+
const sample = panSNSample(path.name);
|
|
19
|
+
let pos = path.start;
|
|
20
|
+
for (const step of path.steps) {
|
|
21
|
+
const visit = {
|
|
22
|
+
path: path.name,
|
|
23
|
+
sample,
|
|
24
|
+
start: pos,
|
|
25
|
+
strand: step.strand
|
|
26
|
+
};
|
|
27
|
+
const existing = pathVisits.get(step.id);
|
|
28
|
+
if (existing) {
|
|
29
|
+
existing.push(visit);
|
|
30
|
+
} else {
|
|
31
|
+
pathVisits.set(step.id, [visit]);
|
|
32
|
+
}
|
|
33
|
+
pos += lengthOf(step.id);
|
|
34
|
+
}
|
|
35
|
+
anchorPaths.push({
|
|
36
|
+
name: path.name,
|
|
37
|
+
sample,
|
|
38
|
+
start: path.start,
|
|
39
|
+
length: pos - path.start
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return { anchorPaths, pathVisits };
|
|
43
|
+
}
|
|
44
|
+
function chooseReferencePath(anchorPaths, preferred) {
|
|
45
|
+
const named = anchorPaths.find(
|
|
46
|
+
(p) => p.sample === preferred || p.name === preferred
|
|
47
|
+
);
|
|
48
|
+
return named ?? anchorPaths[0];
|
|
49
|
+
}
|
|
50
|
+
var OFF_REFERENCE_RANK = 1;
|
|
51
|
+
function anchorNode(node, visits, reference) {
|
|
52
|
+
let anchored = node;
|
|
53
|
+
if (visits && visits.length > 0) {
|
|
54
|
+
const anchor = visits.find((v) => v.path === reference) ?? visits[0];
|
|
55
|
+
anchored = {
|
|
56
|
+
...node,
|
|
57
|
+
stable: {
|
|
58
|
+
refName: anchor.path,
|
|
59
|
+
start: anchor.start,
|
|
60
|
+
rank: anchor.path === reference ? REFERENCE_RANK : OFF_REFERENCE_RANK,
|
|
61
|
+
strand: anchor.strand
|
|
62
|
+
},
|
|
63
|
+
samples: [...new Set(visits.map((v) => v.sample))].sort()
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return anchored;
|
|
67
|
+
}
|
|
68
|
+
function anchorFromPaths(graph, preferred) {
|
|
69
|
+
const { anchorPaths, pathVisits } = graph;
|
|
70
|
+
const reference = anchorPaths && pathVisits ? chooseReferencePath(anchorPaths, preferred) : void 0;
|
|
71
|
+
return reference && pathVisits ? {
|
|
72
|
+
...graph,
|
|
73
|
+
nodes: graph.nodes.map(
|
|
74
|
+
(node) => anchorNode(node, pathVisits.get(node.name), reference.name)
|
|
75
|
+
),
|
|
76
|
+
anchoredBy: "paths",
|
|
77
|
+
referencePath: reference.name
|
|
78
|
+
} : graph;
|
|
79
|
+
}
|
|
80
|
+
function anchorGraph(graph, preferred) {
|
|
81
|
+
return graph.anchoredBy === "tags" ? graph : anchorFromPaths(graph, preferred);
|
|
82
|
+
}
|
|
83
|
+
function trimOrigins(graph, kept) {
|
|
84
|
+
if (!graph.anchorPaths) {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
|
|
88
|
+
const bp = (ids, end = ids.length) => {
|
|
89
|
+
let sum = 0;
|
|
90
|
+
for (let i = 0; i < end; i++) {
|
|
91
|
+
sum += lengthOf.get(ids[i]) ?? 0;
|
|
92
|
+
}
|
|
93
|
+
return sum;
|
|
94
|
+
};
|
|
95
|
+
const byOrigin = /* @__PURE__ */ new Map();
|
|
96
|
+
for (const path of graph.paths ?? []) {
|
|
97
|
+
const origin = pathOrigin(path.name).name;
|
|
98
|
+
byOrigin.set(origin, [...byOrigin.get(origin) ?? [], path]);
|
|
99
|
+
}
|
|
100
|
+
return graph.anchorPaths.map((o) => {
|
|
101
|
+
const pieces = byOrigin.get(o.name);
|
|
102
|
+
const path = pieces?.length === 1 ? pieces[0] : void 0;
|
|
103
|
+
const stretch = path && kept.get(path.name);
|
|
104
|
+
return path && stretch ? {
|
|
105
|
+
...o,
|
|
106
|
+
start: o.start + bp(path.nodeIds, stretch.first),
|
|
107
|
+
length: bp(stretch.nodeIds)
|
|
108
|
+
} : o;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
pathOrigin,
|
|
114
|
+
surveyPaths,
|
|
115
|
+
chooseReferencePath,
|
|
116
|
+
anchorFromPaths,
|
|
117
|
+
anchorGraph,
|
|
118
|
+
trimOrigins
|
|
119
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
polylineSlice
|
|
3
|
+
} from "./chunk-SDGM243E.js";
|
|
4
|
+
import {
|
|
5
|
+
svgPath
|
|
6
|
+
} from "./chunk-3PLYZLYV.js";
|
|
7
|
+
import {
|
|
8
|
+
firstNodeAtOrAfter,
|
|
9
|
+
isAnchored
|
|
10
|
+
} from "./chunk-FYQWPEEY.js";
|
|
11
|
+
|
|
12
|
+
// src/genes/genePins.ts
|
|
13
|
+
function genePins(graph, genes, positions) {
|
|
14
|
+
const byRefName = /* @__PURE__ */ new Map();
|
|
15
|
+
for (const node of graph.nodes) {
|
|
16
|
+
if (isAnchored(node)) {
|
|
17
|
+
const { refName } = node.stable;
|
|
18
|
+
if (positions[node.id]?.length) {
|
|
19
|
+
const entry = byRefName.get(refName) ?? byRefName.set(refName, { nodes: [], reach: 0 }).get(refName);
|
|
20
|
+
entry.nodes.push(node);
|
|
21
|
+
entry.reach = Math.max(entry.reach, node.length);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const { nodes } of byRefName.values()) {
|
|
26
|
+
nodes.sort((a, b) => a.stable.start - b.stable.start);
|
|
27
|
+
}
|
|
28
|
+
const pins = [];
|
|
29
|
+
for (const gene of genes) {
|
|
30
|
+
const onRefName = byRefName.get(gene.refName);
|
|
31
|
+
if (!onRefName) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const parts = [];
|
|
35
|
+
const exonsByNode = [];
|
|
36
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
37
|
+
let at;
|
|
38
|
+
let atDistance = Infinity;
|
|
39
|
+
let covered = 0;
|
|
40
|
+
const mid = (gene.start + gene.end) / 2;
|
|
41
|
+
const { nodes, reach } = onRefName;
|
|
42
|
+
for (let i = firstNodeAtOrAfter(nodes, gene.start - reach); i < nodes.length && nodes[i].stable.start < gene.end; i++) {
|
|
43
|
+
const node = nodes[i];
|
|
44
|
+
const nodeStart = node.stable.start;
|
|
45
|
+
const nodeEnd = nodeStart + node.length;
|
|
46
|
+
if (nodeEnd <= gene.start) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const line = positions[node.id];
|
|
50
|
+
nodeIds.add(node.id);
|
|
51
|
+
covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start);
|
|
52
|
+
const nodeParts = [];
|
|
53
|
+
for (const exon of gene.exons) {
|
|
54
|
+
const a = Math.max(exon.start, nodeStart);
|
|
55
|
+
const b = Math.min(exon.end, nodeEnd);
|
|
56
|
+
if (b <= a) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const stretch = polylineSlice(
|
|
60
|
+
line,
|
|
61
|
+
(a - nodeStart) / node.length,
|
|
62
|
+
(b - nodeStart) / node.length
|
|
63
|
+
);
|
|
64
|
+
if (stretch.length === 1) {
|
|
65
|
+
stretch.push({ ...stretch[0] });
|
|
66
|
+
}
|
|
67
|
+
nodeParts.push(svgPath(stretch));
|
|
68
|
+
}
|
|
69
|
+
if (nodeParts.length > 0) {
|
|
70
|
+
parts.push(...nodeParts);
|
|
71
|
+
exonsByNode.push({ nodeId: node.id, d: nodeParts.join("") });
|
|
72
|
+
}
|
|
73
|
+
const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd);
|
|
74
|
+
const distance = Math.abs(pinBp - mid);
|
|
75
|
+
if (distance < atDistance) {
|
|
76
|
+
atDistance = distance;
|
|
77
|
+
const [p] = polylineSlice(
|
|
78
|
+
line,
|
|
79
|
+
(pinBp - nodeStart) / node.length,
|
|
80
|
+
(pinBp - nodeStart) / node.length
|
|
81
|
+
);
|
|
82
|
+
at = p;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (at) {
|
|
86
|
+
pins.push({
|
|
87
|
+
gene,
|
|
88
|
+
exons: parts.join(""),
|
|
89
|
+
exonsByNode,
|
|
90
|
+
nodeIds,
|
|
91
|
+
at,
|
|
92
|
+
covered: covered / (gene.end - gene.start)
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return pins;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
genePins
|
|
101
|
+
};
|