@jbrowse/bandage-core 4.0.28 → 4.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{chunk-G3HO75W7.js → chunk-QN464POI.js} +264 -90
- package/dist/cli/figure.js +1 -1
- package/dist/index.js +12 -2
- package/dist/types/anchoredNodes.d.ts +4 -0
- package/dist/types/facetGrid.d.ts +22 -1
- package/dist/types/figure.d.ts +2 -3
- package/dist/types/gbzJoin.d.ts +2 -0
- package/dist/types/gbzWindow.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/types.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -145,6 +145,9 @@ function isOffReference(node) {
|
|
|
145
145
|
function backboneNodes(graph) {
|
|
146
146
|
return graph.nodes.filter(isBackbone);
|
|
147
147
|
}
|
|
148
|
+
function overlapsWindow(node, window) {
|
|
149
|
+
return node.stable.start < window.end && node.stable.start + node.length > window.start;
|
|
150
|
+
}
|
|
148
151
|
function backbonePositions(backbone) {
|
|
149
152
|
const positions = {};
|
|
150
153
|
for (const node of backbone) {
|
|
@@ -445,6 +448,7 @@ function surveySegments(gfaGraph) {
|
|
|
445
448
|
}
|
|
446
449
|
return { canonical, traversals };
|
|
447
450
|
}
|
|
451
|
+
var walkStart = (w) => w.start === -1 ? 0 : w.start;
|
|
448
452
|
function anchorablePaths(gfaGraph) {
|
|
449
453
|
return [
|
|
450
454
|
...gfaGraph.paths.map((p) => ({
|
|
@@ -456,10 +460,7 @@ function anchorablePaths(gfaGraph) {
|
|
|
456
460
|
})),
|
|
457
461
|
...gfaGraph.walks.map((w) => ({
|
|
458
462
|
name: `${w.sample}#${w.haplotype}#${w.contig}`,
|
|
459
|
-
|
|
460
|
-
// only offset that can be assumed, and it makes the walk self-relative
|
|
461
|
-
// rather than wrong
|
|
462
|
-
start: w.start === -1 ? 0 : w.start,
|
|
463
|
+
start: walkStart(w),
|
|
463
464
|
steps: w.segments.map((s) => ({
|
|
464
465
|
id: s.id,
|
|
465
466
|
strand: s.strand === "-" ? "-" : "+"
|
|
@@ -521,7 +522,11 @@ function convertGFAToGraph(gfaGraph, name = "Imported GFA") {
|
|
|
521
522
|
}
|
|
522
523
|
for (const gfaPath of gfaGraph.paths) {
|
|
523
524
|
const nodeIds = gfaPath.path.split(",").map((segment) => nodeId(segment.slice(0, -1)));
|
|
524
|
-
paths.push({
|
|
525
|
+
paths.push({
|
|
526
|
+
name: gfaPath.name,
|
|
527
|
+
nodeIds,
|
|
528
|
+
start: pathOrigin(gfaPath.name).start
|
|
529
|
+
});
|
|
525
530
|
recordPathEdges(nodeIds, gfaPath.name);
|
|
526
531
|
}
|
|
527
532
|
for (const walk of gfaGraph.walks) {
|
|
@@ -530,6 +535,7 @@ function convertGFAToGraph(gfaGraph, name = "Imported GFA") {
|
|
|
530
535
|
paths.push({
|
|
531
536
|
name: name2,
|
|
532
537
|
nodeIds,
|
|
538
|
+
start: walkStart(walk),
|
|
533
539
|
sample: walk.sample,
|
|
534
540
|
haplotype: walk.haplotype,
|
|
535
541
|
contig: walk.contig
|
|
@@ -1910,18 +1916,25 @@ function offsetTiers(name, tiers) {
|
|
|
1910
1916
|
return tiers.map((t) => `${t} @${groupDigits(start)}`);
|
|
1911
1917
|
}
|
|
1912
1918
|
function pathLegend(paths, colors) {
|
|
1913
|
-
const
|
|
1919
|
+
const firstOf = /* @__PURE__ */ new Map();
|
|
1920
|
+
paths.forEach((path, i) => {
|
|
1921
|
+
if (!firstOf.has(path.name)) {
|
|
1922
|
+
firstOf.set(path.name, i);
|
|
1923
|
+
}
|
|
1924
|
+
});
|
|
1925
|
+
const entries = [...firstOf].map(([name, i]) => ({ name, i }));
|
|
1926
|
+
const named = entries.map((e) => labelTiers(e.name));
|
|
1914
1927
|
const candidates = [0, 1, 2].flatMap((t) => [
|
|
1915
|
-
|
|
1916
|
-
|
|
1928
|
+
entries.map((_, k) => named[k][t]),
|
|
1929
|
+
entries.map((e, k) => offsetTiers(e.name, named[k])[t])
|
|
1917
1930
|
]);
|
|
1918
1931
|
const distinct = candidates.find(
|
|
1919
|
-
(labels2) => new Set(labels2).size ===
|
|
1932
|
+
(labels2) => new Set(labels2).size === entries.length
|
|
1920
1933
|
);
|
|
1921
1934
|
const labels = distinct ?? candidates.at(-1);
|
|
1922
|
-
return
|
|
1923
|
-
name
|
|
1924
|
-
label: labels[
|
|
1935
|
+
return entries.map(({ name, i }, k) => ({
|
|
1936
|
+
name,
|
|
1937
|
+
label: labels[k],
|
|
1925
1938
|
color: colors?.[i] ?? pathCssColor(i, paths.length)
|
|
1926
1939
|
}));
|
|
1927
1940
|
}
|
|
@@ -1962,21 +1975,26 @@ function sliceBetween(nodeIds, span, region, flanked = true) {
|
|
|
1962
1975
|
}
|
|
1963
1976
|
function walkRows(graph, region, unit) {
|
|
1964
1977
|
const paths = graph.paths ?? [];
|
|
1965
|
-
const
|
|
1966
|
-
|
|
1978
|
+
const isReference2 = (p) => pathOrigin(p.name).name === graph.referencePath;
|
|
1979
|
+
const referencePieces2 = paths.some(isReference2) ? paths.filter(isReference2).sort((a, b) => (a.start ?? 0) - (b.start ?? 0)) : paths.slice(0, 1);
|
|
1980
|
+
const first = referencePieces2[0];
|
|
1981
|
+
const others = paths.filter((p) => !referencePieces2.includes(p));
|
|
1982
|
+
if (!first || others.length === 0) {
|
|
1967
1983
|
return void 0;
|
|
1968
1984
|
}
|
|
1969
1985
|
const lengthOf = new Map(graph.nodes.map((n2) => [n2.id, n2.length]));
|
|
1970
|
-
const referenceStart =
|
|
1986
|
+
const referenceStart = first.start ?? 0;
|
|
1971
1987
|
const cut = region && region.end > region.start ? region : void 0;
|
|
1972
1988
|
const span = /* @__PURE__ */ new Map();
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
const
|
|
1976
|
-
|
|
1977
|
-
span.
|
|
1989
|
+
for (const piece of referencePieces2) {
|
|
1990
|
+
let pos = piece.start ?? 0;
|
|
1991
|
+
for (const id of piece.nodeIds) {
|
|
1992
|
+
const len = lengthOf.get(id) ?? 0;
|
|
1993
|
+
if (!span.has(id)) {
|
|
1994
|
+
span.set(id, { start: pos, end: pos + len });
|
|
1995
|
+
}
|
|
1996
|
+
pos += len;
|
|
1978
1997
|
}
|
|
1979
|
-
pos += len;
|
|
1980
1998
|
}
|
|
1981
1999
|
const flanked = cut === void 0 || [...span.values()].some((s) => s.end <= cut.start) && [...span.values()].some((s) => s.start >= cut.end);
|
|
1982
2000
|
const rowOf = (path) => {
|
|
@@ -2026,18 +2044,15 @@ function walkRows(graph, region, unit) {
|
|
|
2026
2044
|
runs
|
|
2027
2045
|
};
|
|
2028
2046
|
};
|
|
2029
|
-
const meetsReference = (p) => p.nodeIds.some((id) => span.has(id));
|
|
2030
|
-
const placedWalks = new Set(
|
|
2031
|
-
paths.filter((p) => p !== reference && meetsReference(p)).map((p) => p.name)
|
|
2032
|
-
);
|
|
2033
2047
|
const origin = cut ? cut.start : referenceStart;
|
|
2034
2048
|
return {
|
|
2035
2049
|
origin,
|
|
2036
2050
|
unit,
|
|
2037
|
-
reference: rowOf(
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
)
|
|
2051
|
+
reference: rowOf({
|
|
2052
|
+
...first,
|
|
2053
|
+
nodeIds: referencePieces2.flatMap((piece) => piece.nodeIds)
|
|
2054
|
+
}),
|
|
2055
|
+
rows: others.map(rowOf).sort(
|
|
2041
2056
|
(a, b) => Number(b.complete) - Number(a.complete) || b.bp - a.bp || a.label.localeCompare(b.label)
|
|
2042
2057
|
)
|
|
2043
2058
|
};
|
|
@@ -2061,7 +2076,10 @@ function walkRowLayout(graph, region) {
|
|
|
2061
2076
|
if (backbone.length === 0 || !walks) {
|
|
2062
2077
|
return void 0;
|
|
2063
2078
|
}
|
|
2064
|
-
const
|
|
2079
|
+
const overWindow = region ? backbone.filter((n2) => overlapsWindow(n2, region)) : backbone;
|
|
2080
|
+
const nodePositions = backbonePositions(
|
|
2081
|
+
overWindow.length > 0 ? overWindow : backbone
|
|
2082
|
+
);
|
|
2065
2083
|
const rowLabels = [
|
|
2066
2084
|
{ label: walks.reference.label, y: 0 },
|
|
2067
2085
|
...walks.rows.map((row, i) => ({
|
|
@@ -4168,12 +4186,14 @@ function encodingSwatchCss(encoding) {
|
|
|
4168
4186
|
}
|
|
4169
4187
|
|
|
4170
4188
|
// src/walkHighlight.ts
|
|
4171
|
-
|
|
4172
|
-
|
|
4189
|
+
var isReference = (graph, name) => !!graph.referencePath && pathOrigin(name).name === graph.referencePath;
|
|
4190
|
+
function piecesOf(graph, name) {
|
|
4191
|
+
return (graph.paths ?? []).filter((p) => p.name === name).sort((a, b) => (a.start ?? 0) - (b.start ?? 0));
|
|
4173
4192
|
}
|
|
4174
4193
|
function walkHighlight(graph, name) {
|
|
4175
|
-
const
|
|
4176
|
-
|
|
4194
|
+
const pieces = piecesOf(graph, name);
|
|
4195
|
+
const first = pieces[0];
|
|
4196
|
+
if (!first) {
|
|
4177
4197
|
return void 0;
|
|
4178
4198
|
}
|
|
4179
4199
|
const byId = new Map(graph.nodes.map((n2) => [n2.id, n2]));
|
|
@@ -4181,43 +4201,51 @@ function walkHighlight(graph, name) {
|
|
|
4181
4201
|
graph.edges.forEach((e, i) => {
|
|
4182
4202
|
edgeAt.set(`${e.from}>${e.to}`, i);
|
|
4183
4203
|
});
|
|
4184
|
-
const edgeIndexes = /* @__PURE__ */ new Set();
|
|
4185
|
-
for (let i = 1; i < path.nodeIds.length; i++) {
|
|
4186
|
-
const a = path.nodeIds[i - 1];
|
|
4187
|
-
const b = path.nodeIds[i];
|
|
4188
|
-
const index = edgeAt.get(`${a}>${b}`) ?? edgeAt.get(`${b}>${a}`);
|
|
4189
|
-
if (index !== void 0) {
|
|
4190
|
-
edgeIndexes.add(index);
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
4204
|
const lengthOf = (id) => byId.get(id)?.length ?? 0;
|
|
4194
|
-
const
|
|
4205
|
+
const bpOf = (path) => path.nodeIds.reduce((sum, id) => sum + lengthOf(id), 0);
|
|
4206
|
+
const bp = pieces.reduce((sum, piece) => sum + bpOf(piece), 0);
|
|
4207
|
+
const range = first.start === void 0 ? void 0 : {
|
|
4208
|
+
contig: first.contig ?? panSNContig(pathOrigin(name).name),
|
|
4209
|
+
start: first.start,
|
|
4210
|
+
end: Math.max(...pieces.map((p) => (p.start ?? 0) + bpOf(p)))
|
|
4211
|
+
};
|
|
4212
|
+
const span = range ? range.end - range.start : bp;
|
|
4213
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
4214
|
+
const edgeIndexes = /* @__PURE__ */ new Set();
|
|
4195
4215
|
const progress = /* @__PURE__ */ new Map();
|
|
4196
4216
|
let before = 0;
|
|
4197
|
-
for (const
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4217
|
+
for (const piece of pieces) {
|
|
4218
|
+
if (range) {
|
|
4219
|
+
before = Math.max(before, (piece.start ?? 0) - range.start);
|
|
4220
|
+
}
|
|
4221
|
+
piece.nodeIds.forEach((id, i) => {
|
|
4222
|
+
nodeIds.add(id);
|
|
4223
|
+
if (i > 0) {
|
|
4224
|
+
const a = piece.nodeIds[i - 1];
|
|
4225
|
+
const index = edgeAt.get(`${a}>${id}`) ?? edgeAt.get(`${id}>${a}`);
|
|
4226
|
+
if (index !== void 0) {
|
|
4227
|
+
edgeIndexes.add(index);
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
const length = lengthOf(id);
|
|
4231
|
+
if (!progress.has(id)) {
|
|
4232
|
+
progress.set(id, span > 0 ? (before + length / 2) / span : 0);
|
|
4233
|
+
}
|
|
4234
|
+
before += length;
|
|
4235
|
+
});
|
|
4203
4236
|
}
|
|
4204
|
-
const reference =
|
|
4205
|
-
const
|
|
4206
|
-
const origin = graph.anchorPaths?.find((a) => a.name === originName && a.length === bp) ?? graph.anchorPaths?.find((a) => a.name === originName);
|
|
4237
|
+
const reference = isReference(graph, name);
|
|
4238
|
+
const referencePieces2 = reference ? [] : (graph.paths ?? []).filter((p) => isReference(graph, p.name));
|
|
4207
4239
|
return {
|
|
4208
4240
|
name,
|
|
4209
|
-
reference
|
|
4210
|
-
range
|
|
4211
|
-
|
|
4212
|
-
start: origin.start,
|
|
4213
|
-
end: origin.start + origin.length
|
|
4214
|
-
} : void 0,
|
|
4215
|
-
nodeIds: new Set(path.nodeIds),
|
|
4241
|
+
reference,
|
|
4242
|
+
range,
|
|
4243
|
+
nodeIds,
|
|
4216
4244
|
edgeIndexes,
|
|
4217
4245
|
progress,
|
|
4218
|
-
steps:
|
|
4246
|
+
steps: pieces.reduce((sum, piece) => sum + piece.nodeIds.length, 0),
|
|
4219
4247
|
bp,
|
|
4220
|
-
referenceBp:
|
|
4248
|
+
referenceBp: referencePieces2.length > 0 ? referencePieces2.reduce((sum, piece) => sum + bpOf(piece), 0) : void 0
|
|
4221
4249
|
};
|
|
4222
4250
|
}
|
|
4223
4251
|
function laneColors(graph, walk, encoding, ramp) {
|
|
@@ -4232,14 +4260,13 @@ function laneColors(graph, walk, encoding, ramp) {
|
|
|
4232
4260
|
return colors;
|
|
4233
4261
|
}
|
|
4234
4262
|
function reversedNodes(graph, walk) {
|
|
4235
|
-
const reference = referenceOf(graph);
|
|
4236
4263
|
const reversed = /* @__PURE__ */ new Set();
|
|
4237
4264
|
let bp = 0;
|
|
4238
|
-
|
|
4265
|
+
const referencePath = graph.referencePath;
|
|
4266
|
+
if (!referencePath || walk.reference) {
|
|
4239
4267
|
return { reversed, bp };
|
|
4240
4268
|
}
|
|
4241
4269
|
const walkPath = pathOrigin(walk.name).name;
|
|
4242
|
-
const referencePath = pathOrigin(reference.name).name;
|
|
4243
4270
|
const byId = new Map(graph.nodes.map((n2) => [n2.id, n2]));
|
|
4244
4271
|
for (const id of walk.progress.keys()) {
|
|
4245
4272
|
const node = byId.get(id);
|
|
@@ -4336,7 +4363,9 @@ function walkPosition(walk, nodeId, length) {
|
|
|
4336
4363
|
if (progress === void 0 || !range) {
|
|
4337
4364
|
return void 0;
|
|
4338
4365
|
}
|
|
4339
|
-
const start = Math.round(
|
|
4366
|
+
const start = Math.round(
|
|
4367
|
+
range.start + progress * (range.end - range.start) - length / 2
|
|
4368
|
+
);
|
|
4340
4369
|
return `${range.contig}:${groupDigits(start)}-${groupDigits(start + length)}`;
|
|
4341
4370
|
}
|
|
4342
4371
|
|
|
@@ -4389,11 +4418,26 @@ function facetGrid({
|
|
|
4389
4418
|
}
|
|
4390
4419
|
return best;
|
|
4391
4420
|
}
|
|
4392
|
-
function
|
|
4421
|
+
function facetSettingOf(facet) {
|
|
4422
|
+
const raw = typeof facet === "string" ? { field: facet } : facet ?? {};
|
|
4423
|
+
const field = raw.field === "walk" || raw.field === "sample" ? raw.field : "";
|
|
4424
|
+
return {
|
|
4425
|
+
field,
|
|
4426
|
+
domain: [...raw.domain ?? []],
|
|
4427
|
+
...raw.columns === void 0 ? {} : { columns: raw.columns }
|
|
4428
|
+
};
|
|
4429
|
+
}
|
|
4430
|
+
function domainFirst(names, domain) {
|
|
4431
|
+
const rank = new Map(domain.map((name, i) => [name, i]));
|
|
4432
|
+
const listed = (name) => rank.get(name) ?? domain.length;
|
|
4433
|
+
return names.map((name, i) => ({ name, i })).sort((a, b) => listed(a.name) - listed(b.name) || a.i - b.i).map(({ name }) => name);
|
|
4434
|
+
}
|
|
4435
|
+
function facetCells(names, by, domain = []) {
|
|
4436
|
+
const wrapOrder = domainFirst(names, domain);
|
|
4393
4437
|
const wrap = {
|
|
4394
4438
|
columns: void 0,
|
|
4395
4439
|
count: names.length,
|
|
4396
|
-
cells: names.map((
|
|
4440
|
+
cells: names.map((name) => wrapOrder.indexOf(name))
|
|
4397
4441
|
};
|
|
4398
4442
|
if (by === "walk") {
|
|
4399
4443
|
return wrap;
|
|
@@ -4403,7 +4447,10 @@ function facetCells(names, by) {
|
|
|
4403
4447
|
const n2 = Number(haplotype);
|
|
4404
4448
|
return contig !== void 0 && Number.isInteger(n2) && n2 >= 0 ? { sample, column: Math.max(0, n2 - 1) } : void 0;
|
|
4405
4449
|
});
|
|
4406
|
-
const rows =
|
|
4450
|
+
const rows = domainFirst(
|
|
4451
|
+
[...new Set(places.flatMap((p) => p ? [p.sample] : []))],
|
|
4452
|
+
domain
|
|
4453
|
+
);
|
|
4407
4454
|
const columns = Math.max(1, ...places.map((p) => p ? p.column + 1 : 1));
|
|
4408
4455
|
const cells = places.map(
|
|
4409
4456
|
(p) => p ? rows.indexOf(p.sample) * columns + p.column : -1
|
|
@@ -4741,7 +4788,7 @@ function svgCanvas(width, height) {
|
|
|
4741
4788
|
}
|
|
4742
4789
|
|
|
4743
4790
|
// src/version.ts
|
|
4744
|
-
var version = "4.0.
|
|
4791
|
+
var version = "4.0.29";
|
|
4745
4792
|
|
|
4746
4793
|
// src/viewport.ts
|
|
4747
4794
|
function axisScaleOf(scale, pixelRows) {
|
|
@@ -4848,8 +4895,8 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4848
4895
|
const layers = o.walks ?? [];
|
|
4849
4896
|
const walkRamp = layers.some((l) => l.color?.field === "reference") ? referenceRamp ?? computeReferenceRamp(graph, rampDomain) : void 0;
|
|
4850
4897
|
const lift = layers.length > 0 && !layout.tubeMap ? walkLift(graph, layers, walkRamp) : void 0;
|
|
4851
|
-
const
|
|
4852
|
-
const panels =
|
|
4898
|
+
const facet = facetSettingOf(o.facet);
|
|
4899
|
+
const panels = facet.field && lift && lift.walks.length > 1 ? facetLifts(graph, lift, layers, walkRamp) : void 0;
|
|
4853
4900
|
const labels = new Map(
|
|
4854
4901
|
(graph.paths?.length ? pathLegend(graph.paths) : []).map((p) => [
|
|
4855
4902
|
p.name,
|
|
@@ -4966,7 +5013,8 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4966
5013
|
if (panels) {
|
|
4967
5014
|
const place = facetCells(
|
|
4968
5015
|
panels.map((p) => p.walks[0].name),
|
|
4969
|
-
|
|
5016
|
+
facet.field || "walk",
|
|
5017
|
+
facet.domain
|
|
4970
5018
|
);
|
|
4971
5019
|
const grid = facetGrid({
|
|
4972
5020
|
count: place.count,
|
|
@@ -4974,7 +5022,7 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4974
5022
|
pixelRows,
|
|
4975
5023
|
width,
|
|
4976
5024
|
room,
|
|
4977
|
-
columns: place.columns ??
|
|
5025
|
+
columns: place.columns ?? facet.columns
|
|
4978
5026
|
});
|
|
4979
5027
|
panels.forEach((panel, i) => {
|
|
4980
5028
|
const cell = place.cells[i];
|
|
@@ -5343,6 +5391,135 @@ function featuresOnBackbone(features, backbone, aliases) {
|
|
|
5343
5391
|
});
|
|
5344
5392
|
}
|
|
5345
5393
|
|
|
5394
|
+
// src/gbzJoin.ts
|
|
5395
|
+
var UNKNOWN_SAMPLE = "unknown";
|
|
5396
|
+
function parseWalk(line) {
|
|
5397
|
+
const fields = line.split(" ");
|
|
5398
|
+
return {
|
|
5399
|
+
fields,
|
|
5400
|
+
name: fields.slice(1, 4).join(" "),
|
|
5401
|
+
start: +fields[4],
|
|
5402
|
+
end: +fields[5]
|
|
5403
|
+
};
|
|
5404
|
+
}
|
|
5405
|
+
var stepsOf = (walk) => walk.fields[6].match(/[<>][^<>]+/g) ?? [];
|
|
5406
|
+
function segmentLengths(lines) {
|
|
5407
|
+
const lengths = /* @__PURE__ */ new Map();
|
|
5408
|
+
for (const line of lines) {
|
|
5409
|
+
if (line.startsWith("S ")) {
|
|
5410
|
+
const [, id, sequence, ...tags] = line.split(" ");
|
|
5411
|
+
const ln = tags.find((tag) => tag.startsWith("LN:i:"));
|
|
5412
|
+
lengths.set(
|
|
5413
|
+
id,
|
|
5414
|
+
sequence === "*" && ln ? +ln.slice("LN:i:".length) : sequence.length
|
|
5415
|
+
);
|
|
5416
|
+
}
|
|
5417
|
+
}
|
|
5418
|
+
return lengths;
|
|
5419
|
+
}
|
|
5420
|
+
function extend(a, b, lengths) {
|
|
5421
|
+
if (b.end <= a.end) {
|
|
5422
|
+
return a;
|
|
5423
|
+
}
|
|
5424
|
+
const steps = stepsOf(b);
|
|
5425
|
+
let pos = b.start;
|
|
5426
|
+
for (const [i, step] of steps.entries()) {
|
|
5427
|
+
if (pos === a.end) {
|
|
5428
|
+
const fields = [...a.fields];
|
|
5429
|
+
fields[5] = String(b.end);
|
|
5430
|
+
fields[6] = a.fields[6] + steps.slice(i).join("");
|
|
5431
|
+
return { ...a, fields, end: b.end };
|
|
5432
|
+
}
|
|
5433
|
+
const length = lengths.get(step.slice(1));
|
|
5434
|
+
if (pos > a.end || length === void 0) {
|
|
5435
|
+
return void 0;
|
|
5436
|
+
}
|
|
5437
|
+
pos += length;
|
|
5438
|
+
}
|
|
5439
|
+
return void 0;
|
|
5440
|
+
}
|
|
5441
|
+
function joinPieces(pieces, lengths, startsPath) {
|
|
5442
|
+
const joined = [];
|
|
5443
|
+
for (const piece of pieces.sort((a, b) => a.start - b.start)) {
|
|
5444
|
+
const last = joined.at(-1);
|
|
5445
|
+
const [, sample, haplotype, contig] = piece.fields;
|
|
5446
|
+
const joins = last !== void 0 && (piece.start < last.end || piece.start === last.end && !startsPath(sample, +haplotype, contig, piece.start));
|
|
5447
|
+
const extended = joins ? extend(last, piece, lengths) : void 0;
|
|
5448
|
+
if (extended) {
|
|
5449
|
+
joined[joined.length - 1] = extended;
|
|
5450
|
+
} else {
|
|
5451
|
+
joined.push(piece);
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
return joined;
|
|
5455
|
+
}
|
|
5456
|
+
var flip2 = (sign) => sign === "+" ? "-" : "+";
|
|
5457
|
+
function* stepLinks(walk) {
|
|
5458
|
+
const steps = stepsOf(walk).map((step) => ({
|
|
5459
|
+
id: step.slice(1),
|
|
5460
|
+
sign: step.startsWith(">") ? "+" : "-"
|
|
5461
|
+
}));
|
|
5462
|
+
for (let i = 1; i < steps.length; i++) {
|
|
5463
|
+
const a = steps[i - 1];
|
|
5464
|
+
const b = steps[i];
|
|
5465
|
+
yield {
|
|
5466
|
+
forward: `L ${a.id} ${a.sign} ${b.id} ${b.sign} 0M`,
|
|
5467
|
+
reverse: `L ${b.id} ${flip2(b.sign)} ${a.id} ${flip2(a.sign)} 0M`
|
|
5468
|
+
};
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
function joinCuts(gfas, startsPath) {
|
|
5472
|
+
if (gfas.length <= 1) {
|
|
5473
|
+
return gfas[0] ?? "";
|
|
5474
|
+
}
|
|
5475
|
+
const cuts = gfas.map((gfa) => gfa.split("\n").filter((line) => line !== ""));
|
|
5476
|
+
const ofType = (cut, type) => cut.filter((line) => line.startsWith(`${type} `));
|
|
5477
|
+
const lengths = segmentLengths(cuts.flat());
|
|
5478
|
+
let unknownOffset = 0;
|
|
5479
|
+
const walksByCut = cuts.map((cut) => {
|
|
5480
|
+
const walks2 = ofType(cut, "W").map(parseWalk);
|
|
5481
|
+
const offset = unknownOffset;
|
|
5482
|
+
for (const walk of walks2.filter((w) => w.fields[1] === UNKNOWN_SAMPLE)) {
|
|
5483
|
+
const haplotype = +walk.fields[2] + offset;
|
|
5484
|
+
walk.fields[2] = String(haplotype);
|
|
5485
|
+
walk.name = walk.fields.slice(1, 4).join(" ");
|
|
5486
|
+
unknownOffset = Math.max(unknownOffset, haplotype);
|
|
5487
|
+
}
|
|
5488
|
+
return walks2;
|
|
5489
|
+
});
|
|
5490
|
+
const byName = /* @__PURE__ */ new Map();
|
|
5491
|
+
for (const walk of [
|
|
5492
|
+
...walksByCut.flatMap((walks2) => walks2.slice(0, 1)),
|
|
5493
|
+
...walksByCut.flatMap((walks2) => walks2.slice(1))
|
|
5494
|
+
]) {
|
|
5495
|
+
const pieces = byName.get(walk.name);
|
|
5496
|
+
if (pieces) {
|
|
5497
|
+
pieces.push(walk);
|
|
5498
|
+
} else {
|
|
5499
|
+
byName.set(walk.name, [walk]);
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5502
|
+
const walks = [...byName.values()].flatMap(
|
|
5503
|
+
(pieces) => joinPieces(pieces, lengths, startsPath)
|
|
5504
|
+
);
|
|
5505
|
+
const links = new Set(cuts.flatMap((cut) => ofType(cut, "L")));
|
|
5506
|
+
for (const walk of walks) {
|
|
5507
|
+
for (const link of stepLinks(walk)) {
|
|
5508
|
+
if (!links.has(link.forward) && !links.has(link.reverse)) {
|
|
5509
|
+
links.add(link.forward);
|
|
5510
|
+
}
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5513
|
+
const lines = /* @__PURE__ */ new Set([
|
|
5514
|
+
...ofType(cuts[0], "H"),
|
|
5515
|
+
...cuts.flatMap((cut) => ofType(cut, "S")),
|
|
5516
|
+
...links,
|
|
5517
|
+
...walks.map((walk) => walk.fields.join(" "))
|
|
5518
|
+
]);
|
|
5519
|
+
return `${[...lines].join("\n")}
|
|
5520
|
+
`;
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5346
5523
|
// src/gbzWindow.ts
|
|
5347
5524
|
var NoReferenceSampleError = class extends Error {
|
|
5348
5525
|
constructor(anchor, referenceSamples) {
|
|
@@ -5399,13 +5576,13 @@ async function referencePathQuery(db, referenceSample2, refName) {
|
|
|
5399
5576
|
haplotype: path.name.haplotype
|
|
5400
5577
|
} : void 0;
|
|
5401
5578
|
}
|
|
5402
|
-
function nodeLimitError(error, limit, windowBp) {
|
|
5579
|
+
function nodeLimitError(error, limit, windowBp, walkedFrom = 0) {
|
|
5403
5580
|
const isLimit = error instanceof Error && (error.name === "SubgraphLimitError" || /^Subgraph size limit of \d+ nodes exceeded/.test(error.message));
|
|
5404
5581
|
if (!isLimit) {
|
|
5405
5582
|
return void 0;
|
|
5406
5583
|
} else {
|
|
5407
5584
|
const walked = error.walkedBp;
|
|
5408
|
-
const fits = typeof walked === "number" && walked > 0 ? Math.floor(walked * 0.8) : Math.floor(windowBp / 2);
|
|
5585
|
+
const fits = typeof walked === "number" && walked > 0 ? Math.floor((walkedFrom + walked) * 0.8) : Math.floor(windowBp / 2);
|
|
5409
5586
|
return new NodeLimitError(limit, windowBp, Math.max(fits, 1));
|
|
5410
5587
|
}
|
|
5411
5588
|
}
|
|
@@ -5424,19 +5601,6 @@ async function referencePieces(db, query, start, end) {
|
|
|
5424
5601
|
end: pieceEnd
|
|
5425
5602
|
}));
|
|
5426
5603
|
}
|
|
5427
|
-
function joinCuts(gfas) {
|
|
5428
|
-
const cuts = gfas.map((gfa) => gfa.split("\n").filter((line) => line !== ""));
|
|
5429
|
-
const ofType = (cut, type) => cut.filter((line) => line.startsWith(type));
|
|
5430
|
-
const lines = /* @__PURE__ */ new Set([
|
|
5431
|
-
...ofType(cuts[0] ?? [], "H"),
|
|
5432
|
-
...cuts.flatMap((cut) => ofType(cut, "S")),
|
|
5433
|
-
...cuts.flatMap((cut) => ofType(cut, "L")),
|
|
5434
|
-
...cuts.flatMap((cut) => ofType(cut, "W").slice(0, 1)),
|
|
5435
|
-
...cuts.flatMap((cut) => ofType(cut, "W").slice(1))
|
|
5436
|
-
]);
|
|
5437
|
-
return lines.size === 0 ? "" : `${[...lines].join("\n")}
|
|
5438
|
-
`;
|
|
5439
|
-
}
|
|
5440
5604
|
async function cutWindowGFA(db, query, start, end, { keep, ...opts }) {
|
|
5441
5605
|
if (!query) {
|
|
5442
5606
|
return "";
|
|
@@ -5449,15 +5613,24 @@ async function cutWindowGFA(db, query, start, end, { keep, ...opts }) {
|
|
|
5449
5613
|
haplotypes: "all",
|
|
5450
5614
|
...keep === void 0 ? {} : { keep }
|
|
5451
5615
|
}).catch((error) => {
|
|
5452
|
-
throw nodeLimitError(
|
|
5616
|
+
throw nodeLimitError(
|
|
5617
|
+
error,
|
|
5618
|
+
opts.limit,
|
|
5619
|
+
end - start,
|
|
5620
|
+
piece.start - start
|
|
5621
|
+
) ?? error;
|
|
5453
5622
|
})
|
|
5454
5623
|
)
|
|
5455
5624
|
);
|
|
5625
|
+
const paths = await db.paths();
|
|
5456
5626
|
return joinCuts(
|
|
5457
5627
|
await Promise.all(
|
|
5458
5628
|
subgraphs.flatMap(
|
|
5459
5629
|
(subgraph) => subgraph ? [subgraph.toGFA({ names: "resolved" })] : []
|
|
5460
5630
|
)
|
|
5631
|
+
),
|
|
5632
|
+
(sample, haplotype, contig, at) => paths.some(
|
|
5633
|
+
({ name }) => name.sample === sample && name.haplotype === haplotype && name.contig === contig && name.fragment === at
|
|
5461
5634
|
)
|
|
5462
5635
|
);
|
|
5463
5636
|
}
|
|
@@ -5596,6 +5769,7 @@ export {
|
|
|
5596
5769
|
FACET_TITLE_PX,
|
|
5597
5770
|
FACET_PAD_PX,
|
|
5598
5771
|
facetGrid,
|
|
5772
|
+
facetSettingOf,
|
|
5599
5773
|
facetCells,
|
|
5600
5774
|
genePins,
|
|
5601
5775
|
HALO_FACTOR,
|
package/dist/cli/figure.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
facetCells,
|
|
43
43
|
facetGrid,
|
|
44
44
|
facetLifts,
|
|
45
|
+
facetSettingOf,
|
|
45
46
|
featuresOnBackbone,
|
|
46
47
|
figureSvg,
|
|
47
48
|
fitTransform,
|
|
@@ -100,7 +101,7 @@ import {
|
|
|
100
101
|
wellKnownSample,
|
|
101
102
|
yToXOf,
|
|
102
103
|
zoomAbout
|
|
103
|
-
} from "./chunks/chunk-
|
|
104
|
+
} from "./chunks/chunk-QN464POI.js";
|
|
104
105
|
|
|
105
106
|
// src/index.ts
|
|
106
107
|
import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
|
|
@@ -1715,6 +1716,7 @@ function bubbleSubgraph(graph, segmentIds) {
|
|
|
1715
1716
|
const keep = new Set(segmentIds);
|
|
1716
1717
|
const nodes = graph.nodes.filter((n) => keep.has(n.name));
|
|
1717
1718
|
const ids = new Set(nodes.map((n) => n.id));
|
|
1719
|
+
const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
|
|
1718
1720
|
const kept = /* @__PURE__ */ new Map();
|
|
1719
1721
|
const paths = graph.paths?.flatMap((p) => {
|
|
1720
1722
|
const first = p.nodeIds.findIndex((id) => ids.has(id));
|
|
@@ -1723,7 +1725,14 @@ function bubbleSubgraph(graph, segmentIds) {
|
|
|
1723
1725
|
}
|
|
1724
1726
|
const nodeIds = p.nodeIds.filter((id) => ids.has(id));
|
|
1725
1727
|
kept.set(p.name, { first, nodeIds });
|
|
1726
|
-
|
|
1728
|
+
const before = p.nodeIds.slice(0, first).reduce((sum, id) => sum + (lengthOf.get(id) ?? 0), 0);
|
|
1729
|
+
return [
|
|
1730
|
+
{
|
|
1731
|
+
...p,
|
|
1732
|
+
nodeIds,
|
|
1733
|
+
...p.start === void 0 ? {} : { start: p.start + before }
|
|
1734
|
+
}
|
|
1735
|
+
];
|
|
1727
1736
|
});
|
|
1728
1737
|
return {
|
|
1729
1738
|
name: graph.name,
|
|
@@ -1790,6 +1799,7 @@ export {
|
|
|
1790
1799
|
facetCells,
|
|
1791
1800
|
facetGrid,
|
|
1792
1801
|
facetLifts,
|
|
1802
|
+
facetSettingOf,
|
|
1793
1803
|
featuresOnBackbone,
|
|
1794
1804
|
figureSvg,
|
|
1795
1805
|
findHoveredEdge,
|
|
@@ -8,6 +8,10 @@ export declare function isAnchored(node: GraphNode): node is AnchoredNode;
|
|
|
8
8
|
export declare function isBackbone(node: GraphNode): node is AnchoredNode;
|
|
9
9
|
export declare function isOffReference(node: GraphNode): node is AnchoredNode;
|
|
10
10
|
export declare function backboneNodes(graph: Graph): AnchoredNode[];
|
|
11
|
+
export declare function overlapsWindow(node: AnchoredNode, window: {
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
}): boolean;
|
|
11
15
|
export declare function backbonePositions(backbone: AnchoredNode[]): Record<string, NodeSegment[]>;
|
|
12
16
|
export declare function firstNodeAtOrAfter(nodes: AnchoredNode[], bp: number): number;
|
|
13
17
|
export declare function backboneSpan(backbone: AnchoredNode[]): number;
|
|
@@ -20,7 +20,28 @@ export declare function facetGrid({ count, bounds, pixelRows, width, room, colum
|
|
|
20
20
|
columns?: number;
|
|
21
21
|
}): FacetGrid;
|
|
22
22
|
export type FacetBy = 'walk' | 'sample';
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The facet as a session or a figure spec writes it, in the shape every
|
|
25
|
+
* JBrowse display's `facet` takes: the field the panels split on, `walk` for
|
|
26
|
+
* a panel per lifted walk or `sample` for a row per sample and a column per
|
|
27
|
+
* haplotype, the order the panels take, and how many go across under `walk`.
|
|
28
|
+
*/
|
|
29
|
+
export interface FacetSetting {
|
|
30
|
+
field: '' | FacetBy;
|
|
31
|
+
/** The walks, or under `sample` the samples, whose panels come first, in order. */
|
|
32
|
+
domain: string[];
|
|
33
|
+
/** How many panels go across; unset takes whichever count draws each largest. */
|
|
34
|
+
columns?: number;
|
|
35
|
+
}
|
|
36
|
+
/** What a session or a spec may write for the facet: the field bare, or the setting's members. */
|
|
37
|
+
export type FacetInput = string | {
|
|
38
|
+
field?: string;
|
|
39
|
+
domain?: readonly string[];
|
|
40
|
+
columns?: number;
|
|
41
|
+
};
|
|
42
|
+
/** A facet written bare is its field, and a field the pane cannot split on is none. */
|
|
43
|
+
export declare function facetSettingOf(facet: FacetInput | undefined): FacetSetting;
|
|
44
|
+
export declare function facetCells(names: string[], by: FacetBy, domain?: readonly string[]): {
|
|
24
45
|
columns: undefined;
|
|
25
46
|
count: number;
|
|
26
47
|
cells: number[];
|
package/dist/types/figure.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ColorScheme } from './colorSchemes.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FacetInput } from './facetGrid.js';
|
|
3
3
|
import type { GeneModel } from './genes/genePins.js';
|
|
4
4
|
import type { NodeWidth } from './nodeWidths.js';
|
|
5
5
|
import type { Graph, LayoutResult } from './types.js';
|
|
@@ -8,8 +8,7 @@ export interface FigureOptions {
|
|
|
8
8
|
width?: number;
|
|
9
9
|
height?: number;
|
|
10
10
|
walks?: WalkLayer[];
|
|
11
|
-
facet?:
|
|
12
|
-
columns?: number;
|
|
11
|
+
facet?: FacetInput;
|
|
13
12
|
colorScheme?: ColorScheme;
|
|
14
13
|
nodeWidth?: NodeWidth;
|
|
15
14
|
showDeletionEdges?: boolean;
|
|
@@ -16,7 +16,7 @@ export declare function resolveReferenceSample({ configured, anchorPrefix, refer
|
|
|
16
16
|
referenceSamples: string[];
|
|
17
17
|
}): string;
|
|
18
18
|
export declare function referencePathQuery(db: GBZBase, referenceSample: string, refName: string): Promise<PathQuery | undefined>;
|
|
19
|
-
export declare function nodeLimitError(error: unknown, limit: number, windowBp: number): NodeLimitError | undefined;
|
|
19
|
+
export declare function nodeLimitError(error: unknown, limit: number, windowBp: number, walkedFrom?: number): NodeLimitError | undefined;
|
|
20
20
|
export interface GbzWindowOptions {
|
|
21
21
|
context: number;
|
|
22
22
|
snarls: RangeOptions['snarls'];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -42,8 +42,8 @@ export type { GbzSource } from './gbzCut.js';
|
|
|
42
42
|
export { version } from './version.js';
|
|
43
43
|
export type { FigureOptions } from './figure.js';
|
|
44
44
|
export { svgCanvas } from './renderer/svgCanvas.js';
|
|
45
|
-
export { FACET_GAP_PX, FACET_PAD_PX, FACET_TITLE_PX, facetCells, facetGrid, } from './facetGrid.js';
|
|
46
|
-
export type { FacetBy, FacetGrid } from './facetGrid.js';
|
|
45
|
+
export { FACET_GAP_PX, FACET_PAD_PX, FACET_TITLE_PX, facetCells, facetGrid, facetSettingOf, } from './facetGrid.js';
|
|
46
|
+
export type { FacetBy, FacetGrid, FacetInput, FacetSetting } from './facetGrid.js';
|
|
47
47
|
export type { LiftedWalk, WalkLift } from './walkHighlight.js';
|
|
48
48
|
export { WALK_FIELDS, WALK_SCHEMES, encodingStops, encodingSwatchCss, resolveEncoding, schemeCss, } from './walkEncoding.js';
|
|
49
49
|
export type { WalkEncoding, WalkField, WalkLayer, WalkScheme, } from './walkEncoding.js';
|
package/dist/types/types.d.ts
CHANGED
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "4.0.
|
|
1
|
+
export declare const version = "4.0.29";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/bandage-core",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.29",
|
|
4
4
|
"description": "Pangenome graph layout and drawing with no host: GFA in, the Bandage force-directed engine and reference-anchored layouts, a Canvas2D renderer, hit testing and labels out",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|