@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,170 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodedColor,
|
|
3
|
+
resolveEncoding
|
|
4
|
+
} from "./chunk-IMW3AK27.js";
|
|
5
|
+
import {
|
|
6
|
+
pathOrigin
|
|
7
|
+
} from "./chunk-ST2IBUAN.js";
|
|
8
|
+
import {
|
|
9
|
+
panSNContig
|
|
10
|
+
} from "./chunk-BPLIXMH7.js";
|
|
11
|
+
|
|
12
|
+
// src/walkHighlight.ts
|
|
13
|
+
var isReference = (graph, name) => !!graph.referencePath && pathOrigin(name).name === graph.referencePath;
|
|
14
|
+
function piecesOf(graph, name) {
|
|
15
|
+
return (graph.paths ?? []).filter((p) => p.name === name).sort((a, b) => (a.start ?? 0) - (b.start ?? 0));
|
|
16
|
+
}
|
|
17
|
+
function walkHighlight(graph, name) {
|
|
18
|
+
const pieces = piecesOf(graph, name);
|
|
19
|
+
const first = pieces[0];
|
|
20
|
+
if (!first) {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
24
|
+
const edgeAt = /* @__PURE__ */ new Map();
|
|
25
|
+
graph.edges.forEach((e, i) => {
|
|
26
|
+
edgeAt.set(`${e.from}>${e.to}`, i);
|
|
27
|
+
});
|
|
28
|
+
const lengthOf = (id) => byId.get(id)?.length ?? 0;
|
|
29
|
+
const bpOf = (path) => path.nodeIds.reduce((sum, id) => sum + lengthOf(id), 0);
|
|
30
|
+
const bp = pieces.reduce((sum, piece) => sum + bpOf(piece), 0);
|
|
31
|
+
const range = first.start === void 0 ? void 0 : {
|
|
32
|
+
contig: first.contig ?? panSNContig(pathOrigin(name).name),
|
|
33
|
+
start: first.start,
|
|
34
|
+
end: Math.max(...pieces.map((p) => (p.start ?? 0) + bpOf(p)))
|
|
35
|
+
};
|
|
36
|
+
const span = range ? range.end - range.start : bp;
|
|
37
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
38
|
+
const edgeIndexes = /* @__PURE__ */ new Set();
|
|
39
|
+
const progress = /* @__PURE__ */ new Map();
|
|
40
|
+
let before = 0;
|
|
41
|
+
for (const piece of pieces) {
|
|
42
|
+
if (range) {
|
|
43
|
+
before = Math.max(before, (piece.start ?? 0) - range.start);
|
|
44
|
+
}
|
|
45
|
+
piece.nodeIds.forEach((id, i) => {
|
|
46
|
+
nodeIds.add(id);
|
|
47
|
+
if (i > 0) {
|
|
48
|
+
const a = piece.nodeIds[i - 1];
|
|
49
|
+
const index = edgeAt.get(`${a}>${id}`) ?? edgeAt.get(`${id}>${a}`);
|
|
50
|
+
if (index !== void 0) {
|
|
51
|
+
edgeIndexes.add(index);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const length = lengthOf(id);
|
|
55
|
+
if (!progress.has(id)) {
|
|
56
|
+
progress.set(id, span > 0 ? (before + length / 2) / span : 0);
|
|
57
|
+
}
|
|
58
|
+
before += length;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const reference = isReference(graph, name);
|
|
62
|
+
const referencePieces = reference ? [] : (graph.paths ?? []).filter((p) => isReference(graph, p.name));
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
reference,
|
|
66
|
+
range,
|
|
67
|
+
nodeIds,
|
|
68
|
+
edgeIndexes,
|
|
69
|
+
progress,
|
|
70
|
+
steps: pieces.reduce((sum, piece) => sum + piece.nodeIds.length, 0),
|
|
71
|
+
bp,
|
|
72
|
+
referenceBp: referencePieces.length > 0 ? referencePieces.reduce((sum, piece) => sum + bpOf(piece), 0) : void 0
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function laneColors(graph, walk, encoding, ramp) {
|
|
76
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
77
|
+
const colors = /* @__PURE__ */ new Map();
|
|
78
|
+
for (const [id, progress] of walk.progress) {
|
|
79
|
+
const node = byId.get(id);
|
|
80
|
+
const mid = ramp?.midpoints.get(id);
|
|
81
|
+
const t = encoding.field === "progress" ? progress : ramp && mid !== void 0 && !(node?.stable && node.stable.rank > 0) ? (mid - ramp.start) / ramp.span : void 0;
|
|
82
|
+
colors.set(id, encodedColor(encoding, t));
|
|
83
|
+
}
|
|
84
|
+
return colors;
|
|
85
|
+
}
|
|
86
|
+
function reversedNodes(graph, walk) {
|
|
87
|
+
const reversed = /* @__PURE__ */ new Set();
|
|
88
|
+
let bp = 0;
|
|
89
|
+
const referencePath = graph.referencePath;
|
|
90
|
+
if (!referencePath || walk.reference) {
|
|
91
|
+
return { reversed, bp };
|
|
92
|
+
}
|
|
93
|
+
const walkPath = pathOrigin(walk.name).name;
|
|
94
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
95
|
+
for (const id of walk.progress.keys()) {
|
|
96
|
+
const node = byId.get(id);
|
|
97
|
+
const visits = node ? graph.pathVisits?.get(node.name) : void 0;
|
|
98
|
+
const own = visits?.find((v) => v.path === walkPath)?.strand;
|
|
99
|
+
const theirs = visits?.find((v) => v.path === referencePath)?.strand;
|
|
100
|
+
if (own && theirs && own !== theirs) {
|
|
101
|
+
reversed.add(id);
|
|
102
|
+
bp += node.length;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return { reversed, bp };
|
|
106
|
+
}
|
|
107
|
+
function walkLift(graph, layers, ramp) {
|
|
108
|
+
const seen = /* @__PURE__ */ new Set();
|
|
109
|
+
const lifted = layers.filter((layer) => !seen.has(layer.walk) && seen.add(layer.walk)).flatMap((layer) => {
|
|
110
|
+
const walk = walkHighlight(graph, layer.walk);
|
|
111
|
+
return walk ? [{ walk, layer }] : [];
|
|
112
|
+
}).sort((a, b) => Number(b.walk.reference) - Number(a.walk.reference));
|
|
113
|
+
if (lifted.length === 0) {
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
116
|
+
let picked = 0;
|
|
117
|
+
const walks = lifted.map(({ walk, layer }) => {
|
|
118
|
+
const encoding = resolveEncoding(
|
|
119
|
+
layer,
|
|
120
|
+
walk.reference,
|
|
121
|
+
walk.reference ? 0 : picked++,
|
|
122
|
+
lifted.length === 1
|
|
123
|
+
);
|
|
124
|
+
const { reversed, bp } = reversedNodes(graph, walk);
|
|
125
|
+
return {
|
|
126
|
+
...walk,
|
|
127
|
+
encoding,
|
|
128
|
+
colors: laneColors(graph, walk, encoding, ramp),
|
|
129
|
+
reversed,
|
|
130
|
+
reversedBp: bp
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
return liftOf(walks, ramp);
|
|
134
|
+
}
|
|
135
|
+
var FACET_COLOR = { field: "progress", scheme: "red" };
|
|
136
|
+
function facetLifts(graph, lift, layers, ramp) {
|
|
137
|
+
return lift.walks.map((walk) => {
|
|
138
|
+
const stated = layers.find((l) => l.walk === walk.name)?.color;
|
|
139
|
+
const encoding = resolveEncoding(
|
|
140
|
+
{ walk: walk.name, color: { ...FACET_COLOR, ...stated } },
|
|
141
|
+
walk.reference,
|
|
142
|
+
0
|
|
143
|
+
);
|
|
144
|
+
return liftOf(
|
|
145
|
+
[
|
|
146
|
+
{
|
|
147
|
+
...walk,
|
|
148
|
+
encoding,
|
|
149
|
+
colors: laneColors(graph, walk, encoding, ramp)
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
ramp
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function liftOf(walks, ramp) {
|
|
157
|
+
return {
|
|
158
|
+
walks,
|
|
159
|
+
referenceDomain: ramp ? { start: ramp.start, end: ramp.start + ramp.span } : void 0,
|
|
160
|
+
nodeIds: new Set(walks.flatMap((w) => [...w.nodeIds])),
|
|
161
|
+
edgeIndexes: new Set(walks.flatMap((w) => [...w.edgeIndexes])),
|
|
162
|
+
names: new Set(walks.map((w) => w.name))
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export {
|
|
167
|
+
walkHighlight,
|
|
168
|
+
walkLift,
|
|
169
|
+
facetLifts
|
|
170
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/facetGrid.ts
|
|
2
|
+
var FACET_GAP_PX = 8;
|
|
3
|
+
var FACET_TITLE_PX = 34;
|
|
4
|
+
var FACET_PAD_PX = 12;
|
|
5
|
+
var MIN_FACET_PANEL_PX = 60;
|
|
6
|
+
function facetGrid({
|
|
7
|
+
count,
|
|
8
|
+
bounds,
|
|
9
|
+
pixelRows,
|
|
10
|
+
width,
|
|
11
|
+
room,
|
|
12
|
+
columns
|
|
13
|
+
}) {
|
|
14
|
+
function tile(across) {
|
|
15
|
+
const rows = Math.ceil(count / across);
|
|
16
|
+
const panelWidth = Math.floor(
|
|
17
|
+
(width - (across - 1) * FACET_GAP_PX) / across
|
|
18
|
+
);
|
|
19
|
+
const panelRoom = (room - rows * FACET_TITLE_PX - (rows - 1) * FACET_GAP_PX) / rows;
|
|
20
|
+
const fitX = (panelWidth - 2 * FACET_PAD_PX) / bounds.w;
|
|
21
|
+
const fitY = bounds.h > 0 ? (panelRoom - 2 * FACET_PAD_PX) / bounds.h : Infinity;
|
|
22
|
+
const scale = pixelRows ? fitX : Math.min(fitX, fitY);
|
|
23
|
+
const drawn = pixelRows ? bounds.h : bounds.h * scale;
|
|
24
|
+
const height = Math.floor(
|
|
25
|
+
Math.max(
|
|
26
|
+
MIN_FACET_PANEL_PX,
|
|
27
|
+
Math.min(drawn + 2 * FACET_PAD_PX, panelRoom)
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
return {
|
|
31
|
+
columns: across,
|
|
32
|
+
width: panelWidth,
|
|
33
|
+
height,
|
|
34
|
+
scale,
|
|
35
|
+
total: rows * (height + FACET_TITLE_PX) + (rows - 1) * FACET_GAP_PX
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (columns !== void 0) {
|
|
39
|
+
return tile(Math.max(1, Math.min(count, Math.round(columns))));
|
|
40
|
+
}
|
|
41
|
+
let best = tile(1);
|
|
42
|
+
for (let across = 2; across <= count; across++) {
|
|
43
|
+
const grid = tile(across);
|
|
44
|
+
if (grid.scale > best.scale) {
|
|
45
|
+
best = grid;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return best;
|
|
49
|
+
}
|
|
50
|
+
function facetSettingOf(facet) {
|
|
51
|
+
const raw = typeof facet === "string" ? { field: facet } : facet ?? {};
|
|
52
|
+
const field = raw.field === "walk" || raw.field === "sample" ? raw.field : "";
|
|
53
|
+
return {
|
|
54
|
+
field,
|
|
55
|
+
domain: [...raw.domain ?? []],
|
|
56
|
+
...raw.columns === void 0 ? {} : { columns: raw.columns }
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function domainFirst(names, domain) {
|
|
60
|
+
const rank = new Map(domain.map((name, i) => [name, i]));
|
|
61
|
+
const listed = (name) => rank.get(name) ?? domain.length;
|
|
62
|
+
return names.map((name, i) => ({ name, i })).sort((a, b) => listed(a.name) - listed(b.name) || a.i - b.i).map(({ name }) => name);
|
|
63
|
+
}
|
|
64
|
+
function facetCells(names, by, domain = []) {
|
|
65
|
+
const wrapOrder = domainFirst(names, domain);
|
|
66
|
+
const wrap = {
|
|
67
|
+
columns: void 0,
|
|
68
|
+
count: names.length,
|
|
69
|
+
cells: names.map((name) => wrapOrder.indexOf(name))
|
|
70
|
+
};
|
|
71
|
+
if (by === "walk") {
|
|
72
|
+
return wrap;
|
|
73
|
+
}
|
|
74
|
+
const places = names.map((name) => {
|
|
75
|
+
const [sample, haplotype, contig] = name.split("#");
|
|
76
|
+
const n = Number(haplotype);
|
|
77
|
+
return contig !== void 0 && Number.isInteger(n) && n >= 0 ? { sample, column: Math.max(0, n - 1) } : void 0;
|
|
78
|
+
});
|
|
79
|
+
const rows = domainFirst(
|
|
80
|
+
[...new Set(places.flatMap((p) => p ? [p.sample] : []))],
|
|
81
|
+
domain
|
|
82
|
+
);
|
|
83
|
+
const columns = Math.max(1, ...places.map((p) => p ? p.column + 1 : 1));
|
|
84
|
+
const cells = places.map(
|
|
85
|
+
(p) => p ? rows.indexOf(p.sample) * columns + p.column : -1
|
|
86
|
+
);
|
|
87
|
+
return cells.includes(-1) || new Set(cells).size < cells.length ? wrap : { columns, count: rows.length * columns, cells };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
FACET_GAP_PX,
|
|
92
|
+
FACET_TITLE_PX,
|
|
93
|
+
FACET_PAD_PX,
|
|
94
|
+
facetGrid,
|
|
95
|
+
facetSettingOf,
|
|
96
|
+
facetCells
|
|
97
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bypassedPoints
|
|
3
|
+
} from "./chunk-X32PABB7.js";
|
|
4
|
+
import {
|
|
5
|
+
computeEdgeCurves,
|
|
6
|
+
edgeJoinOf,
|
|
7
|
+
routedEdgeCurves,
|
|
8
|
+
strandSides
|
|
9
|
+
} from "./chunk-3PLYZLYV.js";
|
|
10
|
+
|
|
11
|
+
// src/util/edgeCurves.ts
|
|
12
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
13
|
+
function baseEdgeCurves(nodePositions, graph, axis, deletions, version, routes, stranded = false) {
|
|
14
|
+
const { scaleX, scaleY } = axis;
|
|
15
|
+
const cached = cache.get(nodePositions);
|
|
16
|
+
if (cached?.graph === graph && cached.scaleX === scaleX && cached.scaleY === scaleY && cached.deletions === deletions && cached.routes === routes && cached.stranded === stranded && cached.version === version) {
|
|
17
|
+
return cached.curves;
|
|
18
|
+
}
|
|
19
|
+
const curves = /* @__PURE__ */ new Map();
|
|
20
|
+
for (let ei = 0; ei < graph.edges.length; ei++) {
|
|
21
|
+
const edge = graph.edges[ei];
|
|
22
|
+
const from = nodePositions[edge.from];
|
|
23
|
+
const to = nodePositions[edge.to];
|
|
24
|
+
if (from?.length && to?.length) {
|
|
25
|
+
const bypassed = deletions?.get(ei);
|
|
26
|
+
const route = bypassed && routes?.[ei];
|
|
27
|
+
curves.set(
|
|
28
|
+
ei,
|
|
29
|
+
route ? routedEdgeCurves(
|
|
30
|
+
from,
|
|
31
|
+
to,
|
|
32
|
+
route,
|
|
33
|
+
stranded ? strandSides(edge) : void 0
|
|
34
|
+
) : computeEdgeCurves(
|
|
35
|
+
from,
|
|
36
|
+
to,
|
|
37
|
+
edgeJoinOf(edge, stranded),
|
|
38
|
+
0,
|
|
39
|
+
0,
|
|
40
|
+
axis,
|
|
41
|
+
bypassed ? bypassedPoints(nodePositions, bypassed) : []
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
cache.set(nodePositions, {
|
|
47
|
+
graph,
|
|
48
|
+
scaleX,
|
|
49
|
+
scaleY,
|
|
50
|
+
deletions,
|
|
51
|
+
routes,
|
|
52
|
+
stranded,
|
|
53
|
+
version,
|
|
54
|
+
curves
|
|
55
|
+
});
|
|
56
|
+
return curves;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
baseEdgeCurves
|
|
61
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
stableCoordinate
|
|
3
|
+
} from "./chunk-CDLPYR5O.js";
|
|
4
|
+
import {
|
|
5
|
+
pathOrigin,
|
|
6
|
+
surveyPaths
|
|
7
|
+
} from "./chunk-ST2IBUAN.js";
|
|
8
|
+
|
|
9
|
+
// src/gfa/gfaConverter.ts
|
|
10
|
+
function surveySegments(gfaGraph) {
|
|
11
|
+
const canonical = /* @__PURE__ */ new Map();
|
|
12
|
+
const traversals = /* @__PURE__ */ new Map();
|
|
13
|
+
function claim(id, strand) {
|
|
14
|
+
if (!canonical.has(id)) {
|
|
15
|
+
canonical.set(id, strand);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function traverse(id, strand) {
|
|
19
|
+
claim(id, strand);
|
|
20
|
+
traversals.set(id, (traversals.get(id) ?? 0) + 1);
|
|
21
|
+
}
|
|
22
|
+
for (const link of gfaGraph.links) {
|
|
23
|
+
claim(link.source, link.strand1 === "-" ? "-" : "+");
|
|
24
|
+
claim(link.target, link.strand2 === "-" ? "-" : "+");
|
|
25
|
+
}
|
|
26
|
+
for (const gfaPath of gfaGraph.paths) {
|
|
27
|
+
for (const segment of gfaPath.path.split(",")) {
|
|
28
|
+
traverse(segment.slice(0, -1), segment.endsWith("-") ? "-" : "+");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const walk of gfaGraph.walks) {
|
|
32
|
+
for (const seg of walk.segments) {
|
|
33
|
+
traverse(seg.id, seg.strand === "-" ? "-" : "+");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { canonical, traversals };
|
|
37
|
+
}
|
|
38
|
+
var walkStart = (w) => w.start === -1 ? 0 : w.start;
|
|
39
|
+
function anchorablePaths(gfaGraph) {
|
|
40
|
+
return [
|
|
41
|
+
...gfaGraph.paths.map((p) => ({
|
|
42
|
+
...pathOrigin(p.name),
|
|
43
|
+
steps: p.path.split(",").map((segment) => ({
|
|
44
|
+
id: segment.slice(0, -1),
|
|
45
|
+
strand: segment.endsWith("-") ? "-" : "+"
|
|
46
|
+
}))
|
|
47
|
+
})),
|
|
48
|
+
...gfaGraph.walks.map((w) => ({
|
|
49
|
+
name: `${w.sample}#${w.haplotype}#${w.contig}`,
|
|
50
|
+
start: walkStart(w),
|
|
51
|
+
steps: w.segments.map((s) => ({
|
|
52
|
+
id: s.id,
|
|
53
|
+
strand: s.strand === "-" ? "-" : "+"
|
|
54
|
+
}))
|
|
55
|
+
}))
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
function carriedSamples(gfaNode) {
|
|
59
|
+
const sm = gfaNode.tags.SM;
|
|
60
|
+
if (typeof sm !== "string") {
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
const samples = sm.split(",").map((s) => s.trim()).filter(Boolean);
|
|
64
|
+
return samples.length > 0 ? samples : void 0;
|
|
65
|
+
}
|
|
66
|
+
var positive = (v) => typeof v === "number" && v > 0 ? v : void 0;
|
|
67
|
+
function statedDepth({ tags, length }) {
|
|
68
|
+
const count = positive(tags.RC) ?? positive(tags.FC) ?? positive(tags.KC);
|
|
69
|
+
return positive(tags.dp) ?? positive(tags.DP) ?? (count !== void 0 && length > 0 ? count / length : void 0);
|
|
70
|
+
}
|
|
71
|
+
function makeNode(gfaNode, strand, depth) {
|
|
72
|
+
return {
|
|
73
|
+
id: `${gfaNode.id}${strand}`,
|
|
74
|
+
name: gfaNode.id,
|
|
75
|
+
length: gfaNode.length,
|
|
76
|
+
depth,
|
|
77
|
+
stable: stableCoordinate(gfaNode),
|
|
78
|
+
samples: carriedSamples(gfaNode)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function convertGFAToGraph(gfaGraph, name = "Imported GFA") {
|
|
82
|
+
const nodes = [];
|
|
83
|
+
const edges = [];
|
|
84
|
+
const { canonical, traversals } = surveySegments(gfaGraph);
|
|
85
|
+
const nodeId = (segmentId) => `${segmentId}${canonical.get(segmentId) ?? "+"}`;
|
|
86
|
+
for (const gfaNode of gfaGraph.nodes) {
|
|
87
|
+
const depth = statedDepth(gfaNode) ?? Math.max(traversals.get(gfaNode.id) ?? 1, 1);
|
|
88
|
+
nodes.push(makeNode(gfaNode, canonical.get(gfaNode.id) ?? "+", depth));
|
|
89
|
+
}
|
|
90
|
+
for (const link of gfaGraph.links) {
|
|
91
|
+
edges.push({
|
|
92
|
+
from: nodeId(link.source),
|
|
93
|
+
to: nodeId(link.target),
|
|
94
|
+
fromStrand: link.strand1 === "-" ? "-" : "+",
|
|
95
|
+
toStrand: link.strand2 === "-" ? "-" : "+"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const paths = [];
|
|
99
|
+
const edgeToPathsMap = /* @__PURE__ */ new Map();
|
|
100
|
+
const edgeKey = (a, b) => [a, b].sort().join("--");
|
|
101
|
+
function recordPathEdges(nodeIds, name2) {
|
|
102
|
+
for (let i = 0; i < nodeIds.length - 1; i++) {
|
|
103
|
+
const key = edgeKey(nodeIds[i], nodeIds[i + 1]);
|
|
104
|
+
if (!edgeToPathsMap.has(key)) {
|
|
105
|
+
edgeToPathsMap.set(key, /* @__PURE__ */ new Set());
|
|
106
|
+
}
|
|
107
|
+
edgeToPathsMap.get(key).add(name2);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const gfaPath of gfaGraph.paths) {
|
|
111
|
+
const nodeIds = gfaPath.path.split(",").map((segment) => nodeId(segment.slice(0, -1)));
|
|
112
|
+
paths.push({
|
|
113
|
+
name: gfaPath.name,
|
|
114
|
+
nodeIds,
|
|
115
|
+
start: pathOrigin(gfaPath.name).start
|
|
116
|
+
});
|
|
117
|
+
recordPathEdges(nodeIds, gfaPath.name);
|
|
118
|
+
}
|
|
119
|
+
for (const walk of gfaGraph.walks) {
|
|
120
|
+
const nodeIds = walk.segments.map((seg) => nodeId(seg.id));
|
|
121
|
+
const name2 = `${walk.sample}#${walk.haplotype}#${walk.contig}`;
|
|
122
|
+
paths.push({
|
|
123
|
+
name: name2,
|
|
124
|
+
nodeIds,
|
|
125
|
+
start: walkStart(walk),
|
|
126
|
+
sample: walk.sample,
|
|
127
|
+
haplotype: walk.haplotype,
|
|
128
|
+
contig: walk.contig
|
|
129
|
+
});
|
|
130
|
+
recordPathEdges(nodeIds, name2);
|
|
131
|
+
}
|
|
132
|
+
for (const edge of edges) {
|
|
133
|
+
const pathIds = edgeToPathsMap.get(edgeKey(edge.from, edge.to));
|
|
134
|
+
if (pathIds && pathIds.size > 0) {
|
|
135
|
+
edge.pathIds = [...pathIds];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const lengths = new Map(gfaGraph.nodes.map((n) => [n.id, n.length]));
|
|
139
|
+
const anchoring = gfaGraph.paths.length > 0 || gfaGraph.walks.length > 0 ? surveyPaths(anchorablePaths(gfaGraph), (id) => lengths.get(id) ?? 0) : void 0;
|
|
140
|
+
return {
|
|
141
|
+
name,
|
|
142
|
+
nodes,
|
|
143
|
+
edges,
|
|
144
|
+
paths: paths.length > 0 ? paths : void 0,
|
|
145
|
+
anchorPaths: anchoring?.anchorPaths,
|
|
146
|
+
pathVisits: anchoring?.pathVisits,
|
|
147
|
+
// rGFA's own tags win wherever they are present; pathAnchoring only ever
|
|
148
|
+
// fills in for a graph that carries none.
|
|
149
|
+
anchoredBy: nodes.some((n) => n.stable) ? "tags" : void 0
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export {
|
|
154
|
+
convertGFAToGraph
|
|
155
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GBZ_CUT_DEFAULTS,
|
|
3
|
+
cutWindowGFA,
|
|
4
|
+
haplotypeWanted,
|
|
5
|
+
referencePathQuery,
|
|
6
|
+
referenceSamplesOf,
|
|
7
|
+
resolveReferenceSample
|
|
8
|
+
} from "./chunk-LTUWJKHD.js";
|
|
9
|
+
|
|
10
|
+
// src/gbzCut.ts
|
|
11
|
+
import { GBZBase } from "@gmod/gbz-base";
|
|
12
|
+
var HPRC_GBZ = {
|
|
13
|
+
db: "https://s3-us-west-2.amazonaws.com/human-pangenomics/pangenomes/freeze/release2/minigraph-cactus/v2.1/hprc-v2.1-mc-grch38/hprc-v2.1-mc-grch38.gbz.db",
|
|
14
|
+
index: "https://jbrowse.org/demos/hprc/hprc-v2.1-mc-grch38.haplotype-index.f3.db"
|
|
15
|
+
};
|
|
16
|
+
function haplotypeIndexBeside(db) {
|
|
17
|
+
const GBZ_DB = /\.gbz\.db$/i;
|
|
18
|
+
return GBZ_DB.test(db) ? db.replace(GBZ_DB, ".haplotype-index.db") : void 0;
|
|
19
|
+
}
|
|
20
|
+
function parseRegion(text) {
|
|
21
|
+
const m = /^\s*([^:\s]+):([\d,]+)-([\d,]+)\s*$/.exec(text);
|
|
22
|
+
if (!m) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`"${text}" is not a region like chr6:160,614,798-160,647,758`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const n = (s) => Number(s.replaceAll(",", ""));
|
|
28
|
+
return { refName: m[1], start: n(m[2]), end: n(m[3]) };
|
|
29
|
+
}
|
|
30
|
+
async function openGbz(db, index) {
|
|
31
|
+
const base = await GBZBase.open({ source: db, haplotypeIndex: index });
|
|
32
|
+
return { base, referenceSamples: await referenceSamplesOf(base) };
|
|
33
|
+
}
|
|
34
|
+
function referenceSample(ref, samples) {
|
|
35
|
+
try {
|
|
36
|
+
return resolveReferenceSample({
|
|
37
|
+
configured: "",
|
|
38
|
+
anchorPrefix: ref ?? samples[0] ?? "",
|
|
39
|
+
referenceSamples: samples
|
|
40
|
+
});
|
|
41
|
+
} catch (e) {
|
|
42
|
+
if (ref) {
|
|
43
|
+
return ref;
|
|
44
|
+
}
|
|
45
|
+
throw e;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function cutGbzRegion({ base, referenceSamples }, src, signal) {
|
|
49
|
+
const region = parseRegion(src.region);
|
|
50
|
+
const sample = referenceSample(src.referenceSample, referenceSamples);
|
|
51
|
+
const query = await referencePathQuery(base, sample, region.refName);
|
|
52
|
+
if (!query) {
|
|
53
|
+
throw new Error(`${sample} has no indexed path named ${region.refName}`);
|
|
54
|
+
}
|
|
55
|
+
signal?.throwIfAborted();
|
|
56
|
+
const wanted = src.haplotypes?.length ? src.haplotypes : void 0;
|
|
57
|
+
const text = await cutWindowGFA(base, query, region.start, region.end, {
|
|
58
|
+
context: src.context ?? GBZ_CUT_DEFAULTS.context,
|
|
59
|
+
snarls: src.snarls ?? GBZ_CUT_DEFAULTS.snarls,
|
|
60
|
+
limit: GBZ_CUT_DEFAULTS.limit,
|
|
61
|
+
signal,
|
|
62
|
+
keep: wanted ? (name) => haplotypeWanted(name, wanted) : void 0
|
|
63
|
+
});
|
|
64
|
+
return { text, region, sample };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
HPRC_GBZ,
|
|
69
|
+
haplotypeIndexBeside,
|
|
70
|
+
parseRegion,
|
|
71
|
+
openGbz,
|
|
72
|
+
cutGbzRegion
|
|
73
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pathOrigin
|
|
3
|
+
} from "./chunk-ST2IBUAN.js";
|
|
4
|
+
import {
|
|
5
|
+
groupDigits
|
|
6
|
+
} from "./chunk-HCANBJDD.js";
|
|
7
|
+
import {
|
|
8
|
+
isGenericSample
|
|
9
|
+
} from "./chunk-BPLIXMH7.js";
|
|
10
|
+
|
|
11
|
+
// src/pathColors.ts
|
|
12
|
+
function nameHue(name) {
|
|
13
|
+
let hash = 0;
|
|
14
|
+
for (let i = 0; i < name.length; i++) {
|
|
15
|
+
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
|
16
|
+
}
|
|
17
|
+
return Math.abs(hash % 360);
|
|
18
|
+
}
|
|
19
|
+
function pathHueAt(index, count) {
|
|
20
|
+
return index * 360 / count;
|
|
21
|
+
}
|
|
22
|
+
var PATH_SATURATION = 0.7;
|
|
23
|
+
var PATH_LIGHTNESS = 0.5;
|
|
24
|
+
var MAX_PATH_COLORS = 16;
|
|
25
|
+
function pathColorsLegible(pathCount) {
|
|
26
|
+
return pathCount > 0 && pathCount <= MAX_PATH_COLORS;
|
|
27
|
+
}
|
|
28
|
+
function pathCssColor(index, count) {
|
|
29
|
+
return `hsl(${pathHueAt(index, count)}, ${PATH_SATURATION * 100}%, ${PATH_LIGHTNESS * 100}%)`;
|
|
30
|
+
}
|
|
31
|
+
function pathGreyCssColor(index, count) {
|
|
32
|
+
const lightness = count > 1 ? 25 + 45 * index / (count - 1) : 45;
|
|
33
|
+
return `hsl(0, 0%, ${lightness}%)`;
|
|
34
|
+
}
|
|
35
|
+
function labelTiers(name) {
|
|
36
|
+
const parts = pathOrigin(name).name.split("#");
|
|
37
|
+
const full = parts.join("#");
|
|
38
|
+
if (parts.length >= 3 && isGenericSample(parts[0])) {
|
|
39
|
+
const contig = parts.slice(2).join("#");
|
|
40
|
+
return [contig, contig, full];
|
|
41
|
+
}
|
|
42
|
+
return [parts[0], parts.slice(0, 2).join("#"), full];
|
|
43
|
+
}
|
|
44
|
+
function offsetTiers(name, tiers) {
|
|
45
|
+
const { start } = pathOrigin(name);
|
|
46
|
+
return tiers.map((t) => `${t} @${groupDigits(start)}`);
|
|
47
|
+
}
|
|
48
|
+
function pathLegend(paths, colors) {
|
|
49
|
+
const firstOf = /* @__PURE__ */ new Map();
|
|
50
|
+
paths.forEach((path, i) => {
|
|
51
|
+
if (!firstOf.has(path.name)) {
|
|
52
|
+
firstOf.set(path.name, i);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
const entries = [...firstOf].map(([name, i]) => ({ name, i }));
|
|
56
|
+
const named = entries.map((e) => labelTiers(e.name));
|
|
57
|
+
const candidates = [0, 1, 2].flatMap((t) => [
|
|
58
|
+
entries.map((_, k) => named[k][t]),
|
|
59
|
+
entries.map((e, k) => offsetTiers(e.name, named[k])[t])
|
|
60
|
+
]);
|
|
61
|
+
const distinct = candidates.find(
|
|
62
|
+
(labels2) => new Set(labels2).size === entries.length
|
|
63
|
+
);
|
|
64
|
+
const labels = distinct ?? candidates.at(-1);
|
|
65
|
+
return entries.map(({ name, i }, k) => ({
|
|
66
|
+
name,
|
|
67
|
+
label: labels[k],
|
|
68
|
+
color: colors?.[i] ?? pathCssColor(i, paths.length)
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
nameHue,
|
|
74
|
+
pathHueAt,
|
|
75
|
+
PATH_SATURATION,
|
|
76
|
+
PATH_LIGHTNESS,
|
|
77
|
+
MAX_PATH_COLORS,
|
|
78
|
+
pathColorsLegible,
|
|
79
|
+
pathCssColor,
|
|
80
|
+
pathGreyCssColor,
|
|
81
|
+
pathLegend
|
|
82
|
+
};
|