@jbrowse/bandage-core 0.1.20 → 4.0.25
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 +4 -3
- package/dist/chunks/{chunk-3XDR3ZBS.js → chunk-B5ZHLU4N.js} +603 -41
- package/dist/cli/figure.js +61 -56
- package/dist/index.js +89 -273
- package/dist/types/cli/figure.d.ts +8 -8
- package/dist/types/figure.d.ts +3 -1
- package/dist/types/gbzCut.d.ts +33 -0
- package/dist/types/genes/geneFiles.d.ts +10 -0
- package/dist/types/index.d.ts +5 -1
- package/dist/types/labelLayout.d.ts +14 -0
- package/dist/types/overlayLabels.d.ts +1 -0
- package/dist/types/referenceStrip.d.ts +11 -8
- package/dist/types/version.d.ts +1 -0
- package/dist/types/walkKey.d.ts +1 -0
- package/package.json +3 -2
|
@@ -1772,9 +1772,9 @@ function sampleRowLayout(graph, region, keep = []) {
|
|
|
1772
1772
|
},
|
|
1773
1773
|
positions: nodePositions
|
|
1774
1774
|
});
|
|
1775
|
-
const
|
|
1775
|
+
const referenceSample2 = parsePanSN(backbone[0].stable.refName).sample;
|
|
1776
1776
|
const rowLabels = [
|
|
1777
|
-
{ label:
|
|
1777
|
+
{ label: referenceSample2, y: 0 },
|
|
1778
1778
|
...samples.map((sample) => ({
|
|
1779
1779
|
label: sample,
|
|
1780
1780
|
y: rowOf.get(sample) * ROW_HEIGHT_PX
|
|
@@ -2818,10 +2818,10 @@ function brightenAbgr(c, factor) {
|
|
|
2818
2818
|
|
|
2819
2819
|
// src/util/edgeCurves.ts
|
|
2820
2820
|
var cache = /* @__PURE__ */ new WeakMap();
|
|
2821
|
-
function baseEdgeCurves(nodePositions, graph, axis, deletions,
|
|
2821
|
+
function baseEdgeCurves(nodePositions, graph, axis, deletions, version2) {
|
|
2822
2822
|
const { scaleX, scaleY } = axis;
|
|
2823
2823
|
const cached2 = cache.get(nodePositions);
|
|
2824
|
-
if (cached2?.graph === graph && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.deletions === deletions && cached2.version ===
|
|
2824
|
+
if (cached2?.graph === graph && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.deletions === deletions && cached2.version === version2) {
|
|
2825
2825
|
return cached2.curves;
|
|
2826
2826
|
}
|
|
2827
2827
|
const curves = /* @__PURE__ */ new Map();
|
|
@@ -2850,7 +2850,7 @@ function baseEdgeCurves(nodePositions, graph, axis, deletions, version) {
|
|
|
2850
2850
|
scaleX,
|
|
2851
2851
|
scaleY,
|
|
2852
2852
|
deletions,
|
|
2853
|
-
version,
|
|
2853
|
+
version: version2,
|
|
2854
2854
|
curves
|
|
2855
2855
|
});
|
|
2856
2856
|
return curves;
|
|
@@ -3312,7 +3312,7 @@ function buildGeometry(options) {
|
|
|
3312
3312
|
referenceRamp,
|
|
3313
3313
|
deletions,
|
|
3314
3314
|
hiddenEdges,
|
|
3315
|
-
version = 0
|
|
3315
|
+
version: version2 = 0
|
|
3316
3316
|
} = options;
|
|
3317
3317
|
const depthNorm = nodeWidth2 === "depth" ? meanDepth(graph) : 0;
|
|
3318
3318
|
const scale = axis.scaleX;
|
|
@@ -3322,7 +3322,7 @@ function buildGeometry(options) {
|
|
|
3322
3322
|
graph,
|
|
3323
3323
|
axis,
|
|
3324
3324
|
deletions,
|
|
3325
|
-
|
|
3325
|
+
version2
|
|
3326
3326
|
);
|
|
3327
3327
|
const nodeStrokes = [];
|
|
3328
3328
|
const nodeStrokeRuns = /* @__PURE__ */ new Map();
|
|
@@ -3558,6 +3558,9 @@ var Canvas2DRenderer = class extends Canvas2DRenderingBackendBase {
|
|
|
3558
3558
|
}
|
|
3559
3559
|
const ctx = this.ctx;
|
|
3560
3560
|
const { width, height } = ctx.canvas;
|
|
3561
|
+
if (clearColor[3] < 1) {
|
|
3562
|
+
ctx.clearRect(0, 0, width, height);
|
|
3563
|
+
}
|
|
3561
3564
|
ctx.fillStyle = normalizedRgbToCssRgba(
|
|
3562
3565
|
[clearColor[0], clearColor[1], clearColor[2]],
|
|
3563
3566
|
clearColor[3]
|
|
@@ -3742,8 +3745,8 @@ function occupancy(frame, reserved = []) {
|
|
|
3742
3745
|
function placeLabels(candidates, frame, take) {
|
|
3743
3746
|
const out = [];
|
|
3744
3747
|
const row = LABEL_PX + LABEL_PAD * 2;
|
|
3745
|
-
|
|
3746
|
-
const w = labelWidth(
|
|
3748
|
+
const place = (c, text) => {
|
|
3749
|
+
const w = labelWidth(text);
|
|
3747
3750
|
const direction = c.y > frame.height / 2 ? -1 : 1;
|
|
3748
3751
|
for (let tries = 0; tries <= (c.stack ?? 0); tries++) {
|
|
3749
3752
|
const y = c.y + direction * tries * row;
|
|
@@ -3754,13 +3757,19 @@ function placeLabels(candidates, frame, take) {
|
|
|
3754
3757
|
y1: y + LABEL_PAD
|
|
3755
3758
|
};
|
|
3756
3759
|
if (offFrame(box, frame)) {
|
|
3757
|
-
|
|
3760
|
+
return void 0;
|
|
3758
3761
|
}
|
|
3759
3762
|
if (insideFrame(box, frame) && take(box)) {
|
|
3760
|
-
|
|
3761
|
-
break;
|
|
3763
|
+
return { ...c, text, y, w };
|
|
3762
3764
|
}
|
|
3763
3765
|
}
|
|
3766
|
+
return void 0;
|
|
3767
|
+
};
|
|
3768
|
+
for (const c of candidates) {
|
|
3769
|
+
const placed = place(c, c.text) ?? (c.fallback === void 0 ? void 0 : place(c, c.fallback));
|
|
3770
|
+
if (placed) {
|
|
3771
|
+
out.push(placed);
|
|
3772
|
+
}
|
|
3764
3773
|
}
|
|
3765
3774
|
return out;
|
|
3766
3775
|
}
|
|
@@ -3862,10 +3871,10 @@ function labelOrder(nodePositions, nodeLengths) {
|
|
|
3862
3871
|
return order;
|
|
3863
3872
|
}
|
|
3864
3873
|
var arcCache = /* @__PURE__ */ new WeakMap();
|
|
3865
|
-
function arcPlacements(nodePositions, deletions, axis,
|
|
3874
|
+
function arcPlacements(nodePositions, deletions, axis, version2) {
|
|
3866
3875
|
const { scaleX, scaleY } = axis;
|
|
3867
3876
|
const cached2 = arcCache.get(nodePositions);
|
|
3868
|
-
if (cached2?.deletions === deletions && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.version ===
|
|
3877
|
+
if (cached2?.deletions === deletions && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.version === version2) {
|
|
3869
3878
|
return cached2.arcs;
|
|
3870
3879
|
}
|
|
3871
3880
|
const arcs = [];
|
|
@@ -3883,7 +3892,7 @@ function arcPlacements(nodePositions, deletions, axis, version) {
|
|
|
3883
3892
|
});
|
|
3884
3893
|
}
|
|
3885
3894
|
}
|
|
3886
|
-
arcCache.set(nodePositions, { deletions, scaleX, scaleY, version, arcs });
|
|
3895
|
+
arcCache.set(nodePositions, { deletions, scaleX, scaleY, version: version2, arcs });
|
|
3887
3896
|
return arcs;
|
|
3888
3897
|
}
|
|
3889
3898
|
function sizeLabelCandidates({
|
|
@@ -3896,7 +3905,7 @@ function sizeLabelCandidates({
|
|
|
3896
3905
|
translateY,
|
|
3897
3906
|
width,
|
|
3898
3907
|
height,
|
|
3899
|
-
version = 0
|
|
3908
|
+
version: version2 = 0
|
|
3900
3909
|
}) {
|
|
3901
3910
|
const { scaleX, scaleY } = axis;
|
|
3902
3911
|
const deletionCandidates = [];
|
|
@@ -3905,7 +3914,7 @@ function sizeLabelCandidates({
|
|
|
3905
3914
|
nodePositions,
|
|
3906
3915
|
deletions,
|
|
3907
3916
|
axis,
|
|
3908
|
-
|
|
3917
|
+
version2
|
|
3909
3918
|
)) {
|
|
3910
3919
|
if (extent >= MIN_DELETION_LABEL_PX) {
|
|
3911
3920
|
const text = deletionText(deletion.bp);
|
|
@@ -4283,6 +4292,15 @@ function walkKey(walk, reference) {
|
|
|
4283
4292
|
hover: scale === own ? void 0 : own
|
|
4284
4293
|
};
|
|
4285
4294
|
}
|
|
4295
|
+
function walkPosition(walk, nodeId, length) {
|
|
4296
|
+
const progress = walk.progress.get(nodeId);
|
|
4297
|
+
const range = walk.range;
|
|
4298
|
+
if (progress === void 0 || !range) {
|
|
4299
|
+
return void 0;
|
|
4300
|
+
}
|
|
4301
|
+
const start = Math.round(range.start + progress * walk.bp - length / 2);
|
|
4302
|
+
return `${range.contig}:${start.toLocaleString()}-${(start + length).toLocaleString()}`;
|
|
4303
|
+
}
|
|
4286
4304
|
|
|
4287
4305
|
// src/facetGrid.ts
|
|
4288
4306
|
var FACET_GAP_PX = 8;
|
|
@@ -4355,6 +4373,223 @@ function facetCells(names, by) {
|
|
|
4355
4373
|
return cells.includes(-1) || new Set(cells).size < cells.length ? wrap : { columns, count: rows.length * columns, cells };
|
|
4356
4374
|
}
|
|
4357
4375
|
|
|
4376
|
+
// src/genes/genePins.ts
|
|
4377
|
+
function genePins(graph, genes, positions) {
|
|
4378
|
+
const byRefName = /* @__PURE__ */ new Map();
|
|
4379
|
+
for (const node of graph.nodes) {
|
|
4380
|
+
if (isBackbone(node)) {
|
|
4381
|
+
const { refName } = node.stable;
|
|
4382
|
+
if (positions[node.id]?.length) {
|
|
4383
|
+
const entry = byRefName.get(refName) ?? byRefName.set(refName, { nodes: [], reach: 0 }).get(refName);
|
|
4384
|
+
entry.nodes.push(node);
|
|
4385
|
+
entry.reach = Math.max(entry.reach, node.length);
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
for (const { nodes } of byRefName.values()) {
|
|
4390
|
+
nodes.sort((a, b) => a.stable.start - b.stable.start);
|
|
4391
|
+
}
|
|
4392
|
+
const pins = [];
|
|
4393
|
+
for (const gene of genes) {
|
|
4394
|
+
const backbone = byRefName.get(gene.refName);
|
|
4395
|
+
if (!backbone) {
|
|
4396
|
+
continue;
|
|
4397
|
+
}
|
|
4398
|
+
const parts = [];
|
|
4399
|
+
const exonsByNode = [];
|
|
4400
|
+
let at;
|
|
4401
|
+
let atDistance = Infinity;
|
|
4402
|
+
let covered = 0;
|
|
4403
|
+
const mid = (gene.start + gene.end) / 2;
|
|
4404
|
+
const { nodes, reach } = backbone;
|
|
4405
|
+
for (let i = firstNodeAtOrAfter(nodes, gene.start - reach); i < nodes.length && nodes[i].stable.start < gene.end; i++) {
|
|
4406
|
+
const node = nodes[i];
|
|
4407
|
+
const nodeStart = node.stable.start;
|
|
4408
|
+
const nodeEnd = nodeStart + node.length;
|
|
4409
|
+
if (nodeEnd <= gene.start) {
|
|
4410
|
+
continue;
|
|
4411
|
+
}
|
|
4412
|
+
const line = positions[node.id];
|
|
4413
|
+
covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start);
|
|
4414
|
+
const nodeParts = [];
|
|
4415
|
+
for (const exon of gene.exons) {
|
|
4416
|
+
const a = Math.max(exon.start, nodeStart);
|
|
4417
|
+
const b = Math.min(exon.end, nodeEnd);
|
|
4418
|
+
if (b <= a) {
|
|
4419
|
+
continue;
|
|
4420
|
+
}
|
|
4421
|
+
const stretch = polylineSlice(
|
|
4422
|
+
line,
|
|
4423
|
+
(a - nodeStart) / node.length,
|
|
4424
|
+
(b - nodeStart) / node.length
|
|
4425
|
+
);
|
|
4426
|
+
if (stretch.length === 1) {
|
|
4427
|
+
stretch.push({ ...stretch[0] });
|
|
4428
|
+
}
|
|
4429
|
+
nodeParts.push(svgPath(stretch));
|
|
4430
|
+
}
|
|
4431
|
+
if (nodeParts.length > 0) {
|
|
4432
|
+
parts.push(...nodeParts);
|
|
4433
|
+
exonsByNode.push({ nodeId: node.id, d: nodeParts.join("") });
|
|
4434
|
+
}
|
|
4435
|
+
const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd);
|
|
4436
|
+
const distance = Math.abs(pinBp - mid);
|
|
4437
|
+
if (distance < atDistance) {
|
|
4438
|
+
atDistance = distance;
|
|
4439
|
+
const [p] = polylineSlice(
|
|
4440
|
+
line,
|
|
4441
|
+
(pinBp - nodeStart) / node.length,
|
|
4442
|
+
(pinBp - nodeStart) / node.length
|
|
4443
|
+
);
|
|
4444
|
+
at = p;
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
if (at) {
|
|
4448
|
+
pins.push({
|
|
4449
|
+
gene,
|
|
4450
|
+
exons: parts.join(""),
|
|
4451
|
+
exonsByNode,
|
|
4452
|
+
at,
|
|
4453
|
+
covered: covered / (gene.end - gene.start)
|
|
4454
|
+
});
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
return pins;
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
// src/labelLayout.ts
|
|
4461
|
+
var HALO_FACTOR = 3.4;
|
|
4462
|
+
var LEGEND_INSET_PX = 6;
|
|
4463
|
+
var ROUTE_STACK = 8;
|
|
4464
|
+
var GENE_PIN_DROP_PX = 18;
|
|
4465
|
+
var GENE_STACK = 2;
|
|
4466
|
+
var TOPMOST_BASELINE = LABEL_PX + LABEL_PAD;
|
|
4467
|
+
function geneCoverageNote(pin) {
|
|
4468
|
+
const length = pin.gene.end - pin.gene.start;
|
|
4469
|
+
if (pin.covered >= 0.98) {
|
|
4470
|
+
return void 0;
|
|
4471
|
+
}
|
|
4472
|
+
const [per, unit] = length >= 1e6 ? [1e6, "Mb"] : length >= 1e3 ? [1e3, "kb"] : [1, "bp"];
|
|
4473
|
+
const amount = (bp) => +(bp / per).toFixed(per === 1 ? 0 : 1);
|
|
4474
|
+
return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`;
|
|
4475
|
+
}
|
|
4476
|
+
function geneLabelCandidates(pins, screen, contigThickness) {
|
|
4477
|
+
return byExtent(pins, (pin) => pin.gene.end - pin.gene.start).map((pin) => {
|
|
4478
|
+
const { x, y } = screen(pin.at);
|
|
4479
|
+
const note = geneCoverageNote(pin);
|
|
4480
|
+
return {
|
|
4481
|
+
item: pin,
|
|
4482
|
+
x,
|
|
4483
|
+
y: y + contigThickness + GENE_PIN_DROP_PX,
|
|
4484
|
+
text: note ? `${pin.gene.name} \xB7 ${note}` : pin.gene.name,
|
|
4485
|
+
fallback: note ? `${pin.gene.name} \u2026` : void 0,
|
|
4486
|
+
stack: GENE_STACK
|
|
4487
|
+
};
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4490
|
+
function byExtent(items, extent) {
|
|
4491
|
+
return [...items].sort((a, b) => extent(b) - extent(a));
|
|
4492
|
+
}
|
|
4493
|
+
function layoutLabels(m) {
|
|
4494
|
+
const { paneWidth: width, canvasHeight: height, translateX, translateY } = m;
|
|
4495
|
+
const { scaleX, scaleY } = m.axisScale;
|
|
4496
|
+
const frame = { width, height };
|
|
4497
|
+
const screen = (p) => ({
|
|
4498
|
+
x: p.x * scaleX + translateX,
|
|
4499
|
+
y: p.y * scaleY + translateY
|
|
4500
|
+
});
|
|
4501
|
+
const reserved = m.drawnRowLabels.map(
|
|
4502
|
+
({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
|
|
4503
|
+
);
|
|
4504
|
+
const stripPx = m.referenceStripZonePx ?? 0;
|
|
4505
|
+
if (stripPx > 0) {
|
|
4506
|
+
reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
|
|
4507
|
+
}
|
|
4508
|
+
if (m.legendSize.width > 0) {
|
|
4509
|
+
reserved.push({
|
|
4510
|
+
x0: width - LEGEND_INSET_PX - m.legendSize.width,
|
|
4511
|
+
x1: width,
|
|
4512
|
+
y0: 0,
|
|
4513
|
+
y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
|
|
4514
|
+
});
|
|
4515
|
+
}
|
|
4516
|
+
if (m.poppedFrom) {
|
|
4517
|
+
reserved.push({
|
|
4518
|
+
x0: 0,
|
|
4519
|
+
x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,
|
|
4520
|
+
y0: 0,
|
|
4521
|
+
y1: 44
|
|
4522
|
+
});
|
|
4523
|
+
}
|
|
4524
|
+
const take = occupancy(frame, reserved);
|
|
4525
|
+
const onRows = m.drawnRowLabels.length > 0;
|
|
4526
|
+
const rowsTop = onRows ? Math.min(...m.drawnRowLabels.map((r) => r.y)) : Infinity;
|
|
4527
|
+
const halo = m.contigThickness * HALO_FACTOR;
|
|
4528
|
+
const byBubble = byExtent(m.bubbleHalos, (h) => h.members);
|
|
4529
|
+
const bubbles = placeLabels(
|
|
4530
|
+
byBubble.map((h) => {
|
|
4531
|
+
const { x, y } = screen({
|
|
4532
|
+
x: h.labelAt.x,
|
|
4533
|
+
y: Math.min(h.labelAt.y, rowsTop)
|
|
4534
|
+
});
|
|
4535
|
+
const baseline = y - halo / 2 - 6;
|
|
4536
|
+
return {
|
|
4537
|
+
item: h,
|
|
4538
|
+
x,
|
|
4539
|
+
y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,
|
|
4540
|
+
text: h.label
|
|
4541
|
+
};
|
|
4542
|
+
}),
|
|
4543
|
+
frame,
|
|
4544
|
+
take
|
|
4545
|
+
);
|
|
4546
|
+
const genes = placeLabels(
|
|
4547
|
+
geneLabelCandidates(m.genePins, screen, m.contigThickness),
|
|
4548
|
+
frame,
|
|
4549
|
+
take
|
|
4550
|
+
);
|
|
4551
|
+
const sizeCandidates = m.nodePositions && m.labelsNodeSizes ? sizeLabelCandidates({
|
|
4552
|
+
nodePositions: m.nodePositions,
|
|
4553
|
+
nodeLengths: m.nodeLengths,
|
|
4554
|
+
deletions: m.showDeletionEdges ? m.deletions : [],
|
|
4555
|
+
alleleDeletions: m.alleleDeletions,
|
|
4556
|
+
axis: m.axisScale,
|
|
4557
|
+
translateX,
|
|
4558
|
+
translateY,
|
|
4559
|
+
width,
|
|
4560
|
+
height,
|
|
4561
|
+
version: m.positionsVersion
|
|
4562
|
+
}) : { deletions: [], nodes: [] };
|
|
4563
|
+
const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
|
|
4564
|
+
const routes = placeLabels(
|
|
4565
|
+
byBubble.flatMap(
|
|
4566
|
+
(h) => h.routes.map((route) => {
|
|
4567
|
+
const { x, y } = screen(route.at);
|
|
4568
|
+
return {
|
|
4569
|
+
item: { halo: h, route },
|
|
4570
|
+
x,
|
|
4571
|
+
y: y + 4,
|
|
4572
|
+
text: route.text,
|
|
4573
|
+
stack: ROUTE_STACK
|
|
4574
|
+
};
|
|
4575
|
+
})
|
|
4576
|
+
),
|
|
4577
|
+
frame,
|
|
4578
|
+
take
|
|
4579
|
+
);
|
|
4580
|
+
const nodeLabels = placeSizeLabels(
|
|
4581
|
+
sizeCandidates.nodes,
|
|
4582
|
+
take,
|
|
4583
|
+
nodeLabelBudget(width, height)
|
|
4584
|
+
);
|
|
4585
|
+
return {
|
|
4586
|
+
bubbles,
|
|
4587
|
+
genes,
|
|
4588
|
+
routes,
|
|
4589
|
+
sizes: [...deletionLabels, ...nodeLabels]
|
|
4590
|
+
};
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4358
4593
|
// src/renderer/svgCanvas.ts
|
|
4359
4594
|
var n = (v) => +v.toFixed(1);
|
|
4360
4595
|
function paint(css) {
|
|
@@ -4371,9 +4606,43 @@ function paintAttrs(kind, css) {
|
|
|
4371
4606
|
const { color, opacity } = paint(css);
|
|
4372
4607
|
return `${kind}="${color}"${opacity < 1 ? ` ${kind}-opacity="${n(opacity)}"` : ""}`;
|
|
4373
4608
|
}
|
|
4609
|
+
var DOT_PX = 0.5;
|
|
4610
|
+
var DOT_CELL_PX = 0.25;
|
|
4374
4611
|
function svgCanvas(width, height) {
|
|
4375
4612
|
const out = [];
|
|
4376
|
-
let
|
|
4613
|
+
let subpaths = [];
|
|
4614
|
+
const start = (x, y) => {
|
|
4615
|
+
const s = { d: `M${n(x)} ${n(y)}`, segments: 0, x0: x, x1: x, y0: y, y1: y };
|
|
4616
|
+
subpaths.push(s);
|
|
4617
|
+
return s;
|
|
4618
|
+
};
|
|
4619
|
+
const reach = (points) => {
|
|
4620
|
+
const s = subpaths.at(-1) ?? start(points[0], points[1]);
|
|
4621
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
4622
|
+
s.x0 = Math.min(s.x0, points[i]);
|
|
4623
|
+
s.x1 = Math.max(s.x1, points[i]);
|
|
4624
|
+
s.y0 = Math.min(s.y0, points[i + 1]);
|
|
4625
|
+
s.y1 = Math.max(s.y1, points[i + 1]);
|
|
4626
|
+
}
|
|
4627
|
+
s.segments++;
|
|
4628
|
+
return s;
|
|
4629
|
+
};
|
|
4630
|
+
const pathData = (dots) => {
|
|
4631
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4632
|
+
return subpaths.map((s) => {
|
|
4633
|
+
if (!dots || s.x1 - s.x0 >= DOT_PX || s.y1 - s.y0 >= DOT_PX) {
|
|
4634
|
+
return s.d;
|
|
4635
|
+
}
|
|
4636
|
+
const x = (s.x0 + s.x1) / 2;
|
|
4637
|
+
const y = (s.y0 + s.y1) / 2;
|
|
4638
|
+
const cell = `${Math.round(x / DOT_CELL_PX)} ${Math.round(y / DOT_CELL_PX)}`;
|
|
4639
|
+
if (s.segments === 0 || seen.has(cell)) {
|
|
4640
|
+
return "";
|
|
4641
|
+
}
|
|
4642
|
+
seen.add(cell);
|
|
4643
|
+
return `M${n(x)} ${n(y)}h0`;
|
|
4644
|
+
}).join("");
|
|
4645
|
+
};
|
|
4377
4646
|
const ctx = {
|
|
4378
4647
|
canvas: void 0,
|
|
4379
4648
|
fillStyle: "#000",
|
|
@@ -4382,21 +4651,25 @@ function svgCanvas(width, height) {
|
|
|
4382
4651
|
lineCap: "butt",
|
|
4383
4652
|
lineJoin: "miter",
|
|
4384
4653
|
beginPath() {
|
|
4385
|
-
|
|
4654
|
+
subpaths = [];
|
|
4386
4655
|
},
|
|
4387
4656
|
moveTo(x, y) {
|
|
4388
|
-
|
|
4657
|
+
start(x, y);
|
|
4389
4658
|
},
|
|
4390
4659
|
lineTo(x, y) {
|
|
4391
|
-
d += `L${n(x)} ${n(y)}`;
|
|
4660
|
+
reach([x, y]).d += `L${n(x)} ${n(y)}`;
|
|
4392
4661
|
},
|
|
4393
4662
|
bezierCurveTo(cx0, cy0, cx1, cy1, x, y) {
|
|
4394
|
-
d += `C${n(cx0)} ${n(cy0)} ${n(cx1)} ${n(cy1)} ${n(x)} ${n(y)}`;
|
|
4663
|
+
reach([cx0, cy0, cx1, cy1, x, y]).d += `C${n(cx0)} ${n(cy0)} ${n(cx1)} ${n(cy1)} ${n(x)} ${n(y)}`;
|
|
4395
4664
|
},
|
|
4396
4665
|
closePath() {
|
|
4397
|
-
|
|
4666
|
+
const s = subpaths.at(-1);
|
|
4667
|
+
if (s) {
|
|
4668
|
+
s.d += "Z";
|
|
4669
|
+
}
|
|
4398
4670
|
},
|
|
4399
4671
|
stroke() {
|
|
4672
|
+
const d = pathData(ctx.lineCap !== "butt");
|
|
4400
4673
|
if (d) {
|
|
4401
4674
|
out.push(
|
|
4402
4675
|
`<path d="${d}" fill="none" ${paintAttrs("stroke", ctx.strokeStyle)} stroke-width="${n(ctx.lineWidth)}" stroke-linecap="${ctx.lineCap}" stroke-linejoin="${ctx.lineJoin}"/>`
|
|
@@ -4404,6 +4677,7 @@ function svgCanvas(width, height) {
|
|
|
4404
4677
|
}
|
|
4405
4678
|
},
|
|
4406
4679
|
fill() {
|
|
4680
|
+
const d = pathData(false);
|
|
4407
4681
|
if (d) {
|
|
4408
4682
|
out.push(`<path d="${d}" ${paintAttrs("fill", ctx.fillStyle)}/>`);
|
|
4409
4683
|
}
|
|
@@ -4428,6 +4702,9 @@ function svgCanvas(width, height) {
|
|
|
4428
4702
|
};
|
|
4429
4703
|
}
|
|
4430
4704
|
|
|
4705
|
+
// src/version.ts
|
|
4706
|
+
var version = "4.0.25";
|
|
4707
|
+
|
|
4431
4708
|
// src/viewport.ts
|
|
4432
4709
|
function axisScaleOf(scale, pixelRows) {
|
|
4433
4710
|
return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows };
|
|
@@ -4475,6 +4752,11 @@ var BAR_PX = 48;
|
|
|
4475
4752
|
var SWATCH_PX = 18;
|
|
4476
4753
|
var KEY_GAP_PX = 16;
|
|
4477
4754
|
var FADED = "rgb(160,160,160)";
|
|
4755
|
+
var EXON_COLOR = "#daa520";
|
|
4756
|
+
var GENE_INK = "#1c1c22";
|
|
4757
|
+
var MIN_LANE_PX = 4;
|
|
4758
|
+
var EXON_GAP_PX = 1;
|
|
4759
|
+
var EXON_LINE_PX = 2;
|
|
4478
4760
|
function esc(s) {
|
|
4479
4761
|
return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4480
4762
|
}
|
|
@@ -4503,6 +4785,15 @@ function walkKeySvg(id, x, y, walk, label, reference) {
|
|
|
4503
4785
|
key.scale
|
|
4504
4786
|
);
|
|
4505
4787
|
}
|
|
4788
|
+
function screenPath(d, t) {
|
|
4789
|
+
return d.replaceAll(
|
|
4790
|
+
/([ML])(-?[\d.e-]+),(-?[\d.e-]+)/g,
|
|
4791
|
+
(_, cmd, x, y) => `${cmd}${+(Number(x) * t.scaleX + t.translateX).toFixed(1)},${+(Number(y) * t.scaleY + t.translateY).toFixed(1)}`
|
|
4792
|
+
);
|
|
4793
|
+
}
|
|
4794
|
+
function chip(x, y, w, name, note) {
|
|
4795
|
+
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>`;
|
|
4796
|
+
}
|
|
4506
4797
|
function figureSvg(graph, layout, o = {}) {
|
|
4507
4798
|
const width = o.width ?? 1200;
|
|
4508
4799
|
const room = o.height ?? 800;
|
|
@@ -4529,6 +4820,58 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4529
4820
|
...lift.referenceDomain,
|
|
4530
4821
|
name: region?.refName
|
|
4531
4822
|
};
|
|
4823
|
+
const contigThickness = o.contigThickness ?? 6;
|
|
4824
|
+
const nodeWidth2 = o.nodeWidth ?? "depth";
|
|
4825
|
+
const pins = o.genes?.length && !layout.tubeMap ? genePins(graph, o.genes, layout.nodePositions) : [];
|
|
4826
|
+
const ink = nodeInk(graph, nodeById, contigThickness, nodeWidth2);
|
|
4827
|
+
let masks = 0;
|
|
4828
|
+
function overlays(highlight, t, w, h) {
|
|
4829
|
+
const out = [];
|
|
4830
|
+
const inkPx = (nodeId) => {
|
|
4831
|
+
const own = ink.halfWidthPx(nodeId) * 2;
|
|
4832
|
+
return highlight?.nodeIds.has(nodeId) ? Math.max(own, highlight.walks.length * MIN_LANE_PX) : own;
|
|
4833
|
+
};
|
|
4834
|
+
const stretches = pins.flatMap(
|
|
4835
|
+
(pin) => pin.exonsByNode.map(({ nodeId, d }) => ({
|
|
4836
|
+
d: screenPath(d, t),
|
|
4837
|
+
inner: inkPx(nodeId) + 2 * EXON_GAP_PX
|
|
4838
|
+
}))
|
|
4839
|
+
);
|
|
4840
|
+
if (stretches.length > 0) {
|
|
4841
|
+
const id = `exons${masks++}`;
|
|
4842
|
+
const stroke = (color, width2, d) => `<path d="${d}" fill="none" stroke="${color}" stroke-width="${width2}" stroke-linecap="round" stroke-linejoin="round"/>`;
|
|
4843
|
+
out.push(
|
|
4844
|
+
`<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(
|
|
4845
|
+
""
|
|
4846
|
+
)}</mask><rect width="${w}" height="${h}" fill="${EXON_COLOR}" mask="url(#${id})"/>`
|
|
4847
|
+
);
|
|
4848
|
+
}
|
|
4849
|
+
const screen = (p) => ({
|
|
4850
|
+
x: p.x * t.scaleX + t.translateX,
|
|
4851
|
+
y: p.y * t.scaleY + t.translateY
|
|
4852
|
+
});
|
|
4853
|
+
const frame = { width: w, height: h };
|
|
4854
|
+
for (const { item: pin, x, y, w: cw, text } of placeLabels(
|
|
4855
|
+
geneLabelCandidates(pins, screen, contigThickness),
|
|
4856
|
+
frame,
|
|
4857
|
+
occupancy(frame)
|
|
4858
|
+
)) {
|
|
4859
|
+
const pinY = screen(pin.at).y + contigThickness / 2;
|
|
4860
|
+
out.push(
|
|
4861
|
+
`<line x1="${x}" x2="${x}" y1="${y - LABEL_PX - 2}" y2="${pinY}" stroke="${GENE_INK}" stroke-width="0.8" stroke-opacity="0.6"/>`,
|
|
4862
|
+
chip(x, y, cw, pin.gene.name, text.slice(pin.gene.name.length))
|
|
4863
|
+
);
|
|
4864
|
+
}
|
|
4865
|
+
for (const { label, y } of layout.rowLabels ?? []) {
|
|
4866
|
+
const sy = y * t.scaleY + t.translateY;
|
|
4867
|
+
if (sy >= 0 && sy <= h) {
|
|
4868
|
+
out.push(
|
|
4869
|
+
`<text x="6" y="${sy + 4}" ${FONT} stroke="#fff" stroke-width="3" paint-order="stroke">${esc(label)}</text>`
|
|
4870
|
+
);
|
|
4871
|
+
}
|
|
4872
|
+
}
|
|
4873
|
+
return out.join("");
|
|
4874
|
+
}
|
|
4532
4875
|
function drawing(highlight, w, h) {
|
|
4533
4876
|
const fit = fitTransform(
|
|
4534
4877
|
bounds,
|
|
@@ -4554,10 +4897,10 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4554
4897
|
graph,
|
|
4555
4898
|
nodeById,
|
|
4556
4899
|
colorScheme,
|
|
4557
|
-
contigThickness
|
|
4900
|
+
contigThickness,
|
|
4558
4901
|
connectorThickness: o.connectorThickness ?? 2,
|
|
4559
4902
|
drawPaths: false,
|
|
4560
|
-
nodeWidth:
|
|
4903
|
+
nodeWidth: nodeWidth2,
|
|
4561
4904
|
highlight,
|
|
4562
4905
|
axis,
|
|
4563
4906
|
referenceRamp,
|
|
@@ -4567,15 +4910,15 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4567
4910
|
)
|
|
4568
4911
|
})
|
|
4569
4912
|
);
|
|
4570
|
-
|
|
4913
|
+
const t = {
|
|
4571
4914
|
scaleX: axis.scaleX,
|
|
4572
4915
|
scaleY: axis.scaleY,
|
|
4573
4916
|
translateX: fit.translateX,
|
|
4574
|
-
translateY: fit.translateY
|
|
4575
|
-
|
|
4576
|
-
});
|
|
4917
|
+
translateY: fit.translateY
|
|
4918
|
+
};
|
|
4919
|
+
renderer.updateTransform({ ...t, dpr: 1 });
|
|
4577
4920
|
renderer.render([1, 1, 1, 1]);
|
|
4578
|
-
return markup();
|
|
4921
|
+
return markup() + overlays(highlight, t, w, h);
|
|
4579
4922
|
}
|
|
4580
4923
|
const parts = [];
|
|
4581
4924
|
let height;
|
|
@@ -4654,10 +4997,169 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4654
4997
|
);
|
|
4655
4998
|
height = header + h;
|
|
4656
4999
|
}
|
|
4657
|
-
|
|
5000
|
+
const metadata = JSON.stringify({
|
|
5001
|
+
generator: `@jbrowse/bandage-core@${version}`,
|
|
5002
|
+
...o.spec === void 0 ? {} : { spec: o.spec }
|
|
5003
|
+
});
|
|
5004
|
+
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>
|
|
4658
5005
|
`;
|
|
4659
5006
|
}
|
|
4660
5007
|
|
|
5008
|
+
// src/genes/geneFiles.ts
|
|
5009
|
+
var GENE_TYPE = /(gene|gene_segment|RNA|transcript)$/i;
|
|
5010
|
+
var isGene = (type) => GENE_TYPE.test(type) || type === "CDS" || type === "exon";
|
|
5011
|
+
function mergedIntervals(intervals) {
|
|
5012
|
+
const out = [];
|
|
5013
|
+
for (const iv of [...intervals].sort((a, b) => a.start - b.start)) {
|
|
5014
|
+
const last = out.at(-1);
|
|
5015
|
+
if (last && iv.start <= last.end) {
|
|
5016
|
+
last.end = Math.max(last.end, iv.end);
|
|
5017
|
+
} else {
|
|
5018
|
+
out.push({ ...iv });
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
return out;
|
|
5022
|
+
}
|
|
5023
|
+
function strandOf(s) {
|
|
5024
|
+
return s === "+" ? 1 : s === "-" ? -1 : 0;
|
|
5025
|
+
}
|
|
5026
|
+
function unescape(value) {
|
|
5027
|
+
try {
|
|
5028
|
+
return decodeURIComponent(value);
|
|
5029
|
+
} catch {
|
|
5030
|
+
return value;
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
function attributes(column) {
|
|
5034
|
+
const out = /* @__PURE__ */ new Map();
|
|
5035
|
+
for (const pair of column.split(";")) {
|
|
5036
|
+
const eq = pair.indexOf("=");
|
|
5037
|
+
if (eq > 0) {
|
|
5038
|
+
out.set(pair.slice(0, eq).trim(), unescape(pair.slice(eq + 1).trim()));
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
5041
|
+
return out;
|
|
5042
|
+
}
|
|
5043
|
+
function gff3Row(line) {
|
|
5044
|
+
const cols = line.split(" ");
|
|
5045
|
+
const start = Number(cols[3]);
|
|
5046
|
+
const end = Number(cols[4]);
|
|
5047
|
+
if (cols.length < 9 || !Number.isInteger(start) || !Number.isInteger(end)) {
|
|
5048
|
+
return void 0;
|
|
5049
|
+
}
|
|
5050
|
+
return {
|
|
5051
|
+
refName: cols[0],
|
|
5052
|
+
type: cols[2],
|
|
5053
|
+
start: start - 1,
|
|
5054
|
+
end,
|
|
5055
|
+
strand: strandOf(cols[6]),
|
|
5056
|
+
attrs: attributes(cols[8])
|
|
5057
|
+
};
|
|
5058
|
+
}
|
|
5059
|
+
function genesFromGff3Lines(lines) {
|
|
5060
|
+
const rows = [];
|
|
5061
|
+
for (const line of lines) {
|
|
5062
|
+
if (line.startsWith("##FASTA")) {
|
|
5063
|
+
break;
|
|
5064
|
+
}
|
|
5065
|
+
const row = line.startsWith("#") ? void 0 : gff3Row(line);
|
|
5066
|
+
if (row) {
|
|
5067
|
+
rows.push(row);
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
5071
|
+
for (const { attrs } of rows) {
|
|
5072
|
+
const id = attrs.get("ID");
|
|
5073
|
+
const parent = attrs.get("Parent")?.split(",")[0];
|
|
5074
|
+
if (id && parent) {
|
|
5075
|
+
parentOf.set(id, parent);
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
const rootOf = (row) => {
|
|
5079
|
+
let id = row.attrs.get("Parent")?.split(",")[0] ?? row.attrs.get("ID");
|
|
5080
|
+
if (!id) {
|
|
5081
|
+
return row.attrs.get("gene_id");
|
|
5082
|
+
}
|
|
5083
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5084
|
+
while (id && parentOf.has(id) && !seen.has(id)) {
|
|
5085
|
+
seen.add(id);
|
|
5086
|
+
id = parentOf.get(id);
|
|
5087
|
+
}
|
|
5088
|
+
return id;
|
|
5089
|
+
};
|
|
5090
|
+
const groups = /* @__PURE__ */ new Map();
|
|
5091
|
+
rows.forEach((row, i) => {
|
|
5092
|
+
const root = rootOf(row) ?? `#${i}`;
|
|
5093
|
+
const key = `${row.refName} ${root}`;
|
|
5094
|
+
const group = groups.get(key) ?? groups.set(key, { rows: [] }).get(key);
|
|
5095
|
+
group.rows.push(row);
|
|
5096
|
+
if (!row.attrs.has("Parent") && !group.top) {
|
|
5097
|
+
group.top = row;
|
|
5098
|
+
}
|
|
5099
|
+
});
|
|
5100
|
+
const genes = [];
|
|
5101
|
+
for (const [key, { top, rows: rows2 }] of groups) {
|
|
5102
|
+
if (top && !isGene(top.type)) {
|
|
5103
|
+
continue;
|
|
5104
|
+
}
|
|
5105
|
+
const first = top ?? rows2[0];
|
|
5106
|
+
const start = top?.start ?? Math.min(...rows2.map((r) => r.start));
|
|
5107
|
+
const end = top?.end ?? Math.max(...rows2.map((r) => r.end));
|
|
5108
|
+
const a = first.attrs;
|
|
5109
|
+
const exons = rows2.filter((r) => r.type === "exon").map((r) => ({ start: r.start, end: r.end }));
|
|
5110
|
+
genes.push({
|
|
5111
|
+
name: a.get("Name") ?? a.get("gene_name") ?? a.get("gene") ?? a.get("gene_id") ?? key.split(" ")[1],
|
|
5112
|
+
refName: first.refName,
|
|
5113
|
+
start,
|
|
5114
|
+
end,
|
|
5115
|
+
strand: first.strand,
|
|
5116
|
+
exons: exons.length ? mergedIntervals(exons) : [{ start, end }]
|
|
5117
|
+
});
|
|
5118
|
+
}
|
|
5119
|
+
return genes;
|
|
5120
|
+
}
|
|
5121
|
+
function genesFromBed(text) {
|
|
5122
|
+
const genes = /* @__PURE__ */ new Map();
|
|
5123
|
+
for (const line of text.split("\n")) {
|
|
5124
|
+
const cols = line.replace(/\r$/, "").split(" ");
|
|
5125
|
+
const start = Number(cols[1]);
|
|
5126
|
+
const end = Number(cols[2]);
|
|
5127
|
+
if (/^(#|track|browser)/.test(line) || cols.length < 3 || !Number.isInteger(start) || !(end > start)) {
|
|
5128
|
+
continue;
|
|
5129
|
+
}
|
|
5130
|
+
const refName = cols[0];
|
|
5131
|
+
const name = cols[3] || `${refName}:${start + 1}-${end}`;
|
|
5132
|
+
const sizes = cols[10]?.split(",").filter((s) => s !== "").map(Number);
|
|
5133
|
+
const starts = cols[11]?.split(",").filter((s) => s !== "").map(Number);
|
|
5134
|
+
const blocks = sizes?.length && sizes.length === starts?.length ? sizes.map((size, i) => ({
|
|
5135
|
+
start: start + starts[i],
|
|
5136
|
+
end: start + starts[i] + size
|
|
5137
|
+
})) : [{ start, end }];
|
|
5138
|
+
const key = `${refName} ${name}`;
|
|
5139
|
+
const gene = genes.get(key);
|
|
5140
|
+
if (gene) {
|
|
5141
|
+
gene.start = Math.min(gene.start, start);
|
|
5142
|
+
gene.end = Math.max(gene.end, end);
|
|
5143
|
+
gene.exons = mergedIntervals([...gene.exons, ...blocks]);
|
|
5144
|
+
} else {
|
|
5145
|
+
genes.set(key, {
|
|
5146
|
+
name,
|
|
5147
|
+
refName,
|
|
5148
|
+
start,
|
|
5149
|
+
end,
|
|
5150
|
+
strand: strandOf(cols[5]),
|
|
5151
|
+
exons: mergedIntervals(blocks)
|
|
5152
|
+
});
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
return [...genes.values()];
|
|
5156
|
+
}
|
|
5157
|
+
function genesFromText(text) {
|
|
5158
|
+
const first = text.split("\n", 1e3).find((l) => l.trim() !== "" && !/^(#|track|browser)/.test(l));
|
|
5159
|
+
const cols = first?.split(" ") ?? [];
|
|
5160
|
+
return /^\d+$/.test(cols[1] ?? "") && /^\d+$/.test(cols[2] ?? "") ? genesFromBed(text) : genesFromGff3Lines(text.split(/\r?\n/));
|
|
5161
|
+
}
|
|
5162
|
+
|
|
4661
5163
|
// src/reference.ts
|
|
4662
5164
|
var WELL_KNOWN_SAMPLES = /* @__PURE__ */ new Map([
|
|
4663
5165
|
["hg38", "GRCh38"],
|
|
@@ -4832,9 +5334,9 @@ function resolveReferenceSample({
|
|
|
4832
5334
|
}
|
|
4833
5335
|
return sample;
|
|
4834
5336
|
}
|
|
4835
|
-
async function referencePathQuery(db,
|
|
5337
|
+
async function referencePathQuery(db, referenceSample2, refName) {
|
|
4836
5338
|
const path = (await db.paths()).find(
|
|
4837
|
-
(p) => p.isIndexed && p.name.sample ===
|
|
5339
|
+
(p) => p.isIndexed && p.name.sample === referenceSample2 && p.name.contig === refName
|
|
4838
5340
|
);
|
|
4839
5341
|
return path ? {
|
|
4840
5342
|
sample: path.name.sample,
|
|
@@ -4863,6 +5365,60 @@ async function cutWindowGFA(db, query, start, end, { keep, ...opts }) {
|
|
|
4863
5365
|
return subgraph ? subgraph.toGFA({ names: "resolved" }) : "";
|
|
4864
5366
|
}
|
|
4865
5367
|
|
|
5368
|
+
// src/gbzCut.ts
|
|
5369
|
+
import { GBZBase } from "@gmod/gbz-base";
|
|
5370
|
+
var HPRC_GBZ = {
|
|
5371
|
+
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",
|
|
5372
|
+
index: "https://jbrowse.org/demos/hprc/hprc-v2.1-mc-grch38.haplotype-index.anchored.db"
|
|
5373
|
+
};
|
|
5374
|
+
var WALK_LIMIT = 1e5;
|
|
5375
|
+
function parseRegion(text) {
|
|
5376
|
+
const m = /^\s*([^:\s]+):([\d,]+)-([\d,]+)\s*$/.exec(text);
|
|
5377
|
+
if (!m) {
|
|
5378
|
+
throw new Error(
|
|
5379
|
+
`"${text}" is not a region like chr6:160,614,798-160,647,758`
|
|
5380
|
+
);
|
|
5381
|
+
}
|
|
5382
|
+
const n2 = (s) => Number(s.replaceAll(",", ""));
|
|
5383
|
+
return { refName: m[1], start: n2(m[2]), end: n2(m[3]) };
|
|
5384
|
+
}
|
|
5385
|
+
async function openGbz(db, index) {
|
|
5386
|
+
const base = await GBZBase.open(db, index ? { haplotypeIndex: index } : {});
|
|
5387
|
+
return { base, referenceSamples: await referenceSamplesOf(base) };
|
|
5388
|
+
}
|
|
5389
|
+
function referenceSample(ref, samples) {
|
|
5390
|
+
try {
|
|
5391
|
+
return resolveReferenceSample({
|
|
5392
|
+
configured: "",
|
|
5393
|
+
anchorPrefix: ref ?? samples[0] ?? "",
|
|
5394
|
+
referenceSamples: samples
|
|
5395
|
+
});
|
|
5396
|
+
} catch (e) {
|
|
5397
|
+
if (ref) {
|
|
5398
|
+
return ref;
|
|
5399
|
+
}
|
|
5400
|
+
throw e;
|
|
5401
|
+
}
|
|
5402
|
+
}
|
|
5403
|
+
async function cutGbzRegion({ base, referenceSamples }, src, signal) {
|
|
5404
|
+
const region = parseRegion(src.region);
|
|
5405
|
+
const sample = referenceSample(src.referenceSample, referenceSamples);
|
|
5406
|
+
const query = await referencePathQuery(base, sample, region.refName);
|
|
5407
|
+
if (!query) {
|
|
5408
|
+
throw new Error(`${sample} has no indexed path named ${region.refName}`);
|
|
5409
|
+
}
|
|
5410
|
+
signal?.throwIfAborted();
|
|
5411
|
+
const wanted = src.haplotypes?.length ? src.haplotypes : void 0;
|
|
5412
|
+
const text = await cutWindowGFA(base, query, region.start, region.end, {
|
|
5413
|
+
context: src.context ?? 1e3,
|
|
5414
|
+
snarls: src.snarls ?? "contained",
|
|
5415
|
+
limit: WALK_LIMIT,
|
|
5416
|
+
signal,
|
|
5417
|
+
...wanted ? { keep: (name) => haplotypeWanted(name, wanted) } : {}
|
|
5418
|
+
});
|
|
5419
|
+
return { text, region, sample };
|
|
5420
|
+
}
|
|
5421
|
+
|
|
4866
5422
|
// src/loadBandage.ts
|
|
4867
5423
|
var cached;
|
|
4868
5424
|
function loadBandage() {
|
|
@@ -4875,12 +5431,10 @@ function loadBandage() {
|
|
|
4875
5431
|
|
|
4876
5432
|
export {
|
|
4877
5433
|
isBackbone,
|
|
4878
|
-
firstNodeAtOrAfter,
|
|
4879
5434
|
BUBBLE_SPREADS,
|
|
4880
5435
|
panSNSample,
|
|
4881
5436
|
panSNHaplotype,
|
|
4882
5437
|
panSNContig,
|
|
4883
|
-
polylineSlice,
|
|
4884
5438
|
loadGraph,
|
|
4885
5439
|
engineRequest,
|
|
4886
5440
|
engineKey,
|
|
@@ -4926,13 +5480,7 @@ export {
|
|
|
4926
5480
|
LABEL_PX,
|
|
4927
5481
|
LABEL_CHAR_PX,
|
|
4928
5482
|
LABEL_PAD,
|
|
4929
|
-
occupancy,
|
|
4930
|
-
placeLabels,
|
|
4931
|
-
rowLabelBox,
|
|
4932
5483
|
formatBp,
|
|
4933
|
-
sizeLabelCandidates,
|
|
4934
|
-
nodeLabelBudget,
|
|
4935
|
-
placeSizeLabels,
|
|
4936
5484
|
WALK_FIELDS,
|
|
4937
5485
|
WALK_SCHEMES,
|
|
4938
5486
|
resolveEncoding,
|
|
@@ -4944,12 +5492,19 @@ export {
|
|
|
4944
5492
|
facetLifts,
|
|
4945
5493
|
rangeText,
|
|
4946
5494
|
walkKey,
|
|
5495
|
+
walkPosition,
|
|
4947
5496
|
FACET_GAP_PX,
|
|
4948
5497
|
FACET_TITLE_PX,
|
|
4949
5498
|
FACET_PAD_PX,
|
|
4950
5499
|
facetGrid,
|
|
4951
5500
|
facetCells,
|
|
5501
|
+
genePins,
|
|
5502
|
+
HALO_FACTOR,
|
|
5503
|
+
LEGEND_INSET_PX,
|
|
5504
|
+
geneCoverageNote,
|
|
5505
|
+
layoutLabels,
|
|
4952
5506
|
svgCanvas,
|
|
5507
|
+
version,
|
|
4953
5508
|
axisScaleOf,
|
|
4954
5509
|
viewportOf,
|
|
4955
5510
|
padded,
|
|
@@ -4957,6 +5512,9 @@ export {
|
|
|
4957
5512
|
zoomAbout,
|
|
4958
5513
|
screenToLayout,
|
|
4959
5514
|
figureSvg,
|
|
5515
|
+
genesFromGff3Lines,
|
|
5516
|
+
genesFromBed,
|
|
5517
|
+
genesFromText,
|
|
4960
5518
|
WELL_KNOWN_SAMPLES,
|
|
4961
5519
|
wellKnownSample,
|
|
4962
5520
|
graphBackbone,
|
|
@@ -4969,5 +5527,9 @@ export {
|
|
|
4969
5527
|
resolveReferenceSample,
|
|
4970
5528
|
referencePathQuery,
|
|
4971
5529
|
cutWindowGFA,
|
|
5530
|
+
HPRC_GBZ,
|
|
5531
|
+
parseRegion,
|
|
5532
|
+
openGbz,
|
|
5533
|
+
cutGbzRegion,
|
|
4972
5534
|
loadBandage
|
|
4973
5535
|
};
|