@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
package/dist/index.js
CHANGED
|
@@ -8,10 +8,13 @@ import {
|
|
|
8
8
|
FADED_ALPHA,
|
|
9
9
|
FIT_PADDING,
|
|
10
10
|
FORCE_LAYOUT_LABEL,
|
|
11
|
+
HALO_FACTOR,
|
|
12
|
+
HPRC_GBZ,
|
|
11
13
|
LABEL_CHAR_PX,
|
|
12
14
|
LABEL_PAD,
|
|
13
15
|
LABEL_PX,
|
|
14
16
|
LAYOUT_MODES,
|
|
17
|
+
LEGEND_INSET_PX,
|
|
15
18
|
LIFT_BACKDROP_COLOR,
|
|
16
19
|
NODE_WIDTHS,
|
|
17
20
|
REFERENCE_RAMP_MAX_HUE,
|
|
@@ -29,6 +32,7 @@ import {
|
|
|
29
32
|
computeReferenceRamp,
|
|
30
33
|
contains,
|
|
31
34
|
curveBounds,
|
|
35
|
+
cutGbzRegion,
|
|
32
36
|
cutWindowGFA,
|
|
33
37
|
deletionEdges,
|
|
34
38
|
drawingBounds,
|
|
@@ -42,15 +46,20 @@ import {
|
|
|
42
46
|
fadeAbgr,
|
|
43
47
|
featuresOnBackbone,
|
|
44
48
|
figureSvg,
|
|
45
|
-
firstNodeAtOrAfter,
|
|
46
49
|
fitTransform,
|
|
47
50
|
forceLayout,
|
|
48
51
|
formatBp,
|
|
52
|
+
geneCoverageNote,
|
|
53
|
+
genePins,
|
|
54
|
+
genesFromBed,
|
|
55
|
+
genesFromGff3Lines,
|
|
56
|
+
genesFromText,
|
|
49
57
|
getNodeColor,
|
|
50
58
|
graphBackbone,
|
|
51
59
|
haplotypeWanted,
|
|
52
60
|
isBackbone,
|
|
53
61
|
layoutExtent,
|
|
62
|
+
layoutLabels,
|
|
54
63
|
layoutModeByValue,
|
|
55
64
|
loadBandage,
|
|
56
65
|
loadGraph,
|
|
@@ -58,19 +67,16 @@ import {
|
|
|
58
67
|
meanDepth,
|
|
59
68
|
modeUsesLayoutEngine,
|
|
60
69
|
nodeInk,
|
|
61
|
-
nodeLabelBudget,
|
|
62
70
|
nodeWidthPx,
|
|
63
|
-
|
|
71
|
+
openGbz,
|
|
64
72
|
padded,
|
|
65
73
|
panSNContig,
|
|
66
74
|
panSNHaplotype,
|
|
67
75
|
panSNSample,
|
|
76
|
+
parseRegion,
|
|
68
77
|
pathColorsLegible,
|
|
69
78
|
pathLegend,
|
|
70
79
|
pathRibbonOffsets,
|
|
71
|
-
placeLabels,
|
|
72
|
-
placeSizeLabels,
|
|
73
|
-
polylineSlice,
|
|
74
80
|
rangeText,
|
|
75
81
|
refNameBinding,
|
|
76
82
|
referenceOrder,
|
|
@@ -79,23 +85,23 @@ import {
|
|
|
79
85
|
resolveColorScheme,
|
|
80
86
|
resolveEncoding,
|
|
81
87
|
resolveReferenceSample,
|
|
82
|
-
rowLabelBox,
|
|
83
88
|
schemeCss,
|
|
84
89
|
screenToLayout,
|
|
85
|
-
sizeLabelCandidates,
|
|
86
90
|
svgCanvas,
|
|
87
91
|
svgPath,
|
|
88
92
|
translateCurves,
|
|
93
|
+
version,
|
|
89
94
|
viewportOf,
|
|
90
95
|
walkHighlight,
|
|
91
96
|
walkKey,
|
|
92
97
|
walkLift,
|
|
98
|
+
walkPosition,
|
|
93
99
|
walkRows,
|
|
94
100
|
walkRowsExtent,
|
|
95
101
|
wellKnownSample,
|
|
96
102
|
yToXOf,
|
|
97
103
|
zoomAbout
|
|
98
|
-
} from "./chunks/chunk-
|
|
104
|
+
} from "./chunks/chunk-B5ZHLU4N.js";
|
|
99
105
|
|
|
100
106
|
// src/index.ts
|
|
101
107
|
import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
|
|
@@ -212,14 +218,14 @@ var SpatialIndex = class {
|
|
|
212
218
|
}
|
|
213
219
|
};
|
|
214
220
|
var EdgeSpatialIndex = class {
|
|
215
|
-
constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions,
|
|
221
|
+
constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions, version2 = 0) {
|
|
216
222
|
this.cells = /* @__PURE__ */ new Map();
|
|
217
223
|
this.edgeCurves = baseEdgeCurves(
|
|
218
224
|
nodePositions,
|
|
219
225
|
graph,
|
|
220
226
|
axis,
|
|
221
227
|
deletions,
|
|
222
|
-
|
|
228
|
+
version2
|
|
223
229
|
);
|
|
224
230
|
const boxes = [];
|
|
225
231
|
let extentX = 0;
|
|
@@ -332,19 +338,19 @@ function distanceToEdgeCurves(px, py, curves, yToX = 1) {
|
|
|
332
338
|
return minDist;
|
|
333
339
|
}
|
|
334
340
|
var nodeCache = /* @__PURE__ */ new WeakMap();
|
|
335
|
-
function getSpatialIndex(nodePositions,
|
|
341
|
+
function getSpatialIndex(nodePositions, version2) {
|
|
336
342
|
const cached = nodeCache.get(nodePositions);
|
|
337
|
-
if (cached?.version ===
|
|
343
|
+
if (cached?.version === version2) {
|
|
338
344
|
return cached.index;
|
|
339
345
|
}
|
|
340
346
|
const index = new SpatialIndex(nodePositions);
|
|
341
|
-
nodeCache.set(nodePositions, { version, index });
|
|
347
|
+
nodeCache.set(nodePositions, { version: version2, index });
|
|
342
348
|
return index;
|
|
343
349
|
}
|
|
344
350
|
var edgeCache = /* @__PURE__ */ new WeakMap();
|
|
345
|
-
function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis,
|
|
351
|
+
function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version2, deletions) {
|
|
346
352
|
const cached = edgeCache.get(nodePositions);
|
|
347
|
-
if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version ===
|
|
353
|
+
if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version === version2 && cached.deletions === deletions) {
|
|
348
354
|
return cached.index;
|
|
349
355
|
}
|
|
350
356
|
const index = new EdgeSpatialIndex(
|
|
@@ -354,23 +360,23 @@ function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version, del
|
|
|
354
360
|
axis,
|
|
355
361
|
void 0,
|
|
356
362
|
deletions,
|
|
357
|
-
|
|
363
|
+
version2
|
|
358
364
|
);
|
|
359
365
|
edgeCache.set(nodePositions, {
|
|
360
366
|
graph,
|
|
361
367
|
drawPaths,
|
|
362
368
|
scaleX: axis.scaleX,
|
|
363
369
|
scaleY: axis.scaleY,
|
|
364
|
-
version,
|
|
370
|
+
version: version2,
|
|
365
371
|
index,
|
|
366
372
|
deletions
|
|
367
373
|
});
|
|
368
374
|
return index;
|
|
369
375
|
}
|
|
370
376
|
var MIN_NODE_REACH_PX = 5;
|
|
371
|
-
function findHoveredNode(nodePositions, graphX, graphY, axis,
|
|
377
|
+
function findHoveredNode(nodePositions, graphX, graphY, axis, version2 = 0, ink) {
|
|
372
378
|
const yToX = yToXOf(axis);
|
|
373
|
-
const index = getSpatialIndex(nodePositions,
|
|
379
|
+
const index = getSpatialIndex(nodePositions, version2);
|
|
374
380
|
const reach = (px) => Math.max(MIN_NODE_REACH_PX, px) / axis.scaleX;
|
|
375
381
|
const queryReach = reach(ink?.maxHalfWidthPx ?? 0);
|
|
376
382
|
const candidates = index.query(graphX, graphY, queryReach, queryReach / yToX);
|
|
@@ -393,7 +399,7 @@ function findHoveredNode(nodePositions, graphX, graphY, axis, version = 0, ink)
|
|
|
393
399
|
}
|
|
394
400
|
return hovered;
|
|
395
401
|
}
|
|
396
|
-
function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths,
|
|
402
|
+
function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths, version2 = 0, deletions, hiddenEdges) {
|
|
397
403
|
const yToX = yToXOf(axis);
|
|
398
404
|
const edgeThreshold = 10 / axis.scaleX;
|
|
399
405
|
const edgeIndex = getEdgeSpatialIndex(
|
|
@@ -401,7 +407,7 @@ function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths,
|
|
|
401
407
|
graph,
|
|
402
408
|
drawPaths,
|
|
403
409
|
axis,
|
|
404
|
-
|
|
410
|
+
version2,
|
|
405
411
|
deletions
|
|
406
412
|
);
|
|
407
413
|
const candidates = edgeIndex.query(
|
|
@@ -466,27 +472,31 @@ function wheelZoomFactor(e) {
|
|
|
466
472
|
|
|
467
473
|
// src/referenceStrip.ts
|
|
468
474
|
import { abgrToCssRgba } from "@jbrowse/core/util/colorBits";
|
|
469
|
-
var REFERENCE_STRIP_PX =
|
|
475
|
+
var REFERENCE_STRIP_PX = 12;
|
|
470
476
|
var REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8;
|
|
471
|
-
var OVERHANG_PX =
|
|
477
|
+
var OVERHANG_PX = 8;
|
|
478
|
+
var OVERHANG_CAP_PX = OVERHANG_PX + 3;
|
|
479
|
+
var MIN_STAGGER_PX = 12;
|
|
480
|
+
var PALE = abgrToCssRgba(fadeAbgr(LIFT_BACKDROP_COLOR, FADED_ALPHA));
|
|
472
481
|
function referenceStripBlocks(graph, {
|
|
473
482
|
colorScheme,
|
|
474
483
|
referenceRamp,
|
|
475
|
-
|
|
476
|
-
walkColors
|
|
484
|
+
walks
|
|
477
485
|
}) {
|
|
478
486
|
const range = { ...computeColorSchemeRange(graph), referenceRamp };
|
|
479
487
|
const out = [];
|
|
480
488
|
graph.nodes.forEach((node, index) => {
|
|
481
489
|
if (isBackbone(node)) {
|
|
482
|
-
const
|
|
483
|
-
|
|
490
|
+
const colors = walks ? walks.map((w) => {
|
|
491
|
+
const lane = w.colors.get(node.id);
|
|
492
|
+
return lane === void 0 ? PALE : abgrToCssRgba(lane);
|
|
493
|
+
}) : [abgrToCssRgba(getNodeColor(node, index, colorScheme, range))];
|
|
484
494
|
out.push({
|
|
485
495
|
node: node.id,
|
|
486
496
|
bp0: node.stable.start,
|
|
487
497
|
bp1: node.stable.start + node.length,
|
|
488
|
-
|
|
489
|
-
faded
|
|
498
|
+
colors,
|
|
499
|
+
faded: colors.includes(PALE)
|
|
490
500
|
});
|
|
491
501
|
}
|
|
492
502
|
});
|
|
@@ -532,7 +542,7 @@ function stripPixels(blocks, frame, width, dpr) {
|
|
|
532
542
|
const [x0, x1] = span(frame, b.bp0, b.bp1);
|
|
533
543
|
const px0 = Math.round(x0 * dpr);
|
|
534
544
|
return {
|
|
535
|
-
|
|
545
|
+
colors: b.colors,
|
|
536
546
|
x0,
|
|
537
547
|
px0,
|
|
538
548
|
px1: Math.max(Math.round(x1 * dpr), px0 + 1)
|
|
@@ -540,22 +550,34 @@ function stripPixels(blocks, frame, width, dpr) {
|
|
|
540
550
|
}).filter((b) => b.px1 > 0 && b.px0 < width * dpr).sort((a, b) => a.x0 - b.x0);
|
|
541
551
|
const out = [];
|
|
542
552
|
let claimed = -Infinity;
|
|
543
|
-
|
|
553
|
+
const tierEnds = [-Infinity, -Infinity];
|
|
554
|
+
for (const { colors, px0, px1 } of placed) {
|
|
544
555
|
const start = Math.max(px0, claimed);
|
|
545
556
|
if (px1 > start) {
|
|
546
|
-
|
|
557
|
+
const tier = tierEnds[0] < start ? 0 : 1;
|
|
558
|
+
tierEnds[tier] = px1;
|
|
559
|
+
out.push({ colors, x0: start / dpr, x1: px1 / dpr, tier });
|
|
547
560
|
claimed = px1;
|
|
548
561
|
}
|
|
549
562
|
}
|
|
550
|
-
|
|
563
|
+
const covered = out.length > 0 ? out.at(-1).x1 - out[0].x0 : 0;
|
|
564
|
+
return covered >= out.length * MIN_STAGGER_PX ? out : out.map(({ tier: _, ...b }) => b);
|
|
551
565
|
}
|
|
552
|
-
function drawOverhang(ctx,
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
ctx.
|
|
566
|
+
function drawOverhang(ctx, edgeX, inward, ink) {
|
|
567
|
+
const capX = inward > 0 ? edgeX : edgeX - OVERHANG_CAP_PX;
|
|
568
|
+
ctx.clearRect(capX, 0, OVERHANG_CAP_PX, REFERENCE_STRIP_PX);
|
|
569
|
+
const tipX = edgeX + inward;
|
|
570
|
+
const baseX = tipX + inward * OVERHANG_PX;
|
|
571
|
+
ctx.beginPath();
|
|
572
|
+
ctx.moveTo(tipX, REFERENCE_STRIP_PX / 2);
|
|
573
|
+
ctx.lineTo(baseX, 0);
|
|
574
|
+
ctx.lineTo(baseX, REFERENCE_STRIP_PX);
|
|
558
575
|
ctx.closePath();
|
|
576
|
+
ctx.fillStyle = ink;
|
|
577
|
+
ctx.fill();
|
|
578
|
+
}
|
|
579
|
+
function rowEdge(row, rows, dpr) {
|
|
580
|
+
return Math.round(row * REFERENCE_STRIP_PX * dpr / rows) / dpr;
|
|
559
581
|
}
|
|
560
582
|
function drawReferenceStrip(ctx, blocks, frame, {
|
|
561
583
|
width,
|
|
@@ -564,26 +586,24 @@ function drawReferenceStrip(ctx, blocks, frame, {
|
|
|
564
586
|
dpr = 1
|
|
565
587
|
}) {
|
|
566
588
|
const ink = darkMode ? "#ffffff" : "#18181c";
|
|
567
|
-
ctx.
|
|
568
|
-
ctx.fillRect(0, 0, width, REFERENCE_STRIP_ZONE_PX);
|
|
589
|
+
ctx.clearRect(0, 0, width, REFERENCE_STRIP_ZONE_PX);
|
|
569
590
|
for (const b of stripPixels(blocks, frame, width, dpr)) {
|
|
570
|
-
|
|
571
|
-
|
|
591
|
+
const { tier } = b;
|
|
592
|
+
const staggered = tier !== void 0 && b.colors.length === 1;
|
|
593
|
+
const rows = staggered ? 2 : b.colors.length;
|
|
594
|
+
b.colors.forEach((color, i) => {
|
|
595
|
+
const row = staggered ? tier : i;
|
|
596
|
+
const y0 = rowEdge(row, rows, dpr);
|
|
597
|
+
ctx.fillStyle = color;
|
|
598
|
+
ctx.fillRect(b.x0, y0, b.x1 - b.x0, rowEdge(row + 1, rows, dpr) - y0);
|
|
599
|
+
});
|
|
572
600
|
}
|
|
573
601
|
const overhang = stripOverhang(blocks, frame, width);
|
|
574
|
-
if (overhang.left > 0
|
|
575
|
-
ctx
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (overhang.right > 0) {
|
|
580
|
-
drawOverhang(ctx, width, -1);
|
|
581
|
-
}
|
|
582
|
-
ctx.fillStyle = ink;
|
|
583
|
-
ctx.fill();
|
|
584
|
-
ctx.strokeStyle = darkMode ? "#1f1f1f" : "#ffffff";
|
|
585
|
-
ctx.lineWidth = 1;
|
|
586
|
-
ctx.stroke();
|
|
602
|
+
if (overhang.left > 0) {
|
|
603
|
+
drawOverhang(ctx, 0, 1, ink);
|
|
604
|
+
}
|
|
605
|
+
if (overhang.right > 0) {
|
|
606
|
+
drawOverhang(ctx, width, -1, ink);
|
|
587
607
|
}
|
|
588
608
|
if (lit) {
|
|
589
609
|
const [x0, x1] = span(frame, lit.start, lit.end);
|
|
@@ -1042,7 +1062,7 @@ function niceStep(raw) {
|
|
|
1042
1062
|
const m = raw / pow;
|
|
1043
1063
|
return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow;
|
|
1044
1064
|
}
|
|
1045
|
-
function
|
|
1065
|
+
function placeLabels(candidates) {
|
|
1046
1066
|
const placed = [];
|
|
1047
1067
|
for (const a of candidates) {
|
|
1048
1068
|
if (placed.every(
|
|
@@ -1079,7 +1099,7 @@ function scaleMarks(boxes, x, width, measure) {
|
|
|
1079
1099
|
return {
|
|
1080
1100
|
spans: [{ x0: from, x1: to }],
|
|
1081
1101
|
ticks,
|
|
1082
|
-
labels:
|
|
1102
|
+
labels: placeLabels(positions)
|
|
1083
1103
|
};
|
|
1084
1104
|
}
|
|
1085
1105
|
function boxMarks(boxes, x, width, measure) {
|
|
@@ -1120,7 +1140,7 @@ function boxMarks(boxes, x, width, measure) {
|
|
|
1120
1140
|
return {
|
|
1121
1141
|
spans,
|
|
1122
1142
|
ticks,
|
|
1123
|
-
labels:
|
|
1143
|
+
labels: placeLabels([...ends, ...lengths, ...boundaries])
|
|
1124
1144
|
};
|
|
1125
1145
|
}
|
|
1126
1146
|
function rulerMarks(boxes, x, width, measure, referenceAxis = false) {
|
|
@@ -1688,219 +1708,6 @@ function bubbleSubgraph(graph, segmentIds) {
|
|
|
1688
1708
|
referencePath: graph.referencePath
|
|
1689
1709
|
};
|
|
1690
1710
|
}
|
|
1691
|
-
|
|
1692
|
-
// src/labelLayout.ts
|
|
1693
|
-
var HALO_FACTOR = 3.4;
|
|
1694
|
-
var LEGEND_INSET_PX = 6;
|
|
1695
|
-
var ROUTE_STACK = 8;
|
|
1696
|
-
var GENE_PIN_DROP_PX = 18;
|
|
1697
|
-
var GENE_STACK = 2;
|
|
1698
|
-
var TOPMOST_BASELINE = LABEL_PX + LABEL_PAD;
|
|
1699
|
-
function geneCoverageNote(pin) {
|
|
1700
|
-
const length = pin.gene.end - pin.gene.start;
|
|
1701
|
-
if (pin.covered >= 0.98) {
|
|
1702
|
-
return void 0;
|
|
1703
|
-
}
|
|
1704
|
-
const [per, unit] = length >= 1e6 ? [1e6, "Mb"] : length >= 1e3 ? [1e3, "kb"] : [1, "bp"];
|
|
1705
|
-
const amount = (bp) => +(bp / per).toFixed(per === 1 ? 0 : 1);
|
|
1706
|
-
return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`;
|
|
1707
|
-
}
|
|
1708
|
-
function byExtent(items, extent) {
|
|
1709
|
-
return [...items].sort((a, b) => extent(b) - extent(a));
|
|
1710
|
-
}
|
|
1711
|
-
function layoutLabels(m) {
|
|
1712
|
-
const { paneWidth: width, canvasHeight: height, translateX, translateY } = m;
|
|
1713
|
-
const { scaleX, scaleY } = m.axisScale;
|
|
1714
|
-
const frame = { width, height };
|
|
1715
|
-
const screen = (p) => ({
|
|
1716
|
-
x: p.x * scaleX + translateX,
|
|
1717
|
-
y: p.y * scaleY + translateY
|
|
1718
|
-
});
|
|
1719
|
-
const reserved = m.drawnRowLabels.map(
|
|
1720
|
-
({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
|
|
1721
|
-
);
|
|
1722
|
-
const stripPx = m.referenceStripZonePx ?? 0;
|
|
1723
|
-
if (stripPx > 0) {
|
|
1724
|
-
reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
|
|
1725
|
-
}
|
|
1726
|
-
if (m.legendSize.width > 0) {
|
|
1727
|
-
reserved.push({
|
|
1728
|
-
x0: width - LEGEND_INSET_PX - m.legendSize.width,
|
|
1729
|
-
x1: width,
|
|
1730
|
-
y0: 0,
|
|
1731
|
-
y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
|
|
1732
|
-
});
|
|
1733
|
-
}
|
|
1734
|
-
if (m.poppedFrom) {
|
|
1735
|
-
reserved.push({
|
|
1736
|
-
x0: 0,
|
|
1737
|
-
x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,
|
|
1738
|
-
y0: 0,
|
|
1739
|
-
y1: 44
|
|
1740
|
-
});
|
|
1741
|
-
}
|
|
1742
|
-
const take = occupancy(frame, reserved);
|
|
1743
|
-
const onRows = m.drawnRowLabels.length > 0;
|
|
1744
|
-
const rowsTop = onRows ? Math.min(...m.drawnRowLabels.map((r) => r.y)) : Infinity;
|
|
1745
|
-
const halo = m.contigThickness * HALO_FACTOR;
|
|
1746
|
-
const byBubble = byExtent(m.bubbleHalos, (h) => h.members);
|
|
1747
|
-
const bubbles = placeLabels(
|
|
1748
|
-
byBubble.map((h) => {
|
|
1749
|
-
const { x, y } = screen({
|
|
1750
|
-
x: h.labelAt.x,
|
|
1751
|
-
y: Math.min(h.labelAt.y, rowsTop)
|
|
1752
|
-
});
|
|
1753
|
-
const baseline = y - halo / 2 - 6;
|
|
1754
|
-
return {
|
|
1755
|
-
item: h,
|
|
1756
|
-
x,
|
|
1757
|
-
y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,
|
|
1758
|
-
text: h.label
|
|
1759
|
-
};
|
|
1760
|
-
}),
|
|
1761
|
-
frame,
|
|
1762
|
-
take
|
|
1763
|
-
);
|
|
1764
|
-
const genes = placeLabels(
|
|
1765
|
-
byExtent(m.genePins, (pin) => pin.gene.end - pin.gene.start).map((pin) => {
|
|
1766
|
-
const { x, y } = screen(pin.at);
|
|
1767
|
-
const note = geneCoverageNote(pin);
|
|
1768
|
-
return {
|
|
1769
|
-
item: pin,
|
|
1770
|
-
x,
|
|
1771
|
-
y: y + m.contigThickness + GENE_PIN_DROP_PX,
|
|
1772
|
-
text: note ? `${pin.gene.name} \xB7 ${note}` : pin.gene.name,
|
|
1773
|
-
stack: GENE_STACK
|
|
1774
|
-
};
|
|
1775
|
-
}),
|
|
1776
|
-
frame,
|
|
1777
|
-
take
|
|
1778
|
-
);
|
|
1779
|
-
const sizeCandidates = m.nodePositions && m.labelsNodeSizes ? sizeLabelCandidates({
|
|
1780
|
-
nodePositions: m.nodePositions,
|
|
1781
|
-
nodeLengths: m.nodeLengths,
|
|
1782
|
-
deletions: m.showDeletionEdges ? m.deletions : [],
|
|
1783
|
-
alleleDeletions: m.alleleDeletions,
|
|
1784
|
-
axis: m.axisScale,
|
|
1785
|
-
translateX,
|
|
1786
|
-
translateY,
|
|
1787
|
-
width,
|
|
1788
|
-
height,
|
|
1789
|
-
version: m.positionsVersion
|
|
1790
|
-
}) : { deletions: [], nodes: [] };
|
|
1791
|
-
const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
|
|
1792
|
-
const routes2 = placeLabels(
|
|
1793
|
-
byBubble.flatMap(
|
|
1794
|
-
(h) => h.routes.map((route) => {
|
|
1795
|
-
const { x, y } = screen(route.at);
|
|
1796
|
-
return {
|
|
1797
|
-
item: { halo: h, route },
|
|
1798
|
-
x,
|
|
1799
|
-
y: y + 4,
|
|
1800
|
-
text: route.text,
|
|
1801
|
-
stack: ROUTE_STACK
|
|
1802
|
-
};
|
|
1803
|
-
})
|
|
1804
|
-
),
|
|
1805
|
-
frame,
|
|
1806
|
-
take
|
|
1807
|
-
);
|
|
1808
|
-
const nodeLabels = placeSizeLabels(
|
|
1809
|
-
sizeCandidates.nodes,
|
|
1810
|
-
take,
|
|
1811
|
-
nodeLabelBudget(width, height)
|
|
1812
|
-
);
|
|
1813
|
-
return {
|
|
1814
|
-
bubbles,
|
|
1815
|
-
genes,
|
|
1816
|
-
routes: routes2,
|
|
1817
|
-
sizes: [...deletionLabels, ...nodeLabels]
|
|
1818
|
-
};
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
// src/genes/genePins.ts
|
|
1822
|
-
function genePins(graph, genes, positions) {
|
|
1823
|
-
const byRefName = /* @__PURE__ */ new Map();
|
|
1824
|
-
for (const node of graph.nodes) {
|
|
1825
|
-
if (isBackbone(node)) {
|
|
1826
|
-
const { refName } = node.stable;
|
|
1827
|
-
if (positions[node.id]?.length) {
|
|
1828
|
-
const entry = byRefName.get(refName) ?? byRefName.set(refName, { nodes: [], reach: 0 }).get(refName);
|
|
1829
|
-
entry.nodes.push(node);
|
|
1830
|
-
entry.reach = Math.max(entry.reach, node.length);
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
for (const { nodes } of byRefName.values()) {
|
|
1835
|
-
nodes.sort((a, b) => a.stable.start - b.stable.start);
|
|
1836
|
-
}
|
|
1837
|
-
const pins = [];
|
|
1838
|
-
for (const gene of genes) {
|
|
1839
|
-
const backbone = byRefName.get(gene.refName);
|
|
1840
|
-
if (!backbone) {
|
|
1841
|
-
continue;
|
|
1842
|
-
}
|
|
1843
|
-
const parts = [];
|
|
1844
|
-
const exonsByNode = [];
|
|
1845
|
-
let at;
|
|
1846
|
-
let atDistance = Infinity;
|
|
1847
|
-
let covered = 0;
|
|
1848
|
-
const mid = (gene.start + gene.end) / 2;
|
|
1849
|
-
const { nodes, reach } = backbone;
|
|
1850
|
-
for (let i = firstNodeAtOrAfter(nodes, gene.start - reach); i < nodes.length && nodes[i].stable.start < gene.end; i++) {
|
|
1851
|
-
const node = nodes[i];
|
|
1852
|
-
const nodeStart = node.stable.start;
|
|
1853
|
-
const nodeEnd = nodeStart + node.length;
|
|
1854
|
-
if (nodeEnd <= gene.start) {
|
|
1855
|
-
continue;
|
|
1856
|
-
}
|
|
1857
|
-
const line = positions[node.id];
|
|
1858
|
-
covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start);
|
|
1859
|
-
const nodeParts = [];
|
|
1860
|
-
for (const exon of gene.exons) {
|
|
1861
|
-
const a = Math.max(exon.start, nodeStart);
|
|
1862
|
-
const b = Math.min(exon.end, nodeEnd);
|
|
1863
|
-
if (b <= a) {
|
|
1864
|
-
continue;
|
|
1865
|
-
}
|
|
1866
|
-
const stretch = polylineSlice(
|
|
1867
|
-
line,
|
|
1868
|
-
(a - nodeStart) / node.length,
|
|
1869
|
-
(b - nodeStart) / node.length
|
|
1870
|
-
);
|
|
1871
|
-
if (stretch.length === 1) {
|
|
1872
|
-
stretch.push({ ...stretch[0] });
|
|
1873
|
-
}
|
|
1874
|
-
nodeParts.push(svgPath(stretch));
|
|
1875
|
-
}
|
|
1876
|
-
if (nodeParts.length > 0) {
|
|
1877
|
-
parts.push(...nodeParts);
|
|
1878
|
-
exonsByNode.push({ nodeId: node.id, d: nodeParts.join("") });
|
|
1879
|
-
}
|
|
1880
|
-
const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd);
|
|
1881
|
-
const distance = Math.abs(pinBp - mid);
|
|
1882
|
-
if (distance < atDistance) {
|
|
1883
|
-
atDistance = distance;
|
|
1884
|
-
const [p] = polylineSlice(
|
|
1885
|
-
line,
|
|
1886
|
-
(pinBp - nodeStart) / node.length,
|
|
1887
|
-
(pinBp - nodeStart) / node.length
|
|
1888
|
-
);
|
|
1889
|
-
at = p;
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
if (at) {
|
|
1893
|
-
pins.push({
|
|
1894
|
-
gene,
|
|
1895
|
-
exons: parts.join(""),
|
|
1896
|
-
exonsByNode,
|
|
1897
|
-
at,
|
|
1898
|
-
covered: covered / (gene.end - gene.start)
|
|
1899
|
-
});
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
return pins;
|
|
1903
|
-
}
|
|
1904
1711
|
export {
|
|
1905
1712
|
BUBBLE_KIND_COLORS,
|
|
1906
1713
|
BUBBLE_KIND_NAMES,
|
|
@@ -1913,6 +1720,7 @@ export {
|
|
|
1913
1720
|
FIT_PADDING,
|
|
1914
1721
|
FORCE_LAYOUT_LABEL,
|
|
1915
1722
|
HALO_FACTOR,
|
|
1723
|
+
HPRC_GBZ,
|
|
1916
1724
|
LABEL_CHAR_PX,
|
|
1917
1725
|
LABEL_PAD,
|
|
1918
1726
|
LABEL_PX,
|
|
@@ -1938,6 +1746,7 @@ export {
|
|
|
1938
1746
|
computeReferenceRamp,
|
|
1939
1747
|
connectorAt,
|
|
1940
1748
|
contains,
|
|
1749
|
+
cutGbzRegion,
|
|
1941
1750
|
cutWindowGFA,
|
|
1942
1751
|
deletionEdges,
|
|
1943
1752
|
drawReferenceStrip,
|
|
@@ -1962,6 +1771,9 @@ export {
|
|
|
1962
1771
|
formatBp,
|
|
1963
1772
|
geneCoverageNote,
|
|
1964
1773
|
genePins,
|
|
1774
|
+
genesFromBed,
|
|
1775
|
+
genesFromGff3Lines,
|
|
1776
|
+
genesFromText,
|
|
1965
1777
|
getDpr,
|
|
1966
1778
|
graphBackbone,
|
|
1967
1779
|
haplotypeWanted,
|
|
@@ -1975,10 +1787,12 @@ export {
|
|
|
1975
1787
|
modeUsesLayoutEngine,
|
|
1976
1788
|
nodeInk,
|
|
1977
1789
|
nodeWidthPx,
|
|
1790
|
+
openGbz,
|
|
1978
1791
|
padded,
|
|
1979
1792
|
panSNContig,
|
|
1980
1793
|
panSNHaplotype,
|
|
1981
1794
|
panSNSample,
|
|
1795
|
+
parseRegion,
|
|
1982
1796
|
pathColorsLegible,
|
|
1983
1797
|
pathLegend,
|
|
1984
1798
|
rangeText,
|
|
@@ -2003,10 +1817,12 @@ export {
|
|
|
2003
1817
|
tubeMapNodeAt,
|
|
2004
1818
|
tubeMapPicture,
|
|
2005
1819
|
tubeX,
|
|
1820
|
+
version,
|
|
2006
1821
|
viewportOf,
|
|
2007
1822
|
walkHighlight,
|
|
2008
1823
|
walkKey,
|
|
2009
1824
|
walkLift,
|
|
1825
|
+
walkPosition,
|
|
2010
1826
|
walkRows,
|
|
2011
1827
|
walkRowsExtent,
|
|
2012
1828
|
warpX,
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { BubbleSpread } from '../bubbleSpreads.js';
|
|
2
2
|
import type { FigureOptions } from '../figure.js';
|
|
3
|
+
import type { GbzSource } from '../gbzCut.js';
|
|
3
4
|
import type { LayoutModeValue } from '../layoutModes.js';
|
|
4
5
|
import type { WalkLayer } from '../walkEncoding.js';
|
|
5
|
-
export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region'> {
|
|
6
|
+
export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region' | 'genes' | 'spec'> {
|
|
6
7
|
gfa?: string;
|
|
7
|
-
gbz?:
|
|
8
|
-
db: string;
|
|
9
|
-
index?: string;
|
|
10
|
-
region: string;
|
|
11
|
-
haplotypes?: string[];
|
|
12
|
-
referenceSample?: string;
|
|
13
|
-
};
|
|
8
|
+
gbz?: GbzSource;
|
|
14
9
|
region?: string;
|
|
15
10
|
referencePath?: string;
|
|
16
11
|
layout?: LayoutModeValue;
|
|
17
12
|
quality?: number;
|
|
18
13
|
bubbleSpread?: BubbleSpread;
|
|
19
14
|
walks?: (string | WalkLayer)[];
|
|
15
|
+
genes?: {
|
|
16
|
+
file: string;
|
|
17
|
+
index?: string;
|
|
18
|
+
format?: 'gff3' | 'bed';
|
|
19
|
+
};
|
|
20
20
|
}
|
package/dist/types/figure.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ColorScheme } from './colorSchemes.js';
|
|
2
2
|
import type { FacetBy } from './facetGrid.js';
|
|
3
|
+
import type { GeneModel } from './genes/genePins.js';
|
|
3
4
|
import type { NodeWidth } from './nodeWidths.js';
|
|
4
5
|
import type { Graph, LayoutResult } from './types.js';
|
|
5
6
|
import type { WalkLayer } from './walkEncoding.js';
|
|
@@ -19,6 +20,7 @@ export interface FigureOptions {
|
|
|
19
20
|
start: number;
|
|
20
21
|
end: number;
|
|
21
22
|
};
|
|
22
|
-
|
|
23
|
+
genes?: GeneModel[];
|
|
24
|
+
spec?: unknown;
|
|
23
25
|
}
|
|
24
26
|
export declare function figureSvg(graph: Graph, layout: LayoutResult, o?: FigureOptions): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GBZBase } from '@gmod/gbz-base';
|
|
2
|
+
import type { ByteSource, RangeOptions } from '@gmod/gbz-base';
|
|
3
|
+
export interface GbzSource {
|
|
4
|
+
db: string;
|
|
5
|
+
index?: string;
|
|
6
|
+
region: string;
|
|
7
|
+
haplotypes?: string[];
|
|
8
|
+
referenceSample?: string;
|
|
9
|
+
context?: number;
|
|
10
|
+
snarls?: RangeOptions['snarls'];
|
|
11
|
+
}
|
|
12
|
+
export declare const HPRC_GBZ: {
|
|
13
|
+
db: string;
|
|
14
|
+
index: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function parseRegion(text: string): {
|
|
17
|
+
refName: string;
|
|
18
|
+
start: number;
|
|
19
|
+
end: number;
|
|
20
|
+
};
|
|
21
|
+
export declare function openGbz(db: ByteSource, index?: ByteSource): Promise<{
|
|
22
|
+
base: GBZBase;
|
|
23
|
+
referenceSamples: string[];
|
|
24
|
+
}>;
|
|
25
|
+
export declare function cutGbzRegion({ base, referenceSamples }: Awaited<ReturnType<typeof openGbz>>, src: Omit<GbzSource, 'db' | 'index'>, signal?: AbortSignal): Promise<{
|
|
26
|
+
text: string;
|
|
27
|
+
region: {
|
|
28
|
+
refName: string;
|
|
29
|
+
start: number;
|
|
30
|
+
end: number;
|
|
31
|
+
};
|
|
32
|
+
sample: string;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GeneModel } from './genePins.js';
|
|
2
|
+
type Interval = GeneModel['exons'][number];
|
|
3
|
+
export declare function mergedIntervals(intervals: Interval[]): {
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
}[];
|
|
7
|
+
export declare function genesFromGff3Lines(lines: Iterable<string>): GeneModel[];
|
|
8
|
+
export declare function genesFromBed(text: string): GeneModel[];
|
|
9
|
+
export declare function genesFromText(text: string): GeneModel[];
|
|
10
|
+
export {};
|