@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
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,24 @@ 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
|
+
trimOrigins,
|
|
94
|
+
version,
|
|
89
95
|
viewportOf,
|
|
90
96
|
walkHighlight,
|
|
91
97
|
walkKey,
|
|
92
98
|
walkLift,
|
|
99
|
+
walkPosition,
|
|
93
100
|
walkRows,
|
|
94
101
|
walkRowsExtent,
|
|
95
102
|
wellKnownSample,
|
|
96
103
|
yToXOf,
|
|
97
104
|
zoomAbout
|
|
98
|
-
} from "./chunks/chunk-
|
|
105
|
+
} from "./chunks/chunk-75KVO2WV.js";
|
|
99
106
|
|
|
100
107
|
// src/index.ts
|
|
101
108
|
import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
|
|
@@ -212,14 +219,14 @@ var SpatialIndex = class {
|
|
|
212
219
|
}
|
|
213
220
|
};
|
|
214
221
|
var EdgeSpatialIndex = class {
|
|
215
|
-
constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions,
|
|
222
|
+
constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions, version2 = 0) {
|
|
216
223
|
this.cells = /* @__PURE__ */ new Map();
|
|
217
224
|
this.edgeCurves = baseEdgeCurves(
|
|
218
225
|
nodePositions,
|
|
219
226
|
graph,
|
|
220
227
|
axis,
|
|
221
228
|
deletions,
|
|
222
|
-
|
|
229
|
+
version2
|
|
223
230
|
);
|
|
224
231
|
const boxes = [];
|
|
225
232
|
let extentX = 0;
|
|
@@ -332,19 +339,19 @@ function distanceToEdgeCurves(px, py, curves, yToX = 1) {
|
|
|
332
339
|
return minDist;
|
|
333
340
|
}
|
|
334
341
|
var nodeCache = /* @__PURE__ */ new WeakMap();
|
|
335
|
-
function getSpatialIndex(nodePositions,
|
|
342
|
+
function getSpatialIndex(nodePositions, version2) {
|
|
336
343
|
const cached = nodeCache.get(nodePositions);
|
|
337
|
-
if (cached?.version ===
|
|
344
|
+
if (cached?.version === version2) {
|
|
338
345
|
return cached.index;
|
|
339
346
|
}
|
|
340
347
|
const index = new SpatialIndex(nodePositions);
|
|
341
|
-
nodeCache.set(nodePositions, { version, index });
|
|
348
|
+
nodeCache.set(nodePositions, { version: version2, index });
|
|
342
349
|
return index;
|
|
343
350
|
}
|
|
344
351
|
var edgeCache = /* @__PURE__ */ new WeakMap();
|
|
345
|
-
function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis,
|
|
352
|
+
function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version2, deletions) {
|
|
346
353
|
const cached = edgeCache.get(nodePositions);
|
|
347
|
-
if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version ===
|
|
354
|
+
if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version === version2 && cached.deletions === deletions) {
|
|
348
355
|
return cached.index;
|
|
349
356
|
}
|
|
350
357
|
const index = new EdgeSpatialIndex(
|
|
@@ -354,23 +361,23 @@ function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version, del
|
|
|
354
361
|
axis,
|
|
355
362
|
void 0,
|
|
356
363
|
deletions,
|
|
357
|
-
|
|
364
|
+
version2
|
|
358
365
|
);
|
|
359
366
|
edgeCache.set(nodePositions, {
|
|
360
367
|
graph,
|
|
361
368
|
drawPaths,
|
|
362
369
|
scaleX: axis.scaleX,
|
|
363
370
|
scaleY: axis.scaleY,
|
|
364
|
-
version,
|
|
371
|
+
version: version2,
|
|
365
372
|
index,
|
|
366
373
|
deletions
|
|
367
374
|
});
|
|
368
375
|
return index;
|
|
369
376
|
}
|
|
370
377
|
var MIN_NODE_REACH_PX = 5;
|
|
371
|
-
function findHoveredNode(nodePositions, graphX, graphY, axis,
|
|
378
|
+
function findHoveredNode(nodePositions, graphX, graphY, axis, version2 = 0, ink) {
|
|
372
379
|
const yToX = yToXOf(axis);
|
|
373
|
-
const index = getSpatialIndex(nodePositions,
|
|
380
|
+
const index = getSpatialIndex(nodePositions, version2);
|
|
374
381
|
const reach = (px) => Math.max(MIN_NODE_REACH_PX, px) / axis.scaleX;
|
|
375
382
|
const queryReach = reach(ink?.maxHalfWidthPx ?? 0);
|
|
376
383
|
const candidates = index.query(graphX, graphY, queryReach, queryReach / yToX);
|
|
@@ -393,7 +400,7 @@ function findHoveredNode(nodePositions, graphX, graphY, axis, version = 0, ink)
|
|
|
393
400
|
}
|
|
394
401
|
return hovered;
|
|
395
402
|
}
|
|
396
|
-
function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths,
|
|
403
|
+
function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths, version2 = 0, deletions, hiddenEdges) {
|
|
397
404
|
const yToX = yToXOf(axis);
|
|
398
405
|
const edgeThreshold = 10 / axis.scaleX;
|
|
399
406
|
const edgeIndex = getEdgeSpatialIndex(
|
|
@@ -401,7 +408,7 @@ function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths,
|
|
|
401
408
|
graph,
|
|
402
409
|
drawPaths,
|
|
403
410
|
axis,
|
|
404
|
-
|
|
411
|
+
version2,
|
|
405
412
|
deletions
|
|
406
413
|
);
|
|
407
414
|
const candidates = edgeIndex.query(
|
|
@@ -470,6 +477,7 @@ var REFERENCE_STRIP_PX = 12;
|
|
|
470
477
|
var REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8;
|
|
471
478
|
var OVERHANG_PX = 8;
|
|
472
479
|
var OVERHANG_CAP_PX = OVERHANG_PX + 3;
|
|
480
|
+
var MIN_STAGGER_PX = 12;
|
|
473
481
|
var PALE = abgrToCssRgba(fadeAbgr(LIFT_BACKDROP_COLOR, FADED_ALPHA));
|
|
474
482
|
function referenceStripBlocks(graph, {
|
|
475
483
|
colorScheme,
|
|
@@ -553,7 +561,8 @@ function stripPixels(blocks, frame, width, dpr) {
|
|
|
553
561
|
claimed = px1;
|
|
554
562
|
}
|
|
555
563
|
}
|
|
556
|
-
|
|
564
|
+
const covered = out.length > 0 ? out.at(-1).x1 - out[0].x0 : 0;
|
|
565
|
+
return covered >= out.length * MIN_STAGGER_PX ? out : out.map(({ tier: _, ...b }) => b);
|
|
557
566
|
}
|
|
558
567
|
function drawOverhang(ctx, edgeX, inward, ink) {
|
|
559
568
|
const capX = inward > 0 ? edgeX : edgeX - OVERHANG_CAP_PX;
|
|
@@ -580,9 +589,11 @@ function drawReferenceStrip(ctx, blocks, frame, {
|
|
|
580
589
|
const ink = darkMode ? "#ffffff" : "#18181c";
|
|
581
590
|
ctx.clearRect(0, 0, width, REFERENCE_STRIP_ZONE_PX);
|
|
582
591
|
for (const b of stripPixels(blocks, frame, width, dpr)) {
|
|
583
|
-
const
|
|
592
|
+
const { tier } = b;
|
|
593
|
+
const staggered = tier !== void 0 && b.colors.length === 1;
|
|
594
|
+
const rows = staggered ? 2 : b.colors.length;
|
|
584
595
|
b.colors.forEach((color, i) => {
|
|
585
|
-
const row =
|
|
596
|
+
const row = staggered ? tier : i;
|
|
586
597
|
const y0 = rowEdge(row, rows, dpr);
|
|
587
598
|
ctx.fillStyle = color;
|
|
588
599
|
ctx.fillRect(b.x0, y0, b.x1 - b.x0, rowEdge(row + 1, rows, dpr) - y0);
|
|
@@ -715,10 +726,11 @@ function shapeOf(d, color, alpha) {
|
|
|
715
726
|
const commands = parsePath(d);
|
|
716
727
|
return { commands, color, alpha, ...extentOf(commands) };
|
|
717
728
|
}
|
|
718
|
-
function layerOf(layout, type) {
|
|
729
|
+
function layerOf(layout, type, tubes) {
|
|
719
730
|
const { shapes } = layout;
|
|
731
|
+
const colorOf = (s) => tubes?.[s.id] ?? s.color;
|
|
720
732
|
const rects = [...shapes.rectangles, ...shapes.verticalRectangles].filter((r) => r.type === type).map((r) => ({
|
|
721
|
-
color: r
|
|
733
|
+
color: colorOf(r),
|
|
722
734
|
alpha: r.alpha ?? 1,
|
|
723
735
|
x0: r.xStart,
|
|
724
736
|
x1: r.xEnd + 1,
|
|
@@ -726,22 +738,30 @@ function layerOf(layout, type) {
|
|
|
726
738
|
y1: r.yEnd + 1
|
|
727
739
|
}));
|
|
728
740
|
const curves = curvePaths(shapes.curves, type).map(
|
|
729
|
-
(c) => shapeOf(c.path, c
|
|
741
|
+
(c) => shapeOf(c.path, colorOf(c), c.alpha ?? 1)
|
|
730
742
|
);
|
|
731
|
-
const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, c
|
|
743
|
+
const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, colorOf(c), 1));
|
|
732
744
|
return { rects, shapes: [...curves, ...corners] };
|
|
733
745
|
}
|
|
734
|
-
function tubeMapPicture(layout) {
|
|
746
|
+
function tubeMapPicture(layout, colors = {}) {
|
|
735
747
|
const nodes = [];
|
|
736
748
|
layout.nodes.forEach((node) => {
|
|
737
749
|
if (node.order >= 0) {
|
|
738
750
|
const commands = parsePath(nodeOutlinePath(node));
|
|
739
|
-
nodes.push({
|
|
751
|
+
nodes.push({
|
|
752
|
+
name: node.name,
|
|
753
|
+
commands,
|
|
754
|
+
...extentOf(commands),
|
|
755
|
+
color: colors.nodes?.get(node.name)
|
|
756
|
+
});
|
|
740
757
|
}
|
|
741
758
|
});
|
|
742
759
|
return {
|
|
743
760
|
bounds: layout.bounds,
|
|
744
|
-
layers: [
|
|
761
|
+
layers: [
|
|
762
|
+
layerOf(layout, "haplotype", colors.tubes),
|
|
763
|
+
layerOf(layout, "read")
|
|
764
|
+
],
|
|
745
765
|
nodes,
|
|
746
766
|
mismatches: tubeMapMismatches(layout).filter(
|
|
747
767
|
(m) => m.kind !== "insertion" || !m.softClip
|
|
@@ -816,6 +836,7 @@ var MIN_OUTLINE_ALPHA = 0.15;
|
|
|
816
836
|
function outlineAlpha(screenWidth) {
|
|
817
837
|
return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX));
|
|
818
838
|
}
|
|
839
|
+
var NODE_TINT_ALPHA = 0.55;
|
|
819
840
|
function drawTubeMap(ctx, picture, frame) {
|
|
820
841
|
const { x, y, width } = frame;
|
|
821
842
|
const visible = (item) => x(item.x1) >= 0 && x(item.x0) <= width;
|
|
@@ -837,8 +858,10 @@ function drawTubeMap(ctx, picture, frame) {
|
|
|
837
858
|
const lit = node.name === frame.highlightNode;
|
|
838
859
|
ctx.beginPath();
|
|
839
860
|
trace(ctx, node.commands, x, y);
|
|
840
|
-
|
|
861
|
+
const tint = lit ? void 0 : node.color;
|
|
862
|
+
ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : tint ?? fill;
|
|
841
863
|
ctx.strokeStyle = lit ? "#ff0000" : stroke;
|
|
864
|
+
ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
|
|
842
865
|
ctx.fill();
|
|
843
866
|
ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0));
|
|
844
867
|
ctx.stroke();
|
|
@@ -887,6 +910,21 @@ function drawMismatches(ctx, marks, frame) {
|
|
|
887
910
|
}
|
|
888
911
|
}
|
|
889
912
|
|
|
913
|
+
// src/tubeMap/nodeColors.ts
|
|
914
|
+
import { abgrToCssRgba as abgrToCssRgba2 } from "@jbrowse/core/util/colorBits";
|
|
915
|
+
function tubeMapNodeColors(graph, colorScheme, referenceRamp) {
|
|
916
|
+
if (colorScheme === "uniform" || colorScheme === "grey") {
|
|
917
|
+
return void 0;
|
|
918
|
+
}
|
|
919
|
+
const range = { ...computeColorSchemeRange(graph), referenceRamp };
|
|
920
|
+
return new Map(
|
|
921
|
+
graph.nodes.map((node, index) => [
|
|
922
|
+
node.id,
|
|
923
|
+
abgrToCssRgba2(getNodeColor(node, index, colorScheme, range))
|
|
924
|
+
])
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
|
|
890
928
|
// src/tubeMap/warp.ts
|
|
891
929
|
var MAX_GAP_PX = 24;
|
|
892
930
|
var isReal = (c) => c.bp1 > c.bp0;
|
|
@@ -1052,7 +1090,7 @@ function niceStep(raw) {
|
|
|
1052
1090
|
const m = raw / pow;
|
|
1053
1091
|
return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow;
|
|
1054
1092
|
}
|
|
1055
|
-
function
|
|
1093
|
+
function placeLabels(candidates) {
|
|
1056
1094
|
const placed = [];
|
|
1057
1095
|
for (const a of candidates) {
|
|
1058
1096
|
if (placed.every(
|
|
@@ -1089,7 +1127,7 @@ function scaleMarks(boxes, x, width, measure) {
|
|
|
1089
1127
|
return {
|
|
1090
1128
|
spans: [{ x0: from, x1: to }],
|
|
1091
1129
|
ticks,
|
|
1092
|
-
labels:
|
|
1130
|
+
labels: placeLabels(positions)
|
|
1093
1131
|
};
|
|
1094
1132
|
}
|
|
1095
1133
|
function boxMarks(boxes, x, width, measure) {
|
|
@@ -1130,7 +1168,7 @@ function boxMarks(boxes, x, width, measure) {
|
|
|
1130
1168
|
return {
|
|
1131
1169
|
spans,
|
|
1132
1170
|
ticks,
|
|
1133
|
-
labels:
|
|
1171
|
+
labels: placeLabels([...ends, ...lengths, ...boundaries])
|
|
1134
1172
|
};
|
|
1135
1173
|
}
|
|
1136
1174
|
function rulerMarks(boxes, x, width, measure, referenceAxis = false) {
|
|
@@ -1686,232 +1724,32 @@ function bubbleSubgraph(graph, segmentIds) {
|
|
|
1686
1724
|
const keep = new Set(segmentIds);
|
|
1687
1725
|
const nodes = graph.nodes.filter((n) => keep.has(n.name));
|
|
1688
1726
|
const ids = new Set(nodes.map((n) => n.id));
|
|
1689
|
-
const
|
|
1727
|
+
const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
|
|
1728
|
+
const bpOf = (steps) => steps.reduce((sum, id) => sum + (lengthOf.get(id) ?? 0), 0);
|
|
1729
|
+
const stretches = /* @__PURE__ */ new Map();
|
|
1730
|
+
const paths = graph.paths?.flatMap((p) => {
|
|
1731
|
+
const first = p.nodeIds.findIndex((id) => ids.has(id));
|
|
1732
|
+
if (first < 0) {
|
|
1733
|
+
return [];
|
|
1734
|
+
}
|
|
1735
|
+
const nodeIds = p.nodeIds.filter((id) => ids.has(id));
|
|
1736
|
+
stretches.set(p.name, {
|
|
1737
|
+
dropped: bpOf(p.nodeIds.slice(0, first)),
|
|
1738
|
+
bp: bpOf(nodeIds)
|
|
1739
|
+
});
|
|
1740
|
+
return [{ ...p, nodeIds }];
|
|
1741
|
+
});
|
|
1690
1742
|
return {
|
|
1691
1743
|
name: graph.name,
|
|
1692
1744
|
nodes,
|
|
1693
1745
|
edges: graph.edges.filter((e) => ids.has(e.from) && ids.has(e.to)),
|
|
1694
1746
|
...paths?.length ? { paths } : {},
|
|
1695
|
-
anchorPaths: graph.anchorPaths,
|
|
1747
|
+
anchorPaths: graph.anchorPaths && trimOrigins(graph.anchorPaths, graph.paths ?? [], stretches),
|
|
1696
1748
|
pathVisits: graph.pathVisits,
|
|
1697
1749
|
anchoredBy: graph.anchoredBy,
|
|
1698
1750
|
referencePath: graph.referencePath
|
|
1699
1751
|
};
|
|
1700
1752
|
}
|
|
1701
|
-
|
|
1702
|
-
// src/labelLayout.ts
|
|
1703
|
-
var HALO_FACTOR = 3.4;
|
|
1704
|
-
var LEGEND_INSET_PX = 6;
|
|
1705
|
-
var ROUTE_STACK = 8;
|
|
1706
|
-
var GENE_PIN_DROP_PX = 18;
|
|
1707
|
-
var GENE_STACK = 2;
|
|
1708
|
-
var TOPMOST_BASELINE = LABEL_PX + LABEL_PAD;
|
|
1709
|
-
function geneCoverageNote(pin) {
|
|
1710
|
-
const length = pin.gene.end - pin.gene.start;
|
|
1711
|
-
if (pin.covered >= 0.98) {
|
|
1712
|
-
return void 0;
|
|
1713
|
-
}
|
|
1714
|
-
const [per, unit] = length >= 1e6 ? [1e6, "Mb"] : length >= 1e3 ? [1e3, "kb"] : [1, "bp"];
|
|
1715
|
-
const amount = (bp) => +(bp / per).toFixed(per === 1 ? 0 : 1);
|
|
1716
|
-
return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`;
|
|
1717
|
-
}
|
|
1718
|
-
function byExtent(items, extent) {
|
|
1719
|
-
return [...items].sort((a, b) => extent(b) - extent(a));
|
|
1720
|
-
}
|
|
1721
|
-
function layoutLabels(m) {
|
|
1722
|
-
const { paneWidth: width, canvasHeight: height, translateX, translateY } = m;
|
|
1723
|
-
const { scaleX, scaleY } = m.axisScale;
|
|
1724
|
-
const frame = { width, height };
|
|
1725
|
-
const screen = (p) => ({
|
|
1726
|
-
x: p.x * scaleX + translateX,
|
|
1727
|
-
y: p.y * scaleY + translateY
|
|
1728
|
-
});
|
|
1729
|
-
const reserved = m.drawnRowLabels.map(
|
|
1730
|
-
({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
|
|
1731
|
-
);
|
|
1732
|
-
const stripPx = m.referenceStripZonePx ?? 0;
|
|
1733
|
-
if (stripPx > 0) {
|
|
1734
|
-
reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
|
|
1735
|
-
}
|
|
1736
|
-
if (m.legendSize.width > 0) {
|
|
1737
|
-
reserved.push({
|
|
1738
|
-
x0: width - LEGEND_INSET_PX - m.legendSize.width,
|
|
1739
|
-
x1: width,
|
|
1740
|
-
y0: 0,
|
|
1741
|
-
y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
|
|
1742
|
-
});
|
|
1743
|
-
}
|
|
1744
|
-
if (m.poppedFrom) {
|
|
1745
|
-
reserved.push({
|
|
1746
|
-
x0: 0,
|
|
1747
|
-
x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,
|
|
1748
|
-
y0: 0,
|
|
1749
|
-
y1: 44
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
const take = occupancy(frame, reserved);
|
|
1753
|
-
const onRows = m.drawnRowLabels.length > 0;
|
|
1754
|
-
const rowsTop = onRows ? Math.min(...m.drawnRowLabels.map((r) => r.y)) : Infinity;
|
|
1755
|
-
const halo = m.contigThickness * HALO_FACTOR;
|
|
1756
|
-
const byBubble = byExtent(m.bubbleHalos, (h) => h.members);
|
|
1757
|
-
const bubbles = placeLabels(
|
|
1758
|
-
byBubble.map((h) => {
|
|
1759
|
-
const { x, y } = screen({
|
|
1760
|
-
x: h.labelAt.x,
|
|
1761
|
-
y: Math.min(h.labelAt.y, rowsTop)
|
|
1762
|
-
});
|
|
1763
|
-
const baseline = y - halo / 2 - 6;
|
|
1764
|
-
return {
|
|
1765
|
-
item: h,
|
|
1766
|
-
x,
|
|
1767
|
-
y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,
|
|
1768
|
-
text: h.label
|
|
1769
|
-
};
|
|
1770
|
-
}),
|
|
1771
|
-
frame,
|
|
1772
|
-
take
|
|
1773
|
-
);
|
|
1774
|
-
const genes = placeLabels(
|
|
1775
|
-
byExtent(m.genePins, (pin) => pin.gene.end - pin.gene.start).map((pin) => {
|
|
1776
|
-
const { x, y } = screen(pin.at);
|
|
1777
|
-
const note = geneCoverageNote(pin);
|
|
1778
|
-
return {
|
|
1779
|
-
item: pin,
|
|
1780
|
-
x,
|
|
1781
|
-
y: y + m.contigThickness + GENE_PIN_DROP_PX,
|
|
1782
|
-
text: note ? `${pin.gene.name} \xB7 ${note}` : pin.gene.name,
|
|
1783
|
-
fallback: note ? `${pin.gene.name} \u2026` : void 0,
|
|
1784
|
-
stack: GENE_STACK
|
|
1785
|
-
};
|
|
1786
|
-
}),
|
|
1787
|
-
frame,
|
|
1788
|
-
take
|
|
1789
|
-
);
|
|
1790
|
-
const sizeCandidates = m.nodePositions && m.labelsNodeSizes ? sizeLabelCandidates({
|
|
1791
|
-
nodePositions: m.nodePositions,
|
|
1792
|
-
nodeLengths: m.nodeLengths,
|
|
1793
|
-
deletions: m.showDeletionEdges ? m.deletions : [],
|
|
1794
|
-
alleleDeletions: m.alleleDeletions,
|
|
1795
|
-
axis: m.axisScale,
|
|
1796
|
-
translateX,
|
|
1797
|
-
translateY,
|
|
1798
|
-
width,
|
|
1799
|
-
height,
|
|
1800
|
-
version: m.positionsVersion
|
|
1801
|
-
}) : { deletions: [], nodes: [] };
|
|
1802
|
-
const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
|
|
1803
|
-
const routes2 = placeLabels(
|
|
1804
|
-
byBubble.flatMap(
|
|
1805
|
-
(h) => h.routes.map((route) => {
|
|
1806
|
-
const { x, y } = screen(route.at);
|
|
1807
|
-
return {
|
|
1808
|
-
item: { halo: h, route },
|
|
1809
|
-
x,
|
|
1810
|
-
y: y + 4,
|
|
1811
|
-
text: route.text,
|
|
1812
|
-
stack: ROUTE_STACK
|
|
1813
|
-
};
|
|
1814
|
-
})
|
|
1815
|
-
),
|
|
1816
|
-
frame,
|
|
1817
|
-
take
|
|
1818
|
-
);
|
|
1819
|
-
const nodeLabels = placeSizeLabels(
|
|
1820
|
-
sizeCandidates.nodes,
|
|
1821
|
-
take,
|
|
1822
|
-
nodeLabelBudget(width, height)
|
|
1823
|
-
);
|
|
1824
|
-
return {
|
|
1825
|
-
bubbles,
|
|
1826
|
-
genes,
|
|
1827
|
-
routes: routes2,
|
|
1828
|
-
sizes: [...deletionLabels, ...nodeLabels]
|
|
1829
|
-
};
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
// src/genes/genePins.ts
|
|
1833
|
-
function genePins(graph, genes, positions) {
|
|
1834
|
-
const byRefName = /* @__PURE__ */ new Map();
|
|
1835
|
-
for (const node of graph.nodes) {
|
|
1836
|
-
if (isBackbone(node)) {
|
|
1837
|
-
const { refName } = node.stable;
|
|
1838
|
-
if (positions[node.id]?.length) {
|
|
1839
|
-
const entry = byRefName.get(refName) ?? byRefName.set(refName, { nodes: [], reach: 0 }).get(refName);
|
|
1840
|
-
entry.nodes.push(node);
|
|
1841
|
-
entry.reach = Math.max(entry.reach, node.length);
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
for (const { nodes } of byRefName.values()) {
|
|
1846
|
-
nodes.sort((a, b) => a.stable.start - b.stable.start);
|
|
1847
|
-
}
|
|
1848
|
-
const pins = [];
|
|
1849
|
-
for (const gene of genes) {
|
|
1850
|
-
const backbone = byRefName.get(gene.refName);
|
|
1851
|
-
if (!backbone) {
|
|
1852
|
-
continue;
|
|
1853
|
-
}
|
|
1854
|
-
const parts = [];
|
|
1855
|
-
const exonsByNode = [];
|
|
1856
|
-
let at;
|
|
1857
|
-
let atDistance = Infinity;
|
|
1858
|
-
let covered = 0;
|
|
1859
|
-
const mid = (gene.start + gene.end) / 2;
|
|
1860
|
-
const { nodes, reach } = backbone;
|
|
1861
|
-
for (let i = firstNodeAtOrAfter(nodes, gene.start - reach); i < nodes.length && nodes[i].stable.start < gene.end; i++) {
|
|
1862
|
-
const node = nodes[i];
|
|
1863
|
-
const nodeStart = node.stable.start;
|
|
1864
|
-
const nodeEnd = nodeStart + node.length;
|
|
1865
|
-
if (nodeEnd <= gene.start) {
|
|
1866
|
-
continue;
|
|
1867
|
-
}
|
|
1868
|
-
const line = positions[node.id];
|
|
1869
|
-
covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start);
|
|
1870
|
-
const nodeParts = [];
|
|
1871
|
-
for (const exon of gene.exons) {
|
|
1872
|
-
const a = Math.max(exon.start, nodeStart);
|
|
1873
|
-
const b = Math.min(exon.end, nodeEnd);
|
|
1874
|
-
if (b <= a) {
|
|
1875
|
-
continue;
|
|
1876
|
-
}
|
|
1877
|
-
const stretch = polylineSlice(
|
|
1878
|
-
line,
|
|
1879
|
-
(a - nodeStart) / node.length,
|
|
1880
|
-
(b - nodeStart) / node.length
|
|
1881
|
-
);
|
|
1882
|
-
if (stretch.length === 1) {
|
|
1883
|
-
stretch.push({ ...stretch[0] });
|
|
1884
|
-
}
|
|
1885
|
-
nodeParts.push(svgPath(stretch));
|
|
1886
|
-
}
|
|
1887
|
-
if (nodeParts.length > 0) {
|
|
1888
|
-
parts.push(...nodeParts);
|
|
1889
|
-
exonsByNode.push({ nodeId: node.id, d: nodeParts.join("") });
|
|
1890
|
-
}
|
|
1891
|
-
const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd);
|
|
1892
|
-
const distance = Math.abs(pinBp - mid);
|
|
1893
|
-
if (distance < atDistance) {
|
|
1894
|
-
atDistance = distance;
|
|
1895
|
-
const [p] = polylineSlice(
|
|
1896
|
-
line,
|
|
1897
|
-
(pinBp - nodeStart) / node.length,
|
|
1898
|
-
(pinBp - nodeStart) / node.length
|
|
1899
|
-
);
|
|
1900
|
-
at = p;
|
|
1901
|
-
}
|
|
1902
|
-
}
|
|
1903
|
-
if (at) {
|
|
1904
|
-
pins.push({
|
|
1905
|
-
gene,
|
|
1906
|
-
exons: parts.join(""),
|
|
1907
|
-
exonsByNode,
|
|
1908
|
-
at,
|
|
1909
|
-
covered: covered / (gene.end - gene.start)
|
|
1910
|
-
});
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
return pins;
|
|
1914
|
-
}
|
|
1915
1753
|
export {
|
|
1916
1754
|
BUBBLE_KIND_COLORS,
|
|
1917
1755
|
BUBBLE_KIND_NAMES,
|
|
@@ -1924,6 +1762,7 @@ export {
|
|
|
1924
1762
|
FIT_PADDING,
|
|
1925
1763
|
FORCE_LAYOUT_LABEL,
|
|
1926
1764
|
HALO_FACTOR,
|
|
1765
|
+
HPRC_GBZ,
|
|
1927
1766
|
LABEL_CHAR_PX,
|
|
1928
1767
|
LABEL_PAD,
|
|
1929
1768
|
LABEL_PX,
|
|
@@ -1949,6 +1788,7 @@ export {
|
|
|
1949
1788
|
computeReferenceRamp,
|
|
1950
1789
|
connectorAt,
|
|
1951
1790
|
contains,
|
|
1791
|
+
cutGbzRegion,
|
|
1952
1792
|
cutWindowGFA,
|
|
1953
1793
|
deletionEdges,
|
|
1954
1794
|
drawReferenceStrip,
|
|
@@ -1973,6 +1813,9 @@ export {
|
|
|
1973
1813
|
formatBp,
|
|
1974
1814
|
geneCoverageNote,
|
|
1975
1815
|
genePins,
|
|
1816
|
+
genesFromBed,
|
|
1817
|
+
genesFromGff3Lines,
|
|
1818
|
+
genesFromText,
|
|
1976
1819
|
getDpr,
|
|
1977
1820
|
graphBackbone,
|
|
1978
1821
|
haplotypeWanted,
|
|
@@ -1986,10 +1829,12 @@ export {
|
|
|
1986
1829
|
modeUsesLayoutEngine,
|
|
1987
1830
|
nodeInk,
|
|
1988
1831
|
nodeWidthPx,
|
|
1832
|
+
openGbz,
|
|
1989
1833
|
padded,
|
|
1990
1834
|
panSNContig,
|
|
1991
1835
|
panSNHaplotype,
|
|
1992
1836
|
panSNSample,
|
|
1837
|
+
parseRegion,
|
|
1993
1838
|
pathColorsLegible,
|
|
1994
1839
|
pathLegend,
|
|
1995
1840
|
rangeText,
|
|
@@ -2012,12 +1857,15 @@ export {
|
|
|
2012
1857
|
tubeMapFrame,
|
|
2013
1858
|
tubeMapGenes,
|
|
2014
1859
|
tubeMapNodeAt,
|
|
1860
|
+
tubeMapNodeColors,
|
|
2015
1861
|
tubeMapPicture,
|
|
2016
1862
|
tubeX,
|
|
1863
|
+
version,
|
|
2017
1864
|
viewportOf,
|
|
2018
1865
|
walkHighlight,
|
|
2019
1866
|
walkKey,
|
|
2020
1867
|
walkLift,
|
|
1868
|
+
walkPosition,
|
|
2021
1869
|
walkRows,
|
|
2022
1870
|
walkRowsExtent,
|
|
2023
1871
|
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,12 @@ export interface FigureOptions {
|
|
|
19
20
|
start: number;
|
|
20
21
|
end: number;
|
|
21
22
|
};
|
|
22
|
-
|
|
23
|
+
colorDomain?: {
|
|
24
|
+
start: number;
|
|
25
|
+
end: number;
|
|
26
|
+
};
|
|
27
|
+
fitToDrawing?: boolean;
|
|
28
|
+
genes?: GeneModel[];
|
|
29
|
+
spec?: unknown;
|
|
23
30
|
}
|
|
24
31
|
export declare function figureSvg(graph: Graph, layout: LayoutResult, o?: FigureOptions): string;
|