@jbrowse/bandage-core 4.0.24 → 4.0.26
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-4AOHEU76.js → chunk-75KVO2WV.js} +610 -40
- package/dist/cli/figure.js +129 -65
- package/dist/index.js +100 -252
- package/dist/types/cli/figure.d.ts +8 -8
- package/dist/types/figure.d.ts +8 -1
- package/dist/types/gbzCut.d.ts +33 -0
- package/dist/types/genes/geneFiles.d.ts +10 -0
- package/dist/types/index.d.ts +7 -2
- package/dist/types/labelLayout.d.ts +14 -0
- package/dist/types/pathAnchoring.d.ts +5 -1
- package/dist/types/referenceStrip.d.ts +4 -3
- package/dist/types/tubeMap/draw.d.ts +6 -1
- package/dist/types/tubeMap/nodeColors.d.ts +4 -0
- package/dist/types/version.d.ts +1 -0
- package/dist/types/walkKey.d.ts +1 -0
- package/package.json +3 -2
|
@@ -387,6 +387,18 @@ function anchorFromPaths(graph, preferred) {
|
|
|
387
387
|
function anchorGraph(graph, preferred) {
|
|
388
388
|
return graph.anchoredBy === "tags" ? graph : anchorFromPaths(graph, preferred);
|
|
389
389
|
}
|
|
390
|
+
function trimOrigins(origins, paths, stretches) {
|
|
391
|
+
const pieces = /* @__PURE__ */ new Map();
|
|
392
|
+
for (const path of paths) {
|
|
393
|
+
const origin = pathOrigin(path.name).name;
|
|
394
|
+
pieces.set(origin, (pieces.get(origin) ?? 0) + 1);
|
|
395
|
+
}
|
|
396
|
+
return origins.map((o) => {
|
|
397
|
+
const path = paths.find((p) => pathOrigin(p.name).name === o.name);
|
|
398
|
+
const stretch = path ? stretches.get(path.name) : void 0;
|
|
399
|
+
return stretch && pieces.get(o.name) === 1 ? { ...o, start: o.start + stretch.dropped, length: stretch.bp } : o;
|
|
400
|
+
});
|
|
401
|
+
}
|
|
390
402
|
|
|
391
403
|
// src/gfa/gfaConverter.ts
|
|
392
404
|
function surveySegments(gfaGraph) {
|
|
@@ -1772,9 +1784,9 @@ function sampleRowLayout(graph, region, keep = []) {
|
|
|
1772
1784
|
},
|
|
1773
1785
|
positions: nodePositions
|
|
1774
1786
|
});
|
|
1775
|
-
const
|
|
1787
|
+
const referenceSample2 = parsePanSN(backbone[0].stable.refName).sample;
|
|
1776
1788
|
const rowLabels = [
|
|
1777
|
-
{ label:
|
|
1789
|
+
{ label: referenceSample2, y: 0 },
|
|
1778
1790
|
...samples.map((sample) => ({
|
|
1779
1791
|
label: sample,
|
|
1780
1792
|
y: rowOf.get(sample) * ROW_HEIGHT_PX
|
|
@@ -2818,10 +2830,10 @@ function brightenAbgr(c, factor) {
|
|
|
2818
2830
|
|
|
2819
2831
|
// src/util/edgeCurves.ts
|
|
2820
2832
|
var cache = /* @__PURE__ */ new WeakMap();
|
|
2821
|
-
function baseEdgeCurves(nodePositions, graph, axis, deletions,
|
|
2833
|
+
function baseEdgeCurves(nodePositions, graph, axis, deletions, version2) {
|
|
2822
2834
|
const { scaleX, scaleY } = axis;
|
|
2823
2835
|
const cached2 = cache.get(nodePositions);
|
|
2824
|
-
if (cached2?.graph === graph && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.deletions === deletions && cached2.version ===
|
|
2836
|
+
if (cached2?.graph === graph && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.deletions === deletions && cached2.version === version2) {
|
|
2825
2837
|
return cached2.curves;
|
|
2826
2838
|
}
|
|
2827
2839
|
const curves = /* @__PURE__ */ new Map();
|
|
@@ -2850,7 +2862,7 @@ function baseEdgeCurves(nodePositions, graph, axis, deletions, version) {
|
|
|
2850
2862
|
scaleX,
|
|
2851
2863
|
scaleY,
|
|
2852
2864
|
deletions,
|
|
2853
|
-
version,
|
|
2865
|
+
version: version2,
|
|
2854
2866
|
curves
|
|
2855
2867
|
});
|
|
2856
2868
|
return curves;
|
|
@@ -3312,7 +3324,7 @@ function buildGeometry(options) {
|
|
|
3312
3324
|
referenceRamp,
|
|
3313
3325
|
deletions,
|
|
3314
3326
|
hiddenEdges,
|
|
3315
|
-
version = 0
|
|
3327
|
+
version: version2 = 0
|
|
3316
3328
|
} = options;
|
|
3317
3329
|
const depthNorm = nodeWidth2 === "depth" ? meanDepth(graph) : 0;
|
|
3318
3330
|
const scale = axis.scaleX;
|
|
@@ -3322,7 +3334,7 @@ function buildGeometry(options) {
|
|
|
3322
3334
|
graph,
|
|
3323
3335
|
axis,
|
|
3324
3336
|
deletions,
|
|
3325
|
-
|
|
3337
|
+
version2
|
|
3326
3338
|
);
|
|
3327
3339
|
const nodeStrokes = [];
|
|
3328
3340
|
const nodeStrokeRuns = /* @__PURE__ */ new Map();
|
|
@@ -3871,10 +3883,10 @@ function labelOrder(nodePositions, nodeLengths) {
|
|
|
3871
3883
|
return order;
|
|
3872
3884
|
}
|
|
3873
3885
|
var arcCache = /* @__PURE__ */ new WeakMap();
|
|
3874
|
-
function arcPlacements(nodePositions, deletions, axis,
|
|
3886
|
+
function arcPlacements(nodePositions, deletions, axis, version2) {
|
|
3875
3887
|
const { scaleX, scaleY } = axis;
|
|
3876
3888
|
const cached2 = arcCache.get(nodePositions);
|
|
3877
|
-
if (cached2?.deletions === deletions && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.version ===
|
|
3889
|
+
if (cached2?.deletions === deletions && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.version === version2) {
|
|
3878
3890
|
return cached2.arcs;
|
|
3879
3891
|
}
|
|
3880
3892
|
const arcs = [];
|
|
@@ -3892,7 +3904,7 @@ function arcPlacements(nodePositions, deletions, axis, version) {
|
|
|
3892
3904
|
});
|
|
3893
3905
|
}
|
|
3894
3906
|
}
|
|
3895
|
-
arcCache.set(nodePositions, { deletions, scaleX, scaleY, version, arcs });
|
|
3907
|
+
arcCache.set(nodePositions, { deletions, scaleX, scaleY, version: version2, arcs });
|
|
3896
3908
|
return arcs;
|
|
3897
3909
|
}
|
|
3898
3910
|
function sizeLabelCandidates({
|
|
@@ -3905,7 +3917,7 @@ function sizeLabelCandidates({
|
|
|
3905
3917
|
translateY,
|
|
3906
3918
|
width,
|
|
3907
3919
|
height,
|
|
3908
|
-
version = 0
|
|
3920
|
+
version: version2 = 0
|
|
3909
3921
|
}) {
|
|
3910
3922
|
const { scaleX, scaleY } = axis;
|
|
3911
3923
|
const deletionCandidates = [];
|
|
@@ -3914,7 +3926,7 @@ function sizeLabelCandidates({
|
|
|
3914
3926
|
nodePositions,
|
|
3915
3927
|
deletions,
|
|
3916
3928
|
axis,
|
|
3917
|
-
|
|
3929
|
+
version2
|
|
3918
3930
|
)) {
|
|
3919
3931
|
if (extent >= MIN_DELETION_LABEL_PX) {
|
|
3920
3932
|
const text = deletionText(deletion.bp);
|
|
@@ -4278,7 +4290,7 @@ function liftOf(walks, ramp) {
|
|
|
4278
4290
|
function rangeText(contig, start, end) {
|
|
4279
4291
|
const s = Math.round(start);
|
|
4280
4292
|
const e = Math.round(end);
|
|
4281
|
-
return `${contig ? `${contig}:` : ""}${s.toLocaleString()}-${e.toLocaleString()} (${formatBp(e - s)})`;
|
|
4293
|
+
return `${contig ? `${contig}:` : ""}${s.toLocaleString("en-US")}-${e.toLocaleString("en-US")} (${formatBp(e - s)})`;
|
|
4282
4294
|
}
|
|
4283
4295
|
function walkKey(walk, reference) {
|
|
4284
4296
|
const { field } = walk.encoding;
|
|
@@ -4292,6 +4304,15 @@ function walkKey(walk, reference) {
|
|
|
4292
4304
|
hover: scale === own ? void 0 : own
|
|
4293
4305
|
};
|
|
4294
4306
|
}
|
|
4307
|
+
function walkPosition(walk, nodeId, length) {
|
|
4308
|
+
const progress = walk.progress.get(nodeId);
|
|
4309
|
+
const range = walk.range;
|
|
4310
|
+
if (progress === void 0 || !range) {
|
|
4311
|
+
return void 0;
|
|
4312
|
+
}
|
|
4313
|
+
const start = Math.round(range.start + progress * walk.bp - length / 2);
|
|
4314
|
+
return `${range.contig}:${start.toLocaleString("en-US")}-${(start + length).toLocaleString("en-US")}`;
|
|
4315
|
+
}
|
|
4295
4316
|
|
|
4296
4317
|
// src/facetGrid.ts
|
|
4297
4318
|
var FACET_GAP_PX = 8;
|
|
@@ -4364,6 +4385,223 @@ function facetCells(names, by) {
|
|
|
4364
4385
|
return cells.includes(-1) || new Set(cells).size < cells.length ? wrap : { columns, count: rows.length * columns, cells };
|
|
4365
4386
|
}
|
|
4366
4387
|
|
|
4388
|
+
// src/genes/genePins.ts
|
|
4389
|
+
function genePins(graph, genes, positions) {
|
|
4390
|
+
const byRefName = /* @__PURE__ */ new Map();
|
|
4391
|
+
for (const node of graph.nodes) {
|
|
4392
|
+
if (isBackbone(node)) {
|
|
4393
|
+
const { refName } = node.stable;
|
|
4394
|
+
if (positions[node.id]?.length) {
|
|
4395
|
+
const entry = byRefName.get(refName) ?? byRefName.set(refName, { nodes: [], reach: 0 }).get(refName);
|
|
4396
|
+
entry.nodes.push(node);
|
|
4397
|
+
entry.reach = Math.max(entry.reach, node.length);
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
for (const { nodes } of byRefName.values()) {
|
|
4402
|
+
nodes.sort((a, b) => a.stable.start - b.stable.start);
|
|
4403
|
+
}
|
|
4404
|
+
const pins = [];
|
|
4405
|
+
for (const gene of genes) {
|
|
4406
|
+
const backbone = byRefName.get(gene.refName);
|
|
4407
|
+
if (!backbone) {
|
|
4408
|
+
continue;
|
|
4409
|
+
}
|
|
4410
|
+
const parts = [];
|
|
4411
|
+
const exonsByNode = [];
|
|
4412
|
+
let at;
|
|
4413
|
+
let atDistance = Infinity;
|
|
4414
|
+
let covered = 0;
|
|
4415
|
+
const mid = (gene.start + gene.end) / 2;
|
|
4416
|
+
const { nodes, reach } = backbone;
|
|
4417
|
+
for (let i = firstNodeAtOrAfter(nodes, gene.start - reach); i < nodes.length && nodes[i].stable.start < gene.end; i++) {
|
|
4418
|
+
const node = nodes[i];
|
|
4419
|
+
const nodeStart = node.stable.start;
|
|
4420
|
+
const nodeEnd = nodeStart + node.length;
|
|
4421
|
+
if (nodeEnd <= gene.start) {
|
|
4422
|
+
continue;
|
|
4423
|
+
}
|
|
4424
|
+
const line = positions[node.id];
|
|
4425
|
+
covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start);
|
|
4426
|
+
const nodeParts = [];
|
|
4427
|
+
for (const exon of gene.exons) {
|
|
4428
|
+
const a = Math.max(exon.start, nodeStart);
|
|
4429
|
+
const b = Math.min(exon.end, nodeEnd);
|
|
4430
|
+
if (b <= a) {
|
|
4431
|
+
continue;
|
|
4432
|
+
}
|
|
4433
|
+
const stretch = polylineSlice(
|
|
4434
|
+
line,
|
|
4435
|
+
(a - nodeStart) / node.length,
|
|
4436
|
+
(b - nodeStart) / node.length
|
|
4437
|
+
);
|
|
4438
|
+
if (stretch.length === 1) {
|
|
4439
|
+
stretch.push({ ...stretch[0] });
|
|
4440
|
+
}
|
|
4441
|
+
nodeParts.push(svgPath(stretch));
|
|
4442
|
+
}
|
|
4443
|
+
if (nodeParts.length > 0) {
|
|
4444
|
+
parts.push(...nodeParts);
|
|
4445
|
+
exonsByNode.push({ nodeId: node.id, d: nodeParts.join("") });
|
|
4446
|
+
}
|
|
4447
|
+
const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd);
|
|
4448
|
+
const distance = Math.abs(pinBp - mid);
|
|
4449
|
+
if (distance < atDistance) {
|
|
4450
|
+
atDistance = distance;
|
|
4451
|
+
const [p] = polylineSlice(
|
|
4452
|
+
line,
|
|
4453
|
+
(pinBp - nodeStart) / node.length,
|
|
4454
|
+
(pinBp - nodeStart) / node.length
|
|
4455
|
+
);
|
|
4456
|
+
at = p;
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
if (at) {
|
|
4460
|
+
pins.push({
|
|
4461
|
+
gene,
|
|
4462
|
+
exons: parts.join(""),
|
|
4463
|
+
exonsByNode,
|
|
4464
|
+
at,
|
|
4465
|
+
covered: covered / (gene.end - gene.start)
|
|
4466
|
+
});
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
return pins;
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4472
|
+
// src/labelLayout.ts
|
|
4473
|
+
var HALO_FACTOR = 3.4;
|
|
4474
|
+
var LEGEND_INSET_PX = 6;
|
|
4475
|
+
var ROUTE_STACK = 8;
|
|
4476
|
+
var GENE_PIN_DROP_PX = 18;
|
|
4477
|
+
var GENE_STACK = 2;
|
|
4478
|
+
var TOPMOST_BASELINE = LABEL_PX + LABEL_PAD;
|
|
4479
|
+
function geneCoverageNote(pin) {
|
|
4480
|
+
const length = pin.gene.end - pin.gene.start;
|
|
4481
|
+
if (pin.covered >= 0.98) {
|
|
4482
|
+
return void 0;
|
|
4483
|
+
}
|
|
4484
|
+
const [per, unit] = length >= 1e6 ? [1e6, "Mb"] : length >= 1e3 ? [1e3, "kb"] : [1, "bp"];
|
|
4485
|
+
const amount = (bp) => +(bp / per).toFixed(per === 1 ? 0 : 1);
|
|
4486
|
+
return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`;
|
|
4487
|
+
}
|
|
4488
|
+
function geneLabelCandidates(pins, screen, contigThickness) {
|
|
4489
|
+
return byExtent(pins, (pin) => pin.gene.end - pin.gene.start).map((pin) => {
|
|
4490
|
+
const { x, y } = screen(pin.at);
|
|
4491
|
+
const note = geneCoverageNote(pin);
|
|
4492
|
+
return {
|
|
4493
|
+
item: pin,
|
|
4494
|
+
x,
|
|
4495
|
+
y: y + contigThickness + GENE_PIN_DROP_PX,
|
|
4496
|
+
text: note ? `${pin.gene.name} \xB7 ${note}` : pin.gene.name,
|
|
4497
|
+
fallback: note ? `${pin.gene.name} \u2026` : void 0,
|
|
4498
|
+
stack: GENE_STACK
|
|
4499
|
+
};
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
function byExtent(items, extent) {
|
|
4503
|
+
return [...items].sort((a, b) => extent(b) - extent(a));
|
|
4504
|
+
}
|
|
4505
|
+
function layoutLabels(m) {
|
|
4506
|
+
const { paneWidth: width, canvasHeight: height, translateX, translateY } = m;
|
|
4507
|
+
const { scaleX, scaleY } = m.axisScale;
|
|
4508
|
+
const frame = { width, height };
|
|
4509
|
+
const screen = (p) => ({
|
|
4510
|
+
x: p.x * scaleX + translateX,
|
|
4511
|
+
y: p.y * scaleY + translateY
|
|
4512
|
+
});
|
|
4513
|
+
const reserved = m.drawnRowLabels.map(
|
|
4514
|
+
({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
|
|
4515
|
+
);
|
|
4516
|
+
const stripPx = m.referenceStripZonePx ?? 0;
|
|
4517
|
+
if (stripPx > 0) {
|
|
4518
|
+
reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
|
|
4519
|
+
}
|
|
4520
|
+
if (m.legendSize.width > 0) {
|
|
4521
|
+
reserved.push({
|
|
4522
|
+
x0: width - LEGEND_INSET_PX - m.legendSize.width,
|
|
4523
|
+
x1: width,
|
|
4524
|
+
y0: 0,
|
|
4525
|
+
y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4528
|
+
if (m.poppedFrom) {
|
|
4529
|
+
reserved.push({
|
|
4530
|
+
x0: 0,
|
|
4531
|
+
x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,
|
|
4532
|
+
y0: 0,
|
|
4533
|
+
y1: 44
|
|
4534
|
+
});
|
|
4535
|
+
}
|
|
4536
|
+
const take = occupancy(frame, reserved);
|
|
4537
|
+
const onRows = m.drawnRowLabels.length > 0;
|
|
4538
|
+
const rowsTop = onRows ? Math.min(...m.drawnRowLabels.map((r) => r.y)) : Infinity;
|
|
4539
|
+
const halo = m.contigThickness * HALO_FACTOR;
|
|
4540
|
+
const byBubble = byExtent(m.bubbleHalos, (h) => h.members);
|
|
4541
|
+
const bubbles = placeLabels(
|
|
4542
|
+
byBubble.map((h) => {
|
|
4543
|
+
const { x, y } = screen({
|
|
4544
|
+
x: h.labelAt.x,
|
|
4545
|
+
y: Math.min(h.labelAt.y, rowsTop)
|
|
4546
|
+
});
|
|
4547
|
+
const baseline = y - halo / 2 - 6;
|
|
4548
|
+
return {
|
|
4549
|
+
item: h,
|
|
4550
|
+
x,
|
|
4551
|
+
y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,
|
|
4552
|
+
text: h.label
|
|
4553
|
+
};
|
|
4554
|
+
}),
|
|
4555
|
+
frame,
|
|
4556
|
+
take
|
|
4557
|
+
);
|
|
4558
|
+
const genes = placeLabels(
|
|
4559
|
+
geneLabelCandidates(m.genePins, screen, m.contigThickness),
|
|
4560
|
+
frame,
|
|
4561
|
+
take
|
|
4562
|
+
);
|
|
4563
|
+
const sizeCandidates = m.nodePositions && m.labelsNodeSizes ? sizeLabelCandidates({
|
|
4564
|
+
nodePositions: m.nodePositions,
|
|
4565
|
+
nodeLengths: m.nodeLengths,
|
|
4566
|
+
deletions: m.showDeletionEdges ? m.deletions : [],
|
|
4567
|
+
alleleDeletions: m.alleleDeletions,
|
|
4568
|
+
axis: m.axisScale,
|
|
4569
|
+
translateX,
|
|
4570
|
+
translateY,
|
|
4571
|
+
width,
|
|
4572
|
+
height,
|
|
4573
|
+
version: m.positionsVersion
|
|
4574
|
+
}) : { deletions: [], nodes: [] };
|
|
4575
|
+
const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
|
|
4576
|
+
const routes = placeLabels(
|
|
4577
|
+
byBubble.flatMap(
|
|
4578
|
+
(h) => h.routes.map((route) => {
|
|
4579
|
+
const { x, y } = screen(route.at);
|
|
4580
|
+
return {
|
|
4581
|
+
item: { halo: h, route },
|
|
4582
|
+
x,
|
|
4583
|
+
y: y + 4,
|
|
4584
|
+
text: route.text,
|
|
4585
|
+
stack: ROUTE_STACK
|
|
4586
|
+
};
|
|
4587
|
+
})
|
|
4588
|
+
),
|
|
4589
|
+
frame,
|
|
4590
|
+
take
|
|
4591
|
+
);
|
|
4592
|
+
const nodeLabels = placeSizeLabels(
|
|
4593
|
+
sizeCandidates.nodes,
|
|
4594
|
+
take,
|
|
4595
|
+
nodeLabelBudget(width, height)
|
|
4596
|
+
);
|
|
4597
|
+
return {
|
|
4598
|
+
bubbles,
|
|
4599
|
+
genes,
|
|
4600
|
+
routes,
|
|
4601
|
+
sizes: [...deletionLabels, ...nodeLabels]
|
|
4602
|
+
};
|
|
4603
|
+
}
|
|
4604
|
+
|
|
4367
4605
|
// src/renderer/svgCanvas.ts
|
|
4368
4606
|
var n = (v) => +v.toFixed(1);
|
|
4369
4607
|
function paint(css) {
|
|
@@ -4380,9 +4618,43 @@ function paintAttrs(kind, css) {
|
|
|
4380
4618
|
const { color, opacity } = paint(css);
|
|
4381
4619
|
return `${kind}="${color}"${opacity < 1 ? ` ${kind}-opacity="${n(opacity)}"` : ""}`;
|
|
4382
4620
|
}
|
|
4621
|
+
var DOT_PX = 0.5;
|
|
4622
|
+
var DOT_CELL_PX = 0.25;
|
|
4383
4623
|
function svgCanvas(width, height) {
|
|
4384
4624
|
const out = [];
|
|
4385
|
-
let
|
|
4625
|
+
let subpaths = [];
|
|
4626
|
+
const start = (x, y) => {
|
|
4627
|
+
const s = { d: `M${n(x)} ${n(y)}`, segments: 0, x0: x, x1: x, y0: y, y1: y };
|
|
4628
|
+
subpaths.push(s);
|
|
4629
|
+
return s;
|
|
4630
|
+
};
|
|
4631
|
+
const reach = (points) => {
|
|
4632
|
+
const s = subpaths.at(-1) ?? start(points[0], points[1]);
|
|
4633
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
4634
|
+
s.x0 = Math.min(s.x0, points[i]);
|
|
4635
|
+
s.x1 = Math.max(s.x1, points[i]);
|
|
4636
|
+
s.y0 = Math.min(s.y0, points[i + 1]);
|
|
4637
|
+
s.y1 = Math.max(s.y1, points[i + 1]);
|
|
4638
|
+
}
|
|
4639
|
+
s.segments++;
|
|
4640
|
+
return s;
|
|
4641
|
+
};
|
|
4642
|
+
const pathData = (dots) => {
|
|
4643
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4644
|
+
return subpaths.map((s) => {
|
|
4645
|
+
if (!dots || s.x1 - s.x0 >= DOT_PX || s.y1 - s.y0 >= DOT_PX) {
|
|
4646
|
+
return s.d;
|
|
4647
|
+
}
|
|
4648
|
+
const x = (s.x0 + s.x1) / 2;
|
|
4649
|
+
const y = (s.y0 + s.y1) / 2;
|
|
4650
|
+
const cell = `${Math.round(x / DOT_CELL_PX)} ${Math.round(y / DOT_CELL_PX)}`;
|
|
4651
|
+
if (s.segments === 0 || seen.has(cell)) {
|
|
4652
|
+
return "";
|
|
4653
|
+
}
|
|
4654
|
+
seen.add(cell);
|
|
4655
|
+
return `M${n(x)} ${n(y)}h0`;
|
|
4656
|
+
}).join("");
|
|
4657
|
+
};
|
|
4386
4658
|
const ctx = {
|
|
4387
4659
|
canvas: void 0,
|
|
4388
4660
|
fillStyle: "#000",
|
|
@@ -4391,21 +4663,25 @@ function svgCanvas(width, height) {
|
|
|
4391
4663
|
lineCap: "butt",
|
|
4392
4664
|
lineJoin: "miter",
|
|
4393
4665
|
beginPath() {
|
|
4394
|
-
|
|
4666
|
+
subpaths = [];
|
|
4395
4667
|
},
|
|
4396
4668
|
moveTo(x, y) {
|
|
4397
|
-
|
|
4669
|
+
start(x, y);
|
|
4398
4670
|
},
|
|
4399
4671
|
lineTo(x, y) {
|
|
4400
|
-
d += `L${n(x)} ${n(y)}`;
|
|
4672
|
+
reach([x, y]).d += `L${n(x)} ${n(y)}`;
|
|
4401
4673
|
},
|
|
4402
4674
|
bezierCurveTo(cx0, cy0, cx1, cy1, x, y) {
|
|
4403
|
-
d += `C${n(cx0)} ${n(cy0)} ${n(cx1)} ${n(cy1)} ${n(x)} ${n(y)}`;
|
|
4675
|
+
reach([cx0, cy0, cx1, cy1, x, y]).d += `C${n(cx0)} ${n(cy0)} ${n(cx1)} ${n(cy1)} ${n(x)} ${n(y)}`;
|
|
4404
4676
|
},
|
|
4405
4677
|
closePath() {
|
|
4406
|
-
|
|
4678
|
+
const s = subpaths.at(-1);
|
|
4679
|
+
if (s) {
|
|
4680
|
+
s.d += "Z";
|
|
4681
|
+
}
|
|
4407
4682
|
},
|
|
4408
4683
|
stroke() {
|
|
4684
|
+
const d = pathData(ctx.lineCap !== "butt");
|
|
4409
4685
|
if (d) {
|
|
4410
4686
|
out.push(
|
|
4411
4687
|
`<path d="${d}" fill="none" ${paintAttrs("stroke", ctx.strokeStyle)} stroke-width="${n(ctx.lineWidth)}" stroke-linecap="${ctx.lineCap}" stroke-linejoin="${ctx.lineJoin}"/>`
|
|
@@ -4413,6 +4689,7 @@ function svgCanvas(width, height) {
|
|
|
4413
4689
|
}
|
|
4414
4690
|
},
|
|
4415
4691
|
fill() {
|
|
4692
|
+
const d = pathData(false);
|
|
4416
4693
|
if (d) {
|
|
4417
4694
|
out.push(`<path d="${d}" ${paintAttrs("fill", ctx.fillStyle)}/>`);
|
|
4418
4695
|
}
|
|
@@ -4437,6 +4714,9 @@ function svgCanvas(width, height) {
|
|
|
4437
4714
|
};
|
|
4438
4715
|
}
|
|
4439
4716
|
|
|
4717
|
+
// src/version.ts
|
|
4718
|
+
var version = "4.0.26";
|
|
4719
|
+
|
|
4440
4720
|
// src/viewport.ts
|
|
4441
4721
|
function axisScaleOf(scale, pixelRows) {
|
|
4442
4722
|
return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows };
|
|
@@ -4484,6 +4764,11 @@ var BAR_PX = 48;
|
|
|
4484
4764
|
var SWATCH_PX = 18;
|
|
4485
4765
|
var KEY_GAP_PX = 16;
|
|
4486
4766
|
var FADED = "rgb(160,160,160)";
|
|
4767
|
+
var EXON_COLOR = "#daa520";
|
|
4768
|
+
var GENE_INK = "#1c1c22";
|
|
4769
|
+
var MIN_LANE_PX = 4;
|
|
4770
|
+
var EXON_GAP_PX = 1;
|
|
4771
|
+
var EXON_LINE_PX = 2;
|
|
4487
4772
|
function esc(s) {
|
|
4488
4773
|
return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4489
4774
|
}
|
|
@@ -4512,18 +4797,30 @@ function walkKeySvg(id, x, y, walk, label, reference) {
|
|
|
4512
4797
|
key.scale
|
|
4513
4798
|
);
|
|
4514
4799
|
}
|
|
4800
|
+
function screenPath(d, t) {
|
|
4801
|
+
return d.replaceAll(
|
|
4802
|
+
/([ML])(-?[\d.e-]+),(-?[\d.e-]+)/g,
|
|
4803
|
+
(_, cmd, x, y) => `${cmd}${+(Number(x) * t.scaleX + t.translateX).toFixed(1)},${+(Number(y) * t.scaleY + t.translateY).toFixed(1)}`
|
|
4804
|
+
);
|
|
4805
|
+
}
|
|
4806
|
+
function chip(x, y, w, name, note) {
|
|
4807
|
+
return `<rect x="${x - w / 2}" y="${y - LABEL_PX - LABEL_PAD + 2}" width="${w}" height="${LABEL_PX + LABEL_PAD * 2 - 2}" rx="3" fill="#fff" fill-opacity="0.85" stroke="${EXON_COLOR}"/><text x="${x}" y="${y}" font-family="Helvetica, Arial, sans-serif" font-size="${LABEL_PX}" fill="${GENE_INK}" text-anchor="middle"><tspan font-style="italic" font-weight="600">${esc(name)}</tspan>${note ? esc(note) : ""}</text>`;
|
|
4808
|
+
}
|
|
4515
4809
|
function figureSvg(graph, layout, o = {}) {
|
|
4516
4810
|
const width = o.width ?? 1200;
|
|
4517
4811
|
const room = o.height ?? 800;
|
|
4518
4812
|
const region = o.region;
|
|
4519
|
-
const
|
|
4813
|
+
const rampDomain = o.colorDomain ?? region;
|
|
4814
|
+
const bounds = drawingBounds(layout, {
|
|
4815
|
+
region: o.fitToDrawing ? void 0 : region
|
|
4816
|
+
});
|
|
4520
4817
|
const pixelRows = layout.pixelRows ?? false;
|
|
4521
4818
|
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
4522
4819
|
const colorScheme = resolveColorScheme(o.colorScheme ?? "auto", graph);
|
|
4523
|
-
const referenceRamp = colorScheme === "reference-position" && !layout.tubeMap ? computeReferenceRamp(graph,
|
|
4820
|
+
const referenceRamp = colorScheme === "reference-position" && !layout.tubeMap ? computeReferenceRamp(graph, rampDomain) : void 0;
|
|
4524
4821
|
const deletions = layout.tubeMap ? [] : deletionEdges(graph);
|
|
4525
4822
|
const layers = o.walks ?? [];
|
|
4526
|
-
const walkRamp = layers.some((l) => l.color?.field === "reference") ? referenceRamp ?? computeReferenceRamp(graph,
|
|
4823
|
+
const walkRamp = layers.some((l) => l.color?.field === "reference") ? referenceRamp ?? computeReferenceRamp(graph, rampDomain) : void 0;
|
|
4527
4824
|
const lift = layers.length > 0 && !layout.tubeMap ? walkLift(graph, layers, walkRamp) : void 0;
|
|
4528
4825
|
const by = o.facet ?? "none";
|
|
4529
4826
|
const panels = by !== "none" && lift && lift.walks.length > 1 ? facetLifts(graph, lift, layers, walkRamp) : void 0;
|
|
@@ -4538,6 +4835,58 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4538
4835
|
...lift.referenceDomain,
|
|
4539
4836
|
name: region?.refName
|
|
4540
4837
|
};
|
|
4838
|
+
const contigThickness = o.contigThickness ?? 6;
|
|
4839
|
+
const nodeWidth2 = o.nodeWidth ?? "depth";
|
|
4840
|
+
const pins = o.genes?.length && !layout.tubeMap ? genePins(graph, o.genes, layout.nodePositions) : [];
|
|
4841
|
+
const ink = nodeInk(graph, nodeById, contigThickness, nodeWidth2);
|
|
4842
|
+
let masks = 0;
|
|
4843
|
+
function overlays(highlight, t, w, h) {
|
|
4844
|
+
const out = [];
|
|
4845
|
+
const inkPx = (nodeId) => {
|
|
4846
|
+
const own = ink.halfWidthPx(nodeId) * 2;
|
|
4847
|
+
return highlight?.nodeIds.has(nodeId) ? Math.max(own, highlight.walks.length * MIN_LANE_PX) : own;
|
|
4848
|
+
};
|
|
4849
|
+
const stretches = pins.flatMap(
|
|
4850
|
+
(pin) => pin.exonsByNode.map(({ nodeId, d }) => ({
|
|
4851
|
+
d: screenPath(d, t),
|
|
4852
|
+
inner: inkPx(nodeId) + 2 * EXON_GAP_PX
|
|
4853
|
+
}))
|
|
4854
|
+
);
|
|
4855
|
+
if (stretches.length > 0) {
|
|
4856
|
+
const id = `exons${masks++}`;
|
|
4857
|
+
const stroke = (color, width2, d) => `<path d="${d}" fill="none" stroke="${color}" stroke-width="${width2}" stroke-linecap="round" stroke-linejoin="round"/>`;
|
|
4858
|
+
out.push(
|
|
4859
|
+
`<mask id="${id}" maskUnits="userSpaceOnUse" x="0" y="0" width="${w}" height="${h}">${stretches.map((s) => stroke("#fff", s.inner + 2 * EXON_LINE_PX, s.d)).join("")}${stretches.map((s) => stroke("#000", s.inner, s.d)).join(
|
|
4860
|
+
""
|
|
4861
|
+
)}</mask><rect width="${w}" height="${h}" fill="${EXON_COLOR}" mask="url(#${id})"/>`
|
|
4862
|
+
);
|
|
4863
|
+
}
|
|
4864
|
+
const screen = (p) => ({
|
|
4865
|
+
x: p.x * t.scaleX + t.translateX,
|
|
4866
|
+
y: p.y * t.scaleY + t.translateY
|
|
4867
|
+
});
|
|
4868
|
+
const frame = { width: w, height: h };
|
|
4869
|
+
for (const { item: pin, x, y, w: cw, text } of placeLabels(
|
|
4870
|
+
geneLabelCandidates(pins, screen, contigThickness),
|
|
4871
|
+
frame,
|
|
4872
|
+
occupancy(frame)
|
|
4873
|
+
)) {
|
|
4874
|
+
const pinY = screen(pin.at).y + contigThickness / 2;
|
|
4875
|
+
out.push(
|
|
4876
|
+
`<line x1="${x}" x2="${x}" y1="${y - LABEL_PX - 2}" y2="${pinY}" stroke="${GENE_INK}" stroke-width="0.8" stroke-opacity="0.6"/>`,
|
|
4877
|
+
chip(x, y, cw, pin.gene.name, text.slice(pin.gene.name.length))
|
|
4878
|
+
);
|
|
4879
|
+
}
|
|
4880
|
+
for (const { label, y } of layout.rowLabels ?? []) {
|
|
4881
|
+
const sy = y * t.scaleY + t.translateY;
|
|
4882
|
+
if (sy >= 0 && sy <= h) {
|
|
4883
|
+
out.push(
|
|
4884
|
+
`<text x="6" y="${sy + 4}" ${FONT} stroke="#fff" stroke-width="3" paint-order="stroke">${esc(label)}</text>`
|
|
4885
|
+
);
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
return out.join("");
|
|
4889
|
+
}
|
|
4541
4890
|
function drawing(highlight, w, h) {
|
|
4542
4891
|
const fit = fitTransform(
|
|
4543
4892
|
bounds,
|
|
@@ -4563,10 +4912,10 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4563
4912
|
graph,
|
|
4564
4913
|
nodeById,
|
|
4565
4914
|
colorScheme,
|
|
4566
|
-
contigThickness
|
|
4915
|
+
contigThickness,
|
|
4567
4916
|
connectorThickness: o.connectorThickness ?? 2,
|
|
4568
4917
|
drawPaths: false,
|
|
4569
|
-
nodeWidth:
|
|
4918
|
+
nodeWidth: nodeWidth2,
|
|
4570
4919
|
highlight,
|
|
4571
4920
|
axis,
|
|
4572
4921
|
referenceRamp,
|
|
@@ -4576,15 +4925,15 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4576
4925
|
)
|
|
4577
4926
|
})
|
|
4578
4927
|
);
|
|
4579
|
-
|
|
4928
|
+
const t = {
|
|
4580
4929
|
scaleX: axis.scaleX,
|
|
4581
4930
|
scaleY: axis.scaleY,
|
|
4582
4931
|
translateX: fit.translateX,
|
|
4583
|
-
translateY: fit.translateY
|
|
4584
|
-
|
|
4585
|
-
});
|
|
4932
|
+
translateY: fit.translateY
|
|
4933
|
+
};
|
|
4934
|
+
renderer.updateTransform({ ...t, dpr: 1 });
|
|
4586
4935
|
renderer.render([1, 1, 1, 1]);
|
|
4587
|
-
return markup();
|
|
4936
|
+
return markup() + overlays(highlight, t, w, h);
|
|
4588
4937
|
}
|
|
4589
4938
|
const parts = [];
|
|
4590
4939
|
let height;
|
|
@@ -4663,10 +5012,169 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4663
5012
|
);
|
|
4664
5013
|
height = header + h;
|
|
4665
5014
|
}
|
|
4666
|
-
|
|
5015
|
+
const metadata = JSON.stringify({
|
|
5016
|
+
generator: `@jbrowse/bandage-core@${version}`,
|
|
5017
|
+
...o.spec === void 0 ? {} : { spec: o.spec }
|
|
5018
|
+
});
|
|
5019
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><metadata>${esc(metadata)}</metadata><rect width="${width}" height="${height}" fill="#fff"/>${parts.join("")}</svg>
|
|
4667
5020
|
`;
|
|
4668
5021
|
}
|
|
4669
5022
|
|
|
5023
|
+
// src/genes/geneFiles.ts
|
|
5024
|
+
var GENE_TYPE = /(gene|gene_segment|RNA|transcript)$/i;
|
|
5025
|
+
var isGene = (type) => GENE_TYPE.test(type) || type === "CDS" || type === "exon";
|
|
5026
|
+
function mergedIntervals(intervals) {
|
|
5027
|
+
const out = [];
|
|
5028
|
+
for (const iv of [...intervals].sort((a, b) => a.start - b.start)) {
|
|
5029
|
+
const last = out.at(-1);
|
|
5030
|
+
if (last && iv.start <= last.end) {
|
|
5031
|
+
last.end = Math.max(last.end, iv.end);
|
|
5032
|
+
} else {
|
|
5033
|
+
out.push({ ...iv });
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
return out;
|
|
5037
|
+
}
|
|
5038
|
+
function strandOf(s) {
|
|
5039
|
+
return s === "+" ? 1 : s === "-" ? -1 : 0;
|
|
5040
|
+
}
|
|
5041
|
+
function unescape(value) {
|
|
5042
|
+
try {
|
|
5043
|
+
return decodeURIComponent(value);
|
|
5044
|
+
} catch {
|
|
5045
|
+
return value;
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
function attributes(column) {
|
|
5049
|
+
const out = /* @__PURE__ */ new Map();
|
|
5050
|
+
for (const pair of column.split(";")) {
|
|
5051
|
+
const eq = pair.indexOf("=");
|
|
5052
|
+
if (eq > 0) {
|
|
5053
|
+
out.set(pair.slice(0, eq).trim(), unescape(pair.slice(eq + 1).trim()));
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
return out;
|
|
5057
|
+
}
|
|
5058
|
+
function gff3Row(line) {
|
|
5059
|
+
const cols = line.split(" ");
|
|
5060
|
+
const start = Number(cols[3]);
|
|
5061
|
+
const end = Number(cols[4]);
|
|
5062
|
+
if (cols.length < 9 || !Number.isInteger(start) || !Number.isInteger(end)) {
|
|
5063
|
+
return void 0;
|
|
5064
|
+
}
|
|
5065
|
+
return {
|
|
5066
|
+
refName: cols[0],
|
|
5067
|
+
type: cols[2],
|
|
5068
|
+
start: start - 1,
|
|
5069
|
+
end,
|
|
5070
|
+
strand: strandOf(cols[6]),
|
|
5071
|
+
attrs: attributes(cols[8])
|
|
5072
|
+
};
|
|
5073
|
+
}
|
|
5074
|
+
function genesFromGff3Lines(lines) {
|
|
5075
|
+
const rows = [];
|
|
5076
|
+
for (const line of lines) {
|
|
5077
|
+
if (line.startsWith("##FASTA")) {
|
|
5078
|
+
break;
|
|
5079
|
+
}
|
|
5080
|
+
const row = line.startsWith("#") ? void 0 : gff3Row(line);
|
|
5081
|
+
if (row) {
|
|
5082
|
+
rows.push(row);
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
5086
|
+
for (const { attrs } of rows) {
|
|
5087
|
+
const id = attrs.get("ID");
|
|
5088
|
+
const parent = attrs.get("Parent")?.split(",")[0];
|
|
5089
|
+
if (id && parent) {
|
|
5090
|
+
parentOf.set(id, parent);
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
const rootOf = (row) => {
|
|
5094
|
+
let id = row.attrs.get("Parent")?.split(",")[0] ?? row.attrs.get("ID");
|
|
5095
|
+
if (!id) {
|
|
5096
|
+
return row.attrs.get("gene_id");
|
|
5097
|
+
}
|
|
5098
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5099
|
+
while (id && parentOf.has(id) && !seen.has(id)) {
|
|
5100
|
+
seen.add(id);
|
|
5101
|
+
id = parentOf.get(id);
|
|
5102
|
+
}
|
|
5103
|
+
return id;
|
|
5104
|
+
};
|
|
5105
|
+
const groups = /* @__PURE__ */ new Map();
|
|
5106
|
+
rows.forEach((row, i) => {
|
|
5107
|
+
const root = rootOf(row) ?? `#${i}`;
|
|
5108
|
+
const key = `${row.refName} ${root}`;
|
|
5109
|
+
const group = groups.get(key) ?? groups.set(key, { rows: [] }).get(key);
|
|
5110
|
+
group.rows.push(row);
|
|
5111
|
+
if (!row.attrs.has("Parent") && !group.top) {
|
|
5112
|
+
group.top = row;
|
|
5113
|
+
}
|
|
5114
|
+
});
|
|
5115
|
+
const genes = [];
|
|
5116
|
+
for (const [key, { top, rows: rows2 }] of groups) {
|
|
5117
|
+
if (top && !isGene(top.type)) {
|
|
5118
|
+
continue;
|
|
5119
|
+
}
|
|
5120
|
+
const first = top ?? rows2[0];
|
|
5121
|
+
const start = top?.start ?? Math.min(...rows2.map((r) => r.start));
|
|
5122
|
+
const end = top?.end ?? Math.max(...rows2.map((r) => r.end));
|
|
5123
|
+
const a = first.attrs;
|
|
5124
|
+
const exons = rows2.filter((r) => r.type === "exon").map((r) => ({ start: r.start, end: r.end }));
|
|
5125
|
+
genes.push({
|
|
5126
|
+
name: a.get("Name") ?? a.get("gene_name") ?? a.get("gene") ?? a.get("gene_id") ?? key.split(" ")[1],
|
|
5127
|
+
refName: first.refName,
|
|
5128
|
+
start,
|
|
5129
|
+
end,
|
|
5130
|
+
strand: first.strand,
|
|
5131
|
+
exons: exons.length ? mergedIntervals(exons) : [{ start, end }]
|
|
5132
|
+
});
|
|
5133
|
+
}
|
|
5134
|
+
return genes;
|
|
5135
|
+
}
|
|
5136
|
+
function genesFromBed(text) {
|
|
5137
|
+
const genes = /* @__PURE__ */ new Map();
|
|
5138
|
+
for (const line of text.split("\n")) {
|
|
5139
|
+
const cols = line.replace(/\r$/, "").split(" ");
|
|
5140
|
+
const start = Number(cols[1]);
|
|
5141
|
+
const end = Number(cols[2]);
|
|
5142
|
+
if (/^(#|track|browser)/.test(line) || cols.length < 3 || !Number.isInteger(start) || !(end > start)) {
|
|
5143
|
+
continue;
|
|
5144
|
+
}
|
|
5145
|
+
const refName = cols[0];
|
|
5146
|
+
const name = cols[3] && cols[3] !== "." ? cols[3] : `${refName}:${start + 1}-${end}`;
|
|
5147
|
+
const sizes = cols[10]?.split(",").filter((s) => s !== "").map(Number);
|
|
5148
|
+
const starts = cols[11]?.split(",").filter((s) => s !== "").map(Number);
|
|
5149
|
+
const blocks = sizes?.length && sizes.length === starts?.length ? sizes.map((size, i) => ({
|
|
5150
|
+
start: start + starts[i],
|
|
5151
|
+
end: start + starts[i] + size
|
|
5152
|
+
})) : [{ start, end }];
|
|
5153
|
+
const key = `${refName} ${name}`;
|
|
5154
|
+
const gene = genes.get(key);
|
|
5155
|
+
if (gene) {
|
|
5156
|
+
gene.start = Math.min(gene.start, start);
|
|
5157
|
+
gene.end = Math.max(gene.end, end);
|
|
5158
|
+
gene.exons = mergedIntervals([...gene.exons, ...blocks]);
|
|
5159
|
+
} else {
|
|
5160
|
+
genes.set(key, {
|
|
5161
|
+
name,
|
|
5162
|
+
refName,
|
|
5163
|
+
start,
|
|
5164
|
+
end,
|
|
5165
|
+
strand: strandOf(cols[5]),
|
|
5166
|
+
exons: mergedIntervals(blocks)
|
|
5167
|
+
});
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
return [...genes.values()];
|
|
5171
|
+
}
|
|
5172
|
+
function genesFromText(text) {
|
|
5173
|
+
const first = text.split("\n", 1e3).find((l) => l.trim() !== "" && !/^(#|track|browser)/.test(l));
|
|
5174
|
+
const cols = first?.split(" ") ?? [];
|
|
5175
|
+
return /^\d+$/.test(cols[1] ?? "") && /^\d+$/.test(cols[2] ?? "") ? genesFromBed(text) : genesFromGff3Lines(text.split(/\r?\n/));
|
|
5176
|
+
}
|
|
5177
|
+
|
|
4670
5178
|
// src/reference.ts
|
|
4671
5179
|
var WELL_KNOWN_SAMPLES = /* @__PURE__ */ new Map([
|
|
4672
5180
|
["hg38", "GRCh38"],
|
|
@@ -4841,9 +5349,9 @@ function resolveReferenceSample({
|
|
|
4841
5349
|
}
|
|
4842
5350
|
return sample;
|
|
4843
5351
|
}
|
|
4844
|
-
async function referencePathQuery(db,
|
|
5352
|
+
async function referencePathQuery(db, referenceSample2, refName) {
|
|
4845
5353
|
const path = (await db.paths()).find(
|
|
4846
|
-
(p) => p.isIndexed && p.name.sample ===
|
|
5354
|
+
(p) => p.isIndexed && p.name.sample === referenceSample2 && p.name.contig === refName
|
|
4847
5355
|
);
|
|
4848
5356
|
return path ? {
|
|
4849
5357
|
sample: path.name.sample,
|
|
@@ -4872,6 +5380,60 @@ async function cutWindowGFA(db, query, start, end, { keep, ...opts }) {
|
|
|
4872
5380
|
return subgraph ? subgraph.toGFA({ names: "resolved" }) : "";
|
|
4873
5381
|
}
|
|
4874
5382
|
|
|
5383
|
+
// src/gbzCut.ts
|
|
5384
|
+
import { GBZBase } from "@gmod/gbz-base";
|
|
5385
|
+
var HPRC_GBZ = {
|
|
5386
|
+
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",
|
|
5387
|
+
index: "https://jbrowse.org/demos/hprc/hprc-v2.1-mc-grch38.haplotype-index.anchored.db"
|
|
5388
|
+
};
|
|
5389
|
+
var WALK_LIMIT = 1e5;
|
|
5390
|
+
function parseRegion(text) {
|
|
5391
|
+
const m = /^\s*([^:\s]+):([\d,]+)-([\d,]+)\s*$/.exec(text);
|
|
5392
|
+
if (!m) {
|
|
5393
|
+
throw new Error(
|
|
5394
|
+
`"${text}" is not a region like chr6:160,614,798-160,647,758`
|
|
5395
|
+
);
|
|
5396
|
+
}
|
|
5397
|
+
const n2 = (s) => Number(s.replaceAll(",", ""));
|
|
5398
|
+
return { refName: m[1], start: n2(m[2]), end: n2(m[3]) };
|
|
5399
|
+
}
|
|
5400
|
+
async function openGbz(db, index) {
|
|
5401
|
+
const base = await GBZBase.open(db, index ? { haplotypeIndex: index } : {});
|
|
5402
|
+
return { base, referenceSamples: await referenceSamplesOf(base) };
|
|
5403
|
+
}
|
|
5404
|
+
function referenceSample(ref, samples) {
|
|
5405
|
+
try {
|
|
5406
|
+
return resolveReferenceSample({
|
|
5407
|
+
configured: "",
|
|
5408
|
+
anchorPrefix: ref ?? samples[0] ?? "",
|
|
5409
|
+
referenceSamples: samples
|
|
5410
|
+
});
|
|
5411
|
+
} catch (e) {
|
|
5412
|
+
if (ref) {
|
|
5413
|
+
return ref;
|
|
5414
|
+
}
|
|
5415
|
+
throw e;
|
|
5416
|
+
}
|
|
5417
|
+
}
|
|
5418
|
+
async function cutGbzRegion({ base, referenceSamples }, src, signal) {
|
|
5419
|
+
const region = parseRegion(src.region);
|
|
5420
|
+
const sample = referenceSample(src.referenceSample, referenceSamples);
|
|
5421
|
+
const query = await referencePathQuery(base, sample, region.refName);
|
|
5422
|
+
if (!query) {
|
|
5423
|
+
throw new Error(`${sample} has no indexed path named ${region.refName}`);
|
|
5424
|
+
}
|
|
5425
|
+
signal?.throwIfAborted();
|
|
5426
|
+
const wanted = src.haplotypes?.length ? src.haplotypes : void 0;
|
|
5427
|
+
const text = await cutWindowGFA(base, query, region.start, region.end, {
|
|
5428
|
+
context: src.context ?? 1e3,
|
|
5429
|
+
snarls: src.snarls ?? "contained",
|
|
5430
|
+
limit: WALK_LIMIT,
|
|
5431
|
+
signal,
|
|
5432
|
+
...wanted ? { keep: (name) => haplotypeWanted(name, wanted) } : {}
|
|
5433
|
+
});
|
|
5434
|
+
return { text, region, sample };
|
|
5435
|
+
}
|
|
5436
|
+
|
|
4875
5437
|
// src/loadBandage.ts
|
|
4876
5438
|
var cached;
|
|
4877
5439
|
function loadBandage() {
|
|
@@ -4884,12 +5446,11 @@ function loadBandage() {
|
|
|
4884
5446
|
|
|
4885
5447
|
export {
|
|
4886
5448
|
isBackbone,
|
|
4887
|
-
firstNodeAtOrAfter,
|
|
4888
5449
|
BUBBLE_SPREADS,
|
|
4889
5450
|
panSNSample,
|
|
4890
5451
|
panSNHaplotype,
|
|
4891
5452
|
panSNContig,
|
|
4892
|
-
|
|
5453
|
+
trimOrigins,
|
|
4893
5454
|
loadGraph,
|
|
4894
5455
|
engineRequest,
|
|
4895
5456
|
engineKey,
|
|
@@ -4907,6 +5468,7 @@ export {
|
|
|
4907
5468
|
walkRowsExtent,
|
|
4908
5469
|
FORCE_LAYOUT_LABEL,
|
|
4909
5470
|
LAYOUT_MODES,
|
|
5471
|
+
LAYOUT_MODE_VALUES,
|
|
4910
5472
|
modeUsesLayoutEngine,
|
|
4911
5473
|
layoutModeByValue,
|
|
4912
5474
|
COLOR_SCHEMES,
|
|
@@ -4935,13 +5497,7 @@ export {
|
|
|
4935
5497
|
LABEL_PX,
|
|
4936
5498
|
LABEL_CHAR_PX,
|
|
4937
5499
|
LABEL_PAD,
|
|
4938
|
-
occupancy,
|
|
4939
|
-
placeLabels,
|
|
4940
|
-
rowLabelBox,
|
|
4941
5500
|
formatBp,
|
|
4942
|
-
sizeLabelCandidates,
|
|
4943
|
-
nodeLabelBudget,
|
|
4944
|
-
placeSizeLabels,
|
|
4945
5501
|
WALK_FIELDS,
|
|
4946
5502
|
WALK_SCHEMES,
|
|
4947
5503
|
resolveEncoding,
|
|
@@ -4953,12 +5509,19 @@ export {
|
|
|
4953
5509
|
facetLifts,
|
|
4954
5510
|
rangeText,
|
|
4955
5511
|
walkKey,
|
|
5512
|
+
walkPosition,
|
|
4956
5513
|
FACET_GAP_PX,
|
|
4957
5514
|
FACET_TITLE_PX,
|
|
4958
5515
|
FACET_PAD_PX,
|
|
4959
5516
|
facetGrid,
|
|
4960
5517
|
facetCells,
|
|
5518
|
+
genePins,
|
|
5519
|
+
HALO_FACTOR,
|
|
5520
|
+
LEGEND_INSET_PX,
|
|
5521
|
+
geneCoverageNote,
|
|
5522
|
+
layoutLabels,
|
|
4961
5523
|
svgCanvas,
|
|
5524
|
+
version,
|
|
4962
5525
|
axisScaleOf,
|
|
4963
5526
|
viewportOf,
|
|
4964
5527
|
padded,
|
|
@@ -4966,6 +5529,9 @@ export {
|
|
|
4966
5529
|
zoomAbout,
|
|
4967
5530
|
screenToLayout,
|
|
4968
5531
|
figureSvg,
|
|
5532
|
+
genesFromGff3Lines,
|
|
5533
|
+
genesFromBed,
|
|
5534
|
+
genesFromText,
|
|
4969
5535
|
WELL_KNOWN_SAMPLES,
|
|
4970
5536
|
wellKnownSample,
|
|
4971
5537
|
graphBackbone,
|
|
@@ -4978,5 +5544,9 @@ export {
|
|
|
4978
5544
|
resolveReferenceSample,
|
|
4979
5545
|
referencePathQuery,
|
|
4980
5546
|
cutWindowGFA,
|
|
5547
|
+
HPRC_GBZ,
|
|
5548
|
+
parseRegion,
|
|
5549
|
+
openGbz,
|
|
5550
|
+
cutGbzRegion,
|
|
4981
5551
|
loadBandage
|
|
4982
5552
|
};
|