@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,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBackbone
|
|
3
|
+
} from "./chunk-FYQWPEEY.js";
|
|
4
|
+
|
|
5
|
+
// src/layout/orientToReference.ts
|
|
6
|
+
function orientToReference(graph, positions) {
|
|
7
|
+
const backbone = graph.nodes.filter(
|
|
8
|
+
(node) => isBackbone(node) && (positions[node.id]?.length ?? 0) > 0
|
|
9
|
+
);
|
|
10
|
+
if (backbone.length < 2) {
|
|
11
|
+
return positions;
|
|
12
|
+
}
|
|
13
|
+
const points = backbone.map((node) => {
|
|
14
|
+
const segments = positions[node.id];
|
|
15
|
+
const mid = segments[Math.floor(segments.length / 2)];
|
|
16
|
+
return {
|
|
17
|
+
x: mid.x,
|
|
18
|
+
y: mid.y,
|
|
19
|
+
r: node.stable.start + node.length / 2,
|
|
20
|
+
w: node.length
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
const weight = points.reduce((sum, p) => sum + p.w, 0);
|
|
24
|
+
const cx = points.reduce((sum, p) => sum + p.x * p.w, 0) / weight;
|
|
25
|
+
const cy = points.reduce((sum, p) => sum + p.y * p.w, 0) / weight;
|
|
26
|
+
const cr = points.reduce((sum, p) => sum + p.r * p.w, 0) / weight;
|
|
27
|
+
let sxr = 0;
|
|
28
|
+
let syr = 0;
|
|
29
|
+
for (const p of points) {
|
|
30
|
+
sxr += p.w * (p.x - cx) * (p.r - cr);
|
|
31
|
+
syr += p.w * (p.y - cy) * (p.r - cr);
|
|
32
|
+
}
|
|
33
|
+
const angle = -Math.atan2(syr, sxr);
|
|
34
|
+
const c = Math.cos(angle);
|
|
35
|
+
const s = Math.sin(angle);
|
|
36
|
+
const out = {};
|
|
37
|
+
for (const [id, segments] of Object.entries(positions)) {
|
|
38
|
+
out[id] = segments.map((p) => {
|
|
39
|
+
const x = p.x - cx;
|
|
40
|
+
const y = p.y - cy;
|
|
41
|
+
return { x: cx + c * x - s * y, y: cy + s * x + c * y };
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
let below = 0;
|
|
45
|
+
let above = 0;
|
|
46
|
+
for (const node of graph.nodes) {
|
|
47
|
+
if (isBackbone(node)) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
for (const p of out[node.id] ?? []) {
|
|
51
|
+
if (p.y > cy) {
|
|
52
|
+
below++;
|
|
53
|
+
} else {
|
|
54
|
+
above++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (above > below) {
|
|
59
|
+
for (const segments of Object.values(out)) {
|
|
60
|
+
for (const p of segments) {
|
|
61
|
+
p.y = 2 * cy - p.y;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
orientToReference
|
|
70
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicalStrand,
|
|
3
|
+
readColor,
|
|
4
|
+
stepName,
|
|
5
|
+
tubeMapReads
|
|
6
|
+
} from "./chunk-TEDQJPMI.js";
|
|
7
|
+
import {
|
|
8
|
+
pathCssColor,
|
|
9
|
+
pathGreyCssColor
|
|
10
|
+
} from "./chunk-X5L37W4J.js";
|
|
11
|
+
import {
|
|
12
|
+
pathOrigin
|
|
13
|
+
} from "./chunk-ST2IBUAN.js";
|
|
14
|
+
import {
|
|
15
|
+
isBackbone
|
|
16
|
+
} from "./chunk-FYQWPEEY.js";
|
|
17
|
+
|
|
18
|
+
// src/layout/tubeMapLayout.ts
|
|
19
|
+
import { layoutTubeMap } from "@gmod/tubemap-core";
|
|
20
|
+
function visitStrands(graph) {
|
|
21
|
+
const strands = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const [segment, visits] of graph.pathVisits ?? []) {
|
|
23
|
+
for (const visit of visits) {
|
|
24
|
+
const key = `${visit.path} ${segment}`;
|
|
25
|
+
const list = strands.get(key);
|
|
26
|
+
if (list) {
|
|
27
|
+
list.push(visit.strand);
|
|
28
|
+
} else {
|
|
29
|
+
strands.set(key, [visit.strand]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return strands;
|
|
34
|
+
}
|
|
35
|
+
function tubeMapTracks(graph) {
|
|
36
|
+
const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
37
|
+
const strands = visitStrands(graph);
|
|
38
|
+
const paths = (graph.paths ?? []).map((path, index) => ({ path, index }));
|
|
39
|
+
const reference = paths.findIndex(
|
|
40
|
+
(p) => pathOrigin(p.path.name).name === graph.referencePath
|
|
41
|
+
);
|
|
42
|
+
if (reference > 0) {
|
|
43
|
+
paths.unshift(...paths.splice(reference, 1));
|
|
44
|
+
}
|
|
45
|
+
return paths.flatMap(({ path, index }) => {
|
|
46
|
+
const visitName = pathOrigin(path.name).name;
|
|
47
|
+
const seen = /* @__PURE__ */ new Map();
|
|
48
|
+
const sequence = path.nodeIds.flatMap((id) => {
|
|
49
|
+
const node = nodeById.get(id);
|
|
50
|
+
if (!node) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
const k = seen.get(node.name) ?? 0;
|
|
54
|
+
seen.set(node.name, k + 1);
|
|
55
|
+
const strand = strands.get(`${visitName} ${node.name}`)?.[k] ?? canonicalStrand(node);
|
|
56
|
+
return [stepName(node, strand)];
|
|
57
|
+
});
|
|
58
|
+
return sequence.length > 0 ? [
|
|
59
|
+
{
|
|
60
|
+
id: index,
|
|
61
|
+
name: path.name,
|
|
62
|
+
sequence,
|
|
63
|
+
sourceTrackID: 0,
|
|
64
|
+
type: "haplotype"
|
|
65
|
+
}
|
|
66
|
+
] : [];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function tubeMapNodes(graph) {
|
|
70
|
+
return graph.nodes.map((n) => ({
|
|
71
|
+
name: n.id,
|
|
72
|
+
seq: "",
|
|
73
|
+
sequenceLength: Math.max(1, n.length)
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
function hasTubeMapPaths(graph) {
|
|
77
|
+
return (graph.paths?.length ?? 0) > 0;
|
|
78
|
+
}
|
|
79
|
+
var STACK_PX = 600;
|
|
80
|
+
var MIN_TUBE_PX = 3;
|
|
81
|
+
var MAX_TUBE_PX = 15;
|
|
82
|
+
function tubeWidth(trackCount) {
|
|
83
|
+
return Math.max(
|
|
84
|
+
MIN_TUBE_PX,
|
|
85
|
+
Math.min(MAX_TUBE_PX, Math.floor(STACK_PX / Math.max(1, trackCount)))
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
function runTubeMap(graph) {
|
|
89
|
+
const paths = graph.paths ?? [];
|
|
90
|
+
const tracks = tubeMapTracks(graph);
|
|
91
|
+
const reads = graph.reads ? tubeMapReads(graph, graph.reads, paths.length) : [];
|
|
92
|
+
const tubeColor = reads.length > 0 ? pathGreyCssColor : pathCssColor;
|
|
93
|
+
const pathColors = paths.map((_, i) => tubeColor(i, paths.length));
|
|
94
|
+
const layout = layoutTubeMap(tubeMapNodes(graph), tracks, reads, {
|
|
95
|
+
nodeWidthOption: "compressed",
|
|
96
|
+
trackWidth: tubeWidth(tracks.length),
|
|
97
|
+
trackColor: (track) => track.type === "read" ? readColor(track) : pathColors[track.id]
|
|
98
|
+
});
|
|
99
|
+
return layout ? { layout, pathColors } : void 0;
|
|
100
|
+
}
|
|
101
|
+
function drawnNodes(layout) {
|
|
102
|
+
const nodes = [];
|
|
103
|
+
layout.nodes.forEach((node) => {
|
|
104
|
+
if (node.order >= 0) {
|
|
105
|
+
nodes.push(node);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return nodes;
|
|
109
|
+
}
|
|
110
|
+
function centreline(node, yOffset, x0, x1) {
|
|
111
|
+
const y = node.y + node.contentHeight / 2 - yOffset;
|
|
112
|
+
return [
|
|
113
|
+
{ x: x0, y },
|
|
114
|
+
{ x: x1, y }
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
function extentOf(layout, yOffset) {
|
|
118
|
+
return {
|
|
119
|
+
minY: layout.bounds.minY - yOffset,
|
|
120
|
+
maxY: layout.bounds.maxY - yOffset
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function tubeMapLayout(graph) {
|
|
124
|
+
const run = hasTubeMapPaths(graph) ? runTubeMap(graph) : void 0;
|
|
125
|
+
if (!run) {
|
|
126
|
+
return void 0;
|
|
127
|
+
}
|
|
128
|
+
const { layout, pathColors } = run;
|
|
129
|
+
const yOffset = layout.bounds.minY;
|
|
130
|
+
const nodePositions = {};
|
|
131
|
+
for (const node of drawnNodes(layout)) {
|
|
132
|
+
nodePositions[node.name] = centreline(
|
|
133
|
+
node,
|
|
134
|
+
yOffset,
|
|
135
|
+
node.x,
|
|
136
|
+
node.x + node.pixelWidth
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
const { minY, maxY } = extentOf(layout, yOffset);
|
|
140
|
+
return {
|
|
141
|
+
nodePositions,
|
|
142
|
+
tubeMap: { layout, yOffset, pathColors },
|
|
143
|
+
extent: {
|
|
144
|
+
minX: layout.bounds.minX,
|
|
145
|
+
maxX: layout.bounds.maxX,
|
|
146
|
+
minY,
|
|
147
|
+
maxY
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function tubeMapColumns(graph, layout) {
|
|
152
|
+
const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
153
|
+
const byOrder = /* @__PURE__ */ new Map();
|
|
154
|
+
for (const node of drawnNodes(layout)) {
|
|
155
|
+
const list = byOrder.get(node.order);
|
|
156
|
+
if (list) {
|
|
157
|
+
list.push(node);
|
|
158
|
+
} else {
|
|
159
|
+
byOrder.set(node.order, [node]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const columns = [];
|
|
163
|
+
let end = -Infinity;
|
|
164
|
+
for (const order of [...byOrder.keys()].sort((a, b) => a - b)) {
|
|
165
|
+
const nodes = byOrder.get(order);
|
|
166
|
+
let bp0 = Infinity;
|
|
167
|
+
let bp1 = -Infinity;
|
|
168
|
+
for (const node of nodes) {
|
|
169
|
+
const graphNode = nodeById.get(node.name);
|
|
170
|
+
if (graphNode && isBackbone(graphNode)) {
|
|
171
|
+
bp0 = Math.min(bp0, graphNode.stable.start);
|
|
172
|
+
bp1 = Math.max(bp1, graphNode.stable.start + node.sequenceLength);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (bp0 === Infinity) {
|
|
176
|
+
bp0 = bp1 = end;
|
|
177
|
+
}
|
|
178
|
+
if (bp0 < end) {
|
|
179
|
+
bp0 = end;
|
|
180
|
+
bp1 = Math.max(end, bp1);
|
|
181
|
+
}
|
|
182
|
+
end = Math.max(end, bp1);
|
|
183
|
+
columns.push({
|
|
184
|
+
order,
|
|
185
|
+
x0: Math.min(...nodes.map((n) => n.x)),
|
|
186
|
+
x1: Math.max(...nodes.map((n) => n.x + n.pixelWidth)),
|
|
187
|
+
bp0,
|
|
188
|
+
bp1
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
const first = columns.find((c) => c.bp0 !== -Infinity);
|
|
192
|
+
for (const column of columns) {
|
|
193
|
+
if (column.bp0 === -Infinity) {
|
|
194
|
+
column.bp0 = column.bp1 = first?.bp0 ?? 0;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return columns;
|
|
198
|
+
}
|
|
199
|
+
function pathsReachBackbone(graph) {
|
|
200
|
+
return graph.nodes.some(isBackbone) && hasTubeMapPaths(graph);
|
|
201
|
+
}
|
|
202
|
+
function tubeMapReferenceLayout(graph) {
|
|
203
|
+
const run = pathsReachBackbone(graph) ? runTubeMap(graph) : void 0;
|
|
204
|
+
if (!run) {
|
|
205
|
+
return void 0;
|
|
206
|
+
}
|
|
207
|
+
const { layout, pathColors } = run;
|
|
208
|
+
const columns = tubeMapColumns(graph, layout);
|
|
209
|
+
const byOrder = new Map(columns.map((c) => [c.order, c]));
|
|
210
|
+
const yOffset = layout.bounds.minY;
|
|
211
|
+
const nodePositions = {};
|
|
212
|
+
for (const node of drawnNodes(layout)) {
|
|
213
|
+
const column = byOrder.get(node.order);
|
|
214
|
+
nodePositions[node.name] = centreline(node, yOffset, column.bp0, column.bp1);
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
nodePositions,
|
|
218
|
+
tubeMap: { layout, yOffset, columns, pathColors },
|
|
219
|
+
referenceAxis: true,
|
|
220
|
+
pixelRows: true,
|
|
221
|
+
extent: extentOf(layout, yOffset)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export {
|
|
226
|
+
visitStrands,
|
|
227
|
+
tubeMapTracks,
|
|
228
|
+
hasTubeMapPaths,
|
|
229
|
+
tubeWidth,
|
|
230
|
+
tubeMapLayout,
|
|
231
|
+
tubeMapReferenceLayout
|
|
232
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isGenericSample,
|
|
3
|
+
panSNContig,
|
|
4
|
+
panSNHaplotype,
|
|
5
|
+
panSNSample
|
|
6
|
+
} from "./chunk-BPLIXMH7.js";
|
|
7
|
+
import {
|
|
8
|
+
isBackbone
|
|
9
|
+
} from "./chunk-FYQWPEEY.js";
|
|
10
|
+
|
|
11
|
+
// src/reference.ts
|
|
12
|
+
var WELL_KNOWN_SAMPLES = /* @__PURE__ */ new Map([
|
|
13
|
+
["hg38", "GRCh38"],
|
|
14
|
+
["hg19", "GRCh37"],
|
|
15
|
+
["hs1", "CHM13"],
|
|
16
|
+
["t2t-chm13", "CHM13"],
|
|
17
|
+
["chm13v2", "CHM13"]
|
|
18
|
+
]);
|
|
19
|
+
function wellKnownSample(assemblyName) {
|
|
20
|
+
return WELL_KNOWN_SAMPLES.get(assemblyName.toLowerCase());
|
|
21
|
+
}
|
|
22
|
+
function sampleOf(refName) {
|
|
23
|
+
const sample = refName.includes("#") ? panSNSample(refName) : void 0;
|
|
24
|
+
return sample === void 0 || isGenericSample(sample) ? void 0 : sample;
|
|
25
|
+
}
|
|
26
|
+
function haplotypeOf(refName) {
|
|
27
|
+
return panSNHaplotype(refName) ?? panSNSample(refName);
|
|
28
|
+
}
|
|
29
|
+
function graphHaplotypes(graph) {
|
|
30
|
+
const refNames = new Set((graph.anchorPaths ?? []).map((p) => p.name));
|
|
31
|
+
for (const node of graph.nodes) {
|
|
32
|
+
if (node.stable) {
|
|
33
|
+
refNames.add(node.stable.refName);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const bySample = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const refName of refNames) {
|
|
38
|
+
const sample = sampleOf(refName);
|
|
39
|
+
if (sample !== void 0) {
|
|
40
|
+
const haplotypes = bySample.get(sample) ?? bySample.set(sample, /* @__PURE__ */ new Set()).get(sample);
|
|
41
|
+
haplotypes.add(haplotypeOf(refName));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return bySample;
|
|
45
|
+
}
|
|
46
|
+
function backboneFrom(contigs, haplotypes) {
|
|
47
|
+
const samples = new Set(contigs.map((c) => sampleOf(c.refName)));
|
|
48
|
+
const [sample] = samples;
|
|
49
|
+
if (samples.size !== 1 || sample === void 0) {
|
|
50
|
+
return { contigs, prefixes: [], named: false };
|
|
51
|
+
}
|
|
52
|
+
const own = new Set(contigs.map((c) => haplotypeOf(c.refName)));
|
|
53
|
+
const unique = [...haplotypes.get(sample) ?? []].every((h) => own.has(h));
|
|
54
|
+
const shared = new Set(contigs.map((c) => panSNHaplotype(c.refName)));
|
|
55
|
+
const [haplotype] = shared;
|
|
56
|
+
return {
|
|
57
|
+
contigs,
|
|
58
|
+
prefixes: [
|
|
59
|
+
...unique ? [sample] : [],
|
|
60
|
+
...shared.size === 1 && haplotype ? [haplotype] : []
|
|
61
|
+
],
|
|
62
|
+
named: true
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function graphBackbone(graph) {
|
|
66
|
+
const spans = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const node of graph.nodes) {
|
|
68
|
+
if (isBackbone(node)) {
|
|
69
|
+
const { refName, start } = node.stable;
|
|
70
|
+
const end = start + node.length;
|
|
71
|
+
const span = spans.get(refName);
|
|
72
|
+
if (span) {
|
|
73
|
+
span.start = Math.min(span.start, start);
|
|
74
|
+
span.end = Math.max(span.end, end);
|
|
75
|
+
} else {
|
|
76
|
+
spans.set(refName, {
|
|
77
|
+
refName,
|
|
78
|
+
contig: panSNContig(refName),
|
|
79
|
+
start,
|
|
80
|
+
end
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const contigs = [...spans.values()];
|
|
86
|
+
return contigs.length ? backboneFrom(contigs, graphHaplotypes(graph)) : void 0;
|
|
87
|
+
}
|
|
88
|
+
function referenceLabel(graph, region) {
|
|
89
|
+
const backbone = graphBackbone(graph);
|
|
90
|
+
const contig = region ? panSNContig(region.refName) : backbone?.contigs.length === 1 ? backbone.contigs[0].contig : void 0;
|
|
91
|
+
const sample = backbone?.named ? panSNSample(backbone.contigs[0].refName) : void 0;
|
|
92
|
+
return contig && (sample ? `${sample} ${contig}` : contig);
|
|
93
|
+
}
|
|
94
|
+
function backboneAssembly(backbone, assemblies) {
|
|
95
|
+
const names = (a) => [a.name, ...a.aliases ?? []];
|
|
96
|
+
const naming = (prefix) => (a) => names(a).some((n) => n.toLowerCase() === prefix.toLowerCase());
|
|
97
|
+
const prefixes = [...backbone?.prefixes ?? []].reverse();
|
|
98
|
+
const sample = backbone?.prefixes.find((p) => !p.includes("#"))?.toLowerCase();
|
|
99
|
+
for (const prefix of prefixes) {
|
|
100
|
+
const assembly = assemblies.find(naming(prefix));
|
|
101
|
+
if (assembly) {
|
|
102
|
+
return assembly;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return sample === void 0 ? void 0 : assemblies.find(
|
|
106
|
+
(a) => names(a).some((n) => wellKnownSample(n)?.toLowerCase() === sample)
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
function assemblyWalk(graph, assembly) {
|
|
110
|
+
const haplotypes = graphHaplotypes(graph);
|
|
111
|
+
return graph.anchorPaths?.find(
|
|
112
|
+
(walk) => backboneAssembly(
|
|
113
|
+
backboneFrom(
|
|
114
|
+
[
|
|
115
|
+
{
|
|
116
|
+
refName: walk.name,
|
|
117
|
+
contig: panSNContig(walk.name),
|
|
118
|
+
start: walk.start,
|
|
119
|
+
end: walk.start + walk.length
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
haplotypes
|
|
123
|
+
),
|
|
124
|
+
[assembly]
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
function contigNames(contig) {
|
|
129
|
+
const bare = contig.replace(/^chr/i, "");
|
|
130
|
+
const forms = !bare ? [] : /^(M|MT)$/i.test(bare) ? ["M", "MT"] : [bare];
|
|
131
|
+
return [.../* @__PURE__ */ new Set([contig, ...forms.flatMap((f) => [f, `chr${f}`])])];
|
|
132
|
+
}
|
|
133
|
+
function refNameBinding(refNames, aliases = {}) {
|
|
134
|
+
const exact = new Set(refNames);
|
|
135
|
+
const byName = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const refName of exact) {
|
|
137
|
+
const contig = panSNContig(refName);
|
|
138
|
+
const alias = aliases[contig];
|
|
139
|
+
for (const name of /* @__PURE__ */ new Set([
|
|
140
|
+
...contigNames(contig),
|
|
141
|
+
...alias ? [alias] : []
|
|
142
|
+
])) {
|
|
143
|
+
byName.set(name, byName.has(name) ? void 0 : refName);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return (name) => exact.has(name) ? name : byName.get(name);
|
|
147
|
+
}
|
|
148
|
+
function featuresOnBackbone(features, backbone, aliases) {
|
|
149
|
+
const bind = refNameBinding(
|
|
150
|
+
backbone.contigs.map((c) => c.refName),
|
|
151
|
+
aliases
|
|
152
|
+
);
|
|
153
|
+
return features.flatMap((f) => {
|
|
154
|
+
const refName = bind(f.refName);
|
|
155
|
+
return refName === void 0 ? [] : [refName === f.refName ? f : { ...f, refName }];
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export {
|
|
160
|
+
WELL_KNOWN_SAMPLES,
|
|
161
|
+
wellKnownSample,
|
|
162
|
+
graphBackbone,
|
|
163
|
+
referenceLabel,
|
|
164
|
+
backboneAssembly,
|
|
165
|
+
assemblyWalk,
|
|
166
|
+
contigNames,
|
|
167
|
+
refNameBinding,
|
|
168
|
+
featuresOnBackbone
|
|
169
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
alongRow,
|
|
3
|
+
boxesGenes
|
|
4
|
+
} from "./chunk-6SXMCK6I.js";
|
|
5
|
+
import {
|
|
6
|
+
pathOrigin
|
|
7
|
+
} from "./chunk-ST2IBUAN.js";
|
|
8
|
+
import {
|
|
9
|
+
LABEL_CHAR_PX
|
|
10
|
+
} from "./chunk-AB35TJST.js";
|
|
11
|
+
import {
|
|
12
|
+
ROW_HEIGHT_PX
|
|
13
|
+
} from "./chunk-CPMHA4W5.js";
|
|
14
|
+
import {
|
|
15
|
+
el
|
|
16
|
+
} from "./chunk-NNODIW4U.js";
|
|
17
|
+
|
|
18
|
+
// src/layout/walkStrip.ts
|
|
19
|
+
var PAD_PX = 6;
|
|
20
|
+
var WALK_STRIP_CEILING_PX = 260;
|
|
21
|
+
var LABEL_FONT_PX = 11;
|
|
22
|
+
var MIN_ROW_PX = 3;
|
|
23
|
+
var LABELLED_ROW_PX = 12;
|
|
24
|
+
var READOUT_ROOM = 1.3;
|
|
25
|
+
var MARK_INK = "#111";
|
|
26
|
+
var TICK_PX = 3;
|
|
27
|
+
function stripMarks(graph, rows, node) {
|
|
28
|
+
const visits = graph.pathVisits?.get(node.name) ?? [];
|
|
29
|
+
const marks = [];
|
|
30
|
+
for (const row of rows) {
|
|
31
|
+
const axis = row.axis;
|
|
32
|
+
if (!axis) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const path = pathOrigin(row.name).name;
|
|
36
|
+
for (const v of visits) {
|
|
37
|
+
if (v.path !== path) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const { start, end } = alongRow(axis, v.start, v.start + node.length);
|
|
41
|
+
if (end > 0 && start < row.bp) {
|
|
42
|
+
marks.push({ row: row.name, start, bp: end - start });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return marks;
|
|
47
|
+
}
|
|
48
|
+
var spanCache = /* @__PURE__ */ new WeakMap();
|
|
49
|
+
function spansOf(graph, name) {
|
|
50
|
+
let byName = spanCache.get(graph);
|
|
51
|
+
if (!byName) {
|
|
52
|
+
byName = /* @__PURE__ */ new Map();
|
|
53
|
+
spanCache.set(graph, byName);
|
|
54
|
+
}
|
|
55
|
+
if (byName.has(name)) {
|
|
56
|
+
return byName.get(name);
|
|
57
|
+
}
|
|
58
|
+
const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
|
|
59
|
+
const pieces = (graph.paths ?? []).filter((p) => p.name === name);
|
|
60
|
+
let spans;
|
|
61
|
+
if (pieces.every((p) => p.start !== void 0)) {
|
|
62
|
+
spans = { ids: [], starts: [], ends: [] };
|
|
63
|
+
for (const piece of [...pieces].sort((a, b) => a.start - b.start)) {
|
|
64
|
+
let pos = piece.start;
|
|
65
|
+
for (const id of piece.nodeIds) {
|
|
66
|
+
const len = lengthOf.get(id) ?? 0;
|
|
67
|
+
spans.ids.push(id);
|
|
68
|
+
spans.starts.push(pos);
|
|
69
|
+
spans.ends.push(pos + len);
|
|
70
|
+
pos += len;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
byName.set(name, spans);
|
|
75
|
+
return spans;
|
|
76
|
+
}
|
|
77
|
+
function segmentAt(graph, row, offset, pxPerBp) {
|
|
78
|
+
const axis = row.axis;
|
|
79
|
+
const spans = axis && spansOf(graph, row.name);
|
|
80
|
+
if (!axis || !spans || offset < 0 || offset > row.bp) {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
const at = axis.reversed ? axis.start - offset : axis.start + offset;
|
|
84
|
+
const tol = 0.5 / pxPerBp;
|
|
85
|
+
const bar = axis.reversed ? { start: axis.start - row.bp, end: axis.start } : { start: axis.start, end: axis.start + row.bp };
|
|
86
|
+
const from = Math.max(at - tol, bar.start);
|
|
87
|
+
const to = Math.min(at + tol, bar.end);
|
|
88
|
+
let lo = 0;
|
|
89
|
+
let hi = spans.starts.length;
|
|
90
|
+
while (lo < hi) {
|
|
91
|
+
const mid = lo + hi >> 1;
|
|
92
|
+
if (spans.ends[mid] <= from) {
|
|
93
|
+
lo = mid + 1;
|
|
94
|
+
} else {
|
|
95
|
+
hi = mid;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
let best;
|
|
99
|
+
let bestLen = -1;
|
|
100
|
+
let bestHolds = false;
|
|
101
|
+
for (let i = lo; i < spans.starts.length && spans.starts[i] < to; i++) {
|
|
102
|
+
const len = spans.ends[i] - spans.starts[i];
|
|
103
|
+
const holds = spans.starts[i] <= at && at < spans.ends[i];
|
|
104
|
+
if (len > bestLen || len === bestLen && holds && !bestHolds) {
|
|
105
|
+
bestLen = len;
|
|
106
|
+
best = spans.ids[i];
|
|
107
|
+
bestHolds = holds;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return best;
|
|
111
|
+
}
|
|
112
|
+
function walkStripFrame(bars, o) {
|
|
113
|
+
const maxHeight = o.maxHeight ?? WALK_STRIP_CEILING_PX;
|
|
114
|
+
const labelPx = o.labelPx ?? Math.max(...[bars.reference, ...bars.rows].map((r) => r.label.length)) * LABEL_CHAR_PX + 16;
|
|
115
|
+
const count = bars.rows.length + 1;
|
|
116
|
+
const rowPx = Math.max(
|
|
117
|
+
MIN_ROW_PX,
|
|
118
|
+
Math.min(ROW_HEIGHT_PX, Math.floor((maxHeight - 2 * PAD_PX) / count))
|
|
119
|
+
);
|
|
120
|
+
const labelled = rowPx >= LABELLED_ROW_PX;
|
|
121
|
+
const readouts = rowPx >= 14;
|
|
122
|
+
const barPx = Math.max(2, Math.round(rowPx * 0.6));
|
|
123
|
+
const longest = Math.max(bars.reference.bp, ...bars.rows.map((r) => r.bp), 1);
|
|
124
|
+
const left = labelled ? labelPx : PAD_PX;
|
|
125
|
+
const usable = Math.max(1, o.width - left - PAD_PX);
|
|
126
|
+
const scaleX = usable / (longest * (readouts ? READOUT_ROOM : 1));
|
|
127
|
+
return {
|
|
128
|
+
scaleX,
|
|
129
|
+
scaleY: 1,
|
|
130
|
+
translateX: left - bars.origin * scaleX,
|
|
131
|
+
translateY: PAD_PX + rowPx / 2,
|
|
132
|
+
width: o.width,
|
|
133
|
+
height: count * rowPx + 2 * PAD_PX,
|
|
134
|
+
rowPx,
|
|
135
|
+
barPx,
|
|
136
|
+
readouts,
|
|
137
|
+
labelled,
|
|
138
|
+
boxesGenes: boxesGenes(rowPx, barPx)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function stripRowAt(bars, frame, x, y) {
|
|
142
|
+
const i = Math.round((y - frame.translateY) / frame.rowPx);
|
|
143
|
+
const row = [bars.reference, ...bars.rows][i];
|
|
144
|
+
const offset = (x - frame.translateX) / frame.scaleX - bars.origin;
|
|
145
|
+
return row && i >= 0 && offset >= 0 && offset <= row.bp ? { index: i, row, offset } : void 0;
|
|
146
|
+
}
|
|
147
|
+
function walkMarksTree(bars, frame, marks) {
|
|
148
|
+
const index = new Map(
|
|
149
|
+
[bars.reference, ...bars.rows].map((row, i) => [row.name, i])
|
|
150
|
+
);
|
|
151
|
+
const X = (offset) => (bars.origin + offset) * frame.scaleX + frame.translateX;
|
|
152
|
+
return el(
|
|
153
|
+
"g",
|
|
154
|
+
{ "data-testid": "graph-walk-marks" },
|
|
155
|
+
...marks.flatMap((m) => {
|
|
156
|
+
const i = index.get(m.row);
|
|
157
|
+
if (i === void 0) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const y = i * frame.rowPx + frame.translateY;
|
|
161
|
+
const x = X(m.start);
|
|
162
|
+
const w = Math.max(2, m.bp * frame.scaleX);
|
|
163
|
+
const tick = (top) => el("rect", { x, y: top, width: w, height: TICK_PX, fill: MARK_INK });
|
|
164
|
+
return [
|
|
165
|
+
tick(y - frame.barPx / 2 - TICK_PX - 1),
|
|
166
|
+
tick(y + frame.barPx / 2 + 1)
|
|
167
|
+
];
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
function walkStripLabelsTree(bars, frame, bold = /* @__PURE__ */ new Set()) {
|
|
172
|
+
return el(
|
|
173
|
+
"g",
|
|
174
|
+
{ "font-family": "sans-serif", "font-size": LABEL_FONT_PX, fill: "#333" },
|
|
175
|
+
...(frame.labelled ? [bars.reference, ...bars.rows] : []).map(
|
|
176
|
+
(row, i) => el(
|
|
177
|
+
"text",
|
|
178
|
+
{
|
|
179
|
+
x: 6,
|
|
180
|
+
y: i * frame.rowPx + frame.translateY + 4,
|
|
181
|
+
"font-weight": bold.has(row.name) ? 600 : void 0
|
|
182
|
+
},
|
|
183
|
+
row.label
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function stripGeneGaps(frame, rowGenes, gaps) {
|
|
189
|
+
if (!rowGenes?.size) {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
return frame.boxesGenes ? gaps : { untracked: 0, unread: 0, ...gaps, crowded: true };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export {
|
|
196
|
+
WALK_STRIP_CEILING_PX,
|
|
197
|
+
LABELLED_ROW_PX,
|
|
198
|
+
stripMarks,
|
|
199
|
+
segmentAt,
|
|
200
|
+
walkStripFrame,
|
|
201
|
+
stripRowAt,
|
|
202
|
+
walkMarksTree,
|
|
203
|
+
walkStripLabelsTree,
|
|
204
|
+
stripGeneGaps
|
|
205
|
+
};
|