@jbrowse/bandage-core 4.3.0 → 4.4.1
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-IFVLNU7X.js → chunk-JJU4D2SE.js} +251 -21
- package/dist/cli/figure.js +7 -3
- package/dist/index.js +13 -151
- package/dist/types/cli/figure.d.ts +2 -1
- package/dist/types/figure.d.ts +6 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/layout/walkRowDraw.d.ts +6 -1
- package/dist/types/layout/walkStrip.d.ts +7 -3
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -5075,7 +5075,16 @@ function placeRowGenes(rows, byRow) {
|
|
|
5075
5075
|
}
|
|
5076
5076
|
var GENE_FONT_PX = 9;
|
|
5077
5077
|
var GENE_CHAR_PX = 5.2;
|
|
5078
|
-
|
|
5078
|
+
var MIN_LETTERED_BAR_PX = 8;
|
|
5079
|
+
var genePad = (rowPx, barPx) => Math.min(2, (rowPx - barPx) / 2 - 1);
|
|
5080
|
+
function boxesGenes(rowPx, barPx) {
|
|
5081
|
+
return genePad(rowPx, barPx) >= 1;
|
|
5082
|
+
}
|
|
5083
|
+
function rowGeneBoxes(genes, X, y, { rowPx = ROW_HEIGHT_PX, barPx = BAR_PX } = {}) {
|
|
5084
|
+
if (!boxesGenes(rowPx, barPx)) {
|
|
5085
|
+
return [];
|
|
5086
|
+
}
|
|
5087
|
+
const pad = genePad(rowPx, barPx);
|
|
5079
5088
|
const boxes = genes.map((g) => {
|
|
5080
5089
|
const x = X(g.start);
|
|
5081
5090
|
return {
|
|
@@ -5086,7 +5095,7 @@ function rowGeneBoxes(genes, X, y) {
|
|
|
5086
5095
|
});
|
|
5087
5096
|
const taken = [];
|
|
5088
5097
|
const named = new Set(
|
|
5089
|
-
[...boxes].sort((a, b) => a.w - b.w).filter(({ g, x, w }) => {
|
|
5098
|
+
(barPx >= MIN_LETTERED_BAR_PX ? [...boxes] : []).sort((a, b) => a.w - b.w).filter(({ g, x, w }) => {
|
|
5090
5099
|
const half = g.name.length * GENE_CHAR_PX / 2;
|
|
5091
5100
|
const at = [x + w / 2 - half, x + w / 2 + half];
|
|
5092
5101
|
if (2 * half > w + 6 || taken.some(([a, b]) => at[0] < b + 2 && at[1] > a - 2)) {
|
|
@@ -5099,9 +5108,9 @@ function rowGeneBoxes(genes, X, y) {
|
|
|
5099
5108
|
return boxes.map((box) => ({
|
|
5100
5109
|
name: box.g.name,
|
|
5101
5110
|
x: box.x,
|
|
5102
|
-
y: y -
|
|
5111
|
+
y: y - barPx / 2 - pad,
|
|
5103
5112
|
w: box.w,
|
|
5104
|
-
h:
|
|
5113
|
+
h: barPx + 2 * pad,
|
|
5105
5114
|
exons: box.g.exons.map((e) => ({
|
|
5106
5115
|
x: X(e.start),
|
|
5107
5116
|
w: Math.max(1, X(e.end) - X(e.start))
|
|
@@ -5133,7 +5142,13 @@ function walkRowsKey(bars, o = {}) {
|
|
|
5133
5142
|
label: "walked outside the cut"
|
|
5134
5143
|
});
|
|
5135
5144
|
}
|
|
5136
|
-
if (o.genes) {
|
|
5145
|
+
if (o.genes?.crowded) {
|
|
5146
|
+
entries.push({
|
|
5147
|
+
swatch: { kind: "gene" },
|
|
5148
|
+
label: "genes left out: too many rows to box them in",
|
|
5149
|
+
note: true
|
|
5150
|
+
});
|
|
5151
|
+
} else if (o.genes) {
|
|
5137
5152
|
entries.push({
|
|
5138
5153
|
swatch: { kind: "gene" },
|
|
5139
5154
|
label: "genes, each row's own annotation"
|
|
@@ -5260,7 +5275,7 @@ function coalesceRuns(runs, pxPerBp) {
|
|
|
5260
5275
|
flush();
|
|
5261
5276
|
return out;
|
|
5262
5277
|
}
|
|
5263
|
-
function geneTree(box, y) {
|
|
5278
|
+
function geneTree(box, y, barPx) {
|
|
5264
5279
|
return el(
|
|
5265
5280
|
"g",
|
|
5266
5281
|
{ class: "row-gene", "data-testid": "graph-walk-gene" },
|
|
@@ -5268,9 +5283,9 @@ function geneTree(box, y) {
|
|
|
5268
5283
|
...box.exons.map(
|
|
5269
5284
|
(e) => el("rect", {
|
|
5270
5285
|
x: e.x,
|
|
5271
|
-
y: y -
|
|
5286
|
+
y: y - barPx / 2,
|
|
5272
5287
|
width: e.w,
|
|
5273
|
-
height:
|
|
5288
|
+
height: barPx,
|
|
5274
5289
|
fill: GENE_INK,
|
|
5275
5290
|
opacity: 0.35
|
|
5276
5291
|
})
|
|
@@ -5366,9 +5381,10 @@ function walkRowsTree(bars, frame, o = {}) {
|
|
|
5366
5381
|
},
|
|
5367
5382
|
...runs,
|
|
5368
5383
|
...ticks,
|
|
5369
|
-
...rowGeneBoxes(o.rowGenes?.get(row.name) ?? [], along, y
|
|
5370
|
-
|
|
5371
|
-
|
|
5384
|
+
...rowGeneBoxes(o.rowGenes?.get(row.name) ?? [], along, y, {
|
|
5385
|
+
rowPx,
|
|
5386
|
+
barPx
|
|
5387
|
+
}).map((box) => geneTree(box, y, barPx)),
|
|
5372
5388
|
call && el("rect", {
|
|
5373
5389
|
"data-testid": "graph-walk-call",
|
|
5374
5390
|
x: along(call.bp) - 1,
|
|
@@ -5397,6 +5413,183 @@ function walkRowsTree(bars, frame, o = {}) {
|
|
|
5397
5413
|
return el("g", {}, ...[reference, ...rows].map(rowTree));
|
|
5398
5414
|
}
|
|
5399
5415
|
|
|
5416
|
+
// src/layout/walkStrip.ts
|
|
5417
|
+
var PAD_PX = 6;
|
|
5418
|
+
var WALK_STRIP_CEILING_PX = 260;
|
|
5419
|
+
var LABEL_FONT_PX = 11;
|
|
5420
|
+
var MIN_ROW_PX = 3;
|
|
5421
|
+
var LABELLED_ROW_PX = 12;
|
|
5422
|
+
var READOUT_ROOM2 = 1.3;
|
|
5423
|
+
var MARK_INK = "#111";
|
|
5424
|
+
var TICK_PX = 3;
|
|
5425
|
+
function stripMarks(graph, rows, node) {
|
|
5426
|
+
const visits = graph.pathVisits?.get(node.name) ?? [];
|
|
5427
|
+
const marks = [];
|
|
5428
|
+
for (const row of rows) {
|
|
5429
|
+
const axis = row.axis;
|
|
5430
|
+
if (!axis) {
|
|
5431
|
+
continue;
|
|
5432
|
+
}
|
|
5433
|
+
const path = pathOrigin(row.name).name;
|
|
5434
|
+
for (const v of visits) {
|
|
5435
|
+
if (v.path !== path) {
|
|
5436
|
+
continue;
|
|
5437
|
+
}
|
|
5438
|
+
const { start, end } = alongRow(axis, v.start, v.start + node.length);
|
|
5439
|
+
if (end > 0 && start < row.bp) {
|
|
5440
|
+
marks.push({ row: row.name, start, bp: end - start });
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
return marks;
|
|
5445
|
+
}
|
|
5446
|
+
var spanCache = /* @__PURE__ */ new WeakMap();
|
|
5447
|
+
function spansOf(graph, name) {
|
|
5448
|
+
let byName = spanCache.get(graph);
|
|
5449
|
+
if (!byName) {
|
|
5450
|
+
byName = /* @__PURE__ */ new Map();
|
|
5451
|
+
spanCache.set(graph, byName);
|
|
5452
|
+
}
|
|
5453
|
+
if (byName.has(name)) {
|
|
5454
|
+
return byName.get(name);
|
|
5455
|
+
}
|
|
5456
|
+
const lengthOf = new Map(graph.nodes.map((n2) => [n2.id, n2.length]));
|
|
5457
|
+
const pieces = (graph.paths ?? []).filter((p) => p.name === name);
|
|
5458
|
+
let spans;
|
|
5459
|
+
if (pieces.every((p) => p.start !== void 0)) {
|
|
5460
|
+
spans = { ids: [], starts: [], ends: [] };
|
|
5461
|
+
for (const piece of [...pieces].sort((a, b) => a.start - b.start)) {
|
|
5462
|
+
let pos = piece.start;
|
|
5463
|
+
for (const id of piece.nodeIds) {
|
|
5464
|
+
const len = lengthOf.get(id) ?? 0;
|
|
5465
|
+
spans.ids.push(id);
|
|
5466
|
+
spans.starts.push(pos);
|
|
5467
|
+
spans.ends.push(pos + len);
|
|
5468
|
+
pos += len;
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5472
|
+
byName.set(name, spans);
|
|
5473
|
+
return spans;
|
|
5474
|
+
}
|
|
5475
|
+
function segmentAt(graph, row, offset, pxPerBp) {
|
|
5476
|
+
const axis = row.axis;
|
|
5477
|
+
const spans = axis && spansOf(graph, row.name);
|
|
5478
|
+
if (!axis || !spans || offset < 0 || offset > row.bp) {
|
|
5479
|
+
return void 0;
|
|
5480
|
+
}
|
|
5481
|
+
const at = axis.reversed ? axis.start - offset : axis.start + offset;
|
|
5482
|
+
const tol = 0.5 / pxPerBp;
|
|
5483
|
+
const bar = axis.reversed ? { start: axis.start - row.bp, end: axis.start } : { start: axis.start, end: axis.start + row.bp };
|
|
5484
|
+
const from = Math.max(at - tol, bar.start);
|
|
5485
|
+
const to = Math.min(at + tol, bar.end);
|
|
5486
|
+
let lo = 0;
|
|
5487
|
+
let hi = spans.starts.length;
|
|
5488
|
+
while (lo < hi) {
|
|
5489
|
+
const mid = lo + hi >> 1;
|
|
5490
|
+
if (spans.ends[mid] <= from) {
|
|
5491
|
+
lo = mid + 1;
|
|
5492
|
+
} else {
|
|
5493
|
+
hi = mid;
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
let best;
|
|
5497
|
+
let bestLen = -1;
|
|
5498
|
+
let bestHolds = false;
|
|
5499
|
+
for (let i = lo; i < spans.starts.length && spans.starts[i] < to; i++) {
|
|
5500
|
+
const len = spans.ends[i] - spans.starts[i];
|
|
5501
|
+
const holds = spans.starts[i] <= at && at < spans.ends[i];
|
|
5502
|
+
if (len > bestLen || len === bestLen && holds && !bestHolds) {
|
|
5503
|
+
bestLen = len;
|
|
5504
|
+
best = spans.ids[i];
|
|
5505
|
+
bestHolds = holds;
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
return best;
|
|
5509
|
+
}
|
|
5510
|
+
function walkStripFrame(bars, o) {
|
|
5511
|
+
const maxHeight = o.maxHeight ?? WALK_STRIP_CEILING_PX;
|
|
5512
|
+
const labelPx = o.labelPx ?? Math.max(...[bars.reference, ...bars.rows].map((r) => r.label.length)) * LABEL_CHAR_PX + 16;
|
|
5513
|
+
const count = bars.rows.length + 1;
|
|
5514
|
+
const rowPx = Math.max(
|
|
5515
|
+
MIN_ROW_PX,
|
|
5516
|
+
Math.min(ROW_HEIGHT_PX, Math.floor((maxHeight - 2 * PAD_PX) / count))
|
|
5517
|
+
);
|
|
5518
|
+
const labelled = rowPx >= LABELLED_ROW_PX;
|
|
5519
|
+
const readouts = rowPx >= 14;
|
|
5520
|
+
const barPx = Math.max(2, Math.round(rowPx * 0.6));
|
|
5521
|
+
const longest = Math.max(bars.reference.bp, ...bars.rows.map((r) => r.bp), 1);
|
|
5522
|
+
const left = labelled ? labelPx : PAD_PX;
|
|
5523
|
+
const usable = Math.max(1, o.width - left - PAD_PX);
|
|
5524
|
+
const scaleX = usable / (longest * (readouts ? READOUT_ROOM2 : 1));
|
|
5525
|
+
return {
|
|
5526
|
+
scaleX,
|
|
5527
|
+
scaleY: 1,
|
|
5528
|
+
translateX: left - bars.origin * scaleX,
|
|
5529
|
+
translateY: PAD_PX + rowPx / 2,
|
|
5530
|
+
width: o.width,
|
|
5531
|
+
height: count * rowPx + 2 * PAD_PX,
|
|
5532
|
+
rowPx,
|
|
5533
|
+
barPx,
|
|
5534
|
+
readouts,
|
|
5535
|
+
labelled,
|
|
5536
|
+
boxesGenes: boxesGenes(rowPx, barPx)
|
|
5537
|
+
};
|
|
5538
|
+
}
|
|
5539
|
+
function stripRowAt(bars, frame, x, y) {
|
|
5540
|
+
const i = Math.round((y - frame.translateY) / frame.rowPx);
|
|
5541
|
+
const row = [bars.reference, ...bars.rows][i];
|
|
5542
|
+
const offset = (x - frame.translateX) / frame.scaleX - bars.origin;
|
|
5543
|
+
return row && i >= 0 && offset >= 0 && offset <= row.bp ? { index: i, row, offset } : void 0;
|
|
5544
|
+
}
|
|
5545
|
+
function walkMarksTree(bars, frame, marks) {
|
|
5546
|
+
const index = new Map(
|
|
5547
|
+
[bars.reference, ...bars.rows].map((row, i) => [row.name, i])
|
|
5548
|
+
);
|
|
5549
|
+
const X = (offset) => (bars.origin + offset) * frame.scaleX + frame.translateX;
|
|
5550
|
+
return el(
|
|
5551
|
+
"g",
|
|
5552
|
+
{ "data-testid": "graph-walk-marks" },
|
|
5553
|
+
...marks.flatMap((m) => {
|
|
5554
|
+
const i = index.get(m.row);
|
|
5555
|
+
if (i === void 0) {
|
|
5556
|
+
return [];
|
|
5557
|
+
}
|
|
5558
|
+
const y = i * frame.rowPx + frame.translateY;
|
|
5559
|
+
const x = X(m.start);
|
|
5560
|
+
const w = Math.max(2, m.bp * frame.scaleX);
|
|
5561
|
+
const tick = (top) => el("rect", { x, y: top, width: w, height: TICK_PX, fill: MARK_INK });
|
|
5562
|
+
return [
|
|
5563
|
+
tick(y - frame.barPx / 2 - TICK_PX - 1),
|
|
5564
|
+
tick(y + frame.barPx / 2 + 1)
|
|
5565
|
+
];
|
|
5566
|
+
})
|
|
5567
|
+
);
|
|
5568
|
+
}
|
|
5569
|
+
function walkStripLabelsTree(bars, frame, bold = /* @__PURE__ */ new Set()) {
|
|
5570
|
+
return el(
|
|
5571
|
+
"g",
|
|
5572
|
+
{ "font-family": "sans-serif", "font-size": LABEL_FONT_PX, fill: "#333" },
|
|
5573
|
+
...(frame.labelled ? [bars.reference, ...bars.rows] : []).map(
|
|
5574
|
+
(row, i) => el(
|
|
5575
|
+
"text",
|
|
5576
|
+
{
|
|
5577
|
+
x: 6,
|
|
5578
|
+
y: i * frame.rowPx + frame.translateY + 4,
|
|
5579
|
+
"font-weight": bold.has(row.name) ? 600 : void 0
|
|
5580
|
+
},
|
|
5581
|
+
row.label
|
|
5582
|
+
)
|
|
5583
|
+
)
|
|
5584
|
+
);
|
|
5585
|
+
}
|
|
5586
|
+
function stripGeneGaps(frame, gaps, rowGenes) {
|
|
5587
|
+
if (rowGenes ? !rowGenes.size : !gaps) {
|
|
5588
|
+
return void 0;
|
|
5589
|
+
}
|
|
5590
|
+
return frame.boxesGenes ? gaps : { untracked: 0, unread: 0, ...gaps, crowded: true };
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5400
5593
|
// src/renderer/svgCanvas.ts
|
|
5401
5594
|
var n = (v) => +v.toFixed(1);
|
|
5402
5595
|
function paint(css) {
|
|
@@ -5510,7 +5703,7 @@ function svgCanvas(width, height) {
|
|
|
5510
5703
|
}
|
|
5511
5704
|
|
|
5512
5705
|
// src/version.ts
|
|
5513
|
-
var version = "4.
|
|
5706
|
+
var version = "4.4.1";
|
|
5514
5707
|
|
|
5515
5708
|
// src/viewport.ts
|
|
5516
5709
|
function axisScaleOf(scale, pixelRows) {
|
|
@@ -5558,6 +5751,7 @@ var FONT = 'font-family="Helvetica, Arial, sans-serif" font-size="11"';
|
|
|
5558
5751
|
var BAR_PX2 = 48;
|
|
5559
5752
|
var SWATCH_PX = 18;
|
|
5560
5753
|
var KEY_GAP_PX = 16;
|
|
5754
|
+
var STRIP_KEY_PX = 22;
|
|
5561
5755
|
var FADED = "rgb(160,160,160)";
|
|
5562
5756
|
var EXON_COLOR = "#daa520";
|
|
5563
5757
|
var GENE_INK2 = "#1c1c22";
|
|
@@ -5615,6 +5809,10 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
5615
5809
|
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
5616
5810
|
const colorScheme = resolveColorScheme(o.colorScheme ?? "auto", graph);
|
|
5617
5811
|
const referenceRamp = colorScheme === "reference-position" && !layout.tubeMap ? computeReferenceRamp(graph, rampDomain) : void 0;
|
|
5812
|
+
const rampInterval = referenceRamp && {
|
|
5813
|
+
start: referenceRamp.start,
|
|
5814
|
+
end: referenceRamp.start + referenceRamp.span
|
|
5815
|
+
};
|
|
5618
5816
|
const deletions = layout.tubeMap ? [] : deletionEdges(graph);
|
|
5619
5817
|
const layers = o.walks ?? [];
|
|
5620
5818
|
const walkRamp = layers.some((l) => l.color?.field === "reference") ? referenceRamp ?? computeReferenceRamp(graph, rampDomain) : void 0;
|
|
@@ -5681,10 +5879,7 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
5681
5879
|
rows,
|
|
5682
5880
|
{ ...t, width: w, height: h },
|
|
5683
5881
|
{
|
|
5684
|
-
ramp:
|
|
5685
|
-
start: referenceRamp.start,
|
|
5686
|
-
end: referenceRamp.start + referenceRamp.span
|
|
5687
|
-
},
|
|
5882
|
+
ramp: rampInterval,
|
|
5688
5883
|
rowGenes: o.rowGenes
|
|
5689
5884
|
}
|
|
5690
5885
|
)
|
|
@@ -5807,10 +6002,7 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
5807
6002
|
if (rows) {
|
|
5808
6003
|
const key = walkRowsKeyTree(
|
|
5809
6004
|
walkRowsKey(rows, {
|
|
5810
|
-
ramp:
|
|
5811
|
-
start: referenceRamp.start,
|
|
5812
|
-
end: referenceRamp.start + referenceRamp.span
|
|
5813
|
-
},
|
|
6005
|
+
ramp: rampInterval,
|
|
5814
6006
|
genes: o.rowGenes?.size ? o.rowGeneGaps : void 0
|
|
5815
6007
|
}),
|
|
5816
6008
|
x,
|
|
@@ -5846,6 +6038,36 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
5846
6038
|
);
|
|
5847
6039
|
height = header + h;
|
|
5848
6040
|
}
|
|
6041
|
+
const strip = o.walkStrip;
|
|
6042
|
+
if (strip && !rows && !layout.tubeMap) {
|
|
6043
|
+
const frame = walkStripFrame(strip.rows, { width });
|
|
6044
|
+
const key = walkRowsKeyTree(
|
|
6045
|
+
walkRowsKey(strip.rows, {
|
|
6046
|
+
ramp: rampInterval,
|
|
6047
|
+
genes: stripGeneGaps(frame, strip.rowGeneGaps, strip.rowGenes)
|
|
6048
|
+
}),
|
|
6049
|
+
6,
|
|
6050
|
+
height + frame.height
|
|
6051
|
+
);
|
|
6052
|
+
parts.push(
|
|
6053
|
+
`<line x1="0" x2="${width}" y1="${height + 0.5}" y2="${height + 0.5}" stroke="#ddd"/>`,
|
|
6054
|
+
`<svg y="${height}" width="${width}" height="${frame.height}">${serializeEl(
|
|
6055
|
+
walkRowsTree(strip.rows, frame, {
|
|
6056
|
+
ramp: rampInterval,
|
|
6057
|
+
rowGenes: strip.rowGenes,
|
|
6058
|
+
idPrefix: "strip"
|
|
6059
|
+
})
|
|
6060
|
+
)}${serializeEl(
|
|
6061
|
+
walkStripLabelsTree(
|
|
6062
|
+
strip.rows,
|
|
6063
|
+
frame,
|
|
6064
|
+
new Set(layers.map((l) => l.walk))
|
|
6065
|
+
)
|
|
6066
|
+
)}</svg>`,
|
|
6067
|
+
serializeEl(key.tree)
|
|
6068
|
+
);
|
|
6069
|
+
height += frame.height + STRIP_KEY_PX;
|
|
6070
|
+
}
|
|
5849
6071
|
const metadata = JSON.stringify({
|
|
5850
6072
|
generator: `@jbrowse/bandage-core@${version}`,
|
|
5851
6073
|
...o.spec === void 0 ? {} : { spec: o.spec }
|
|
@@ -6456,7 +6678,6 @@ export {
|
|
|
6456
6678
|
panSNSample,
|
|
6457
6679
|
panSNHaplotype,
|
|
6458
6680
|
panSNContig,
|
|
6459
|
-
pathOrigin,
|
|
6460
6681
|
trimOrigins,
|
|
6461
6682
|
loadGraph,
|
|
6462
6683
|
engineRequest,
|
|
@@ -6543,6 +6764,15 @@ export {
|
|
|
6543
6764
|
walkRowsKeyTree,
|
|
6544
6765
|
coalesceRuns,
|
|
6545
6766
|
walkRowsTree,
|
|
6767
|
+
WALK_STRIP_CEILING_PX,
|
|
6768
|
+
LABELLED_ROW_PX,
|
|
6769
|
+
stripMarks,
|
|
6770
|
+
segmentAt,
|
|
6771
|
+
walkStripFrame,
|
|
6772
|
+
stripRowAt,
|
|
6773
|
+
walkMarksTree,
|
|
6774
|
+
walkStripLabelsTree,
|
|
6775
|
+
stripGeneGaps,
|
|
6546
6776
|
svgCanvas,
|
|
6547
6777
|
version,
|
|
6548
6778
|
axisScaleOf,
|
package/dist/cli/figure.js
CHANGED
|
@@ -16,8 +16,9 @@ import {
|
|
|
16
16
|
loadGraph,
|
|
17
17
|
openGbz,
|
|
18
18
|
parseRegion,
|
|
19
|
-
refNameBinding
|
|
20
|
-
|
|
19
|
+
refNameBinding,
|
|
20
|
+
walkRows
|
|
21
|
+
} from "../chunks/chunk-JJU4D2SE.js";
|
|
21
22
|
|
|
22
23
|
// src/cli/figure.ts
|
|
23
24
|
import { access, open, readFile, writeFile } from "node:fs/promises";
|
|
@@ -146,7 +147,8 @@ async function renderSpec(spec, base) {
|
|
|
146
147
|
byteSource(resolve(db)),
|
|
147
148
|
indexLocation ? byteSource(indexLocation) : void 0
|
|
148
149
|
),
|
|
149
|
-
|
|
150
|
+
// the strip measures whole walks, which a cut only follows when asked
|
|
151
|
+
spec.walkStrip ? { snarls: "overlapping", ...spec.gbz } : spec.gbz
|
|
150
152
|
);
|
|
151
153
|
source = { ...cut, name: `${cut.sample} ${spec.gbz.region}` };
|
|
152
154
|
} else if (spec.gfa) {
|
|
@@ -170,8 +172,10 @@ async function renderSpec(spec, base) {
|
|
|
170
172
|
},
|
|
171
173
|
engine
|
|
172
174
|
)).result;
|
|
175
|
+
const stripRows = spec.walkStrip ? walkRows(graph, source.region) : void 0;
|
|
173
176
|
return figureSvg(graph, layout, {
|
|
174
177
|
...spec,
|
|
178
|
+
walkStrip: stripRows && { rows: stripRows },
|
|
175
179
|
walks: spec.walks?.map((w) => typeof w === "string" ? { walk: w } : w),
|
|
176
180
|
region: source.region,
|
|
177
181
|
genes: spec.genes ? await readGenes(spec.genes, graph, resolve) : void 0,
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
GENE_INK,
|
|
14
14
|
HALO_FACTOR,
|
|
15
15
|
HPRC_GBZ,
|
|
16
|
+
LABELLED_ROW_PX,
|
|
16
17
|
LABEL_CHAR_PX,
|
|
17
18
|
LABEL_PAD,
|
|
18
19
|
LABEL_PX,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
ROW_HEIGHT_PX,
|
|
29
30
|
WALK_FIELDS,
|
|
30
31
|
WALK_SCHEMES,
|
|
32
|
+
WALK_STRIP_CEILING_PX,
|
|
31
33
|
WELL_KNOWN_SAMPLES,
|
|
32
34
|
alongRow,
|
|
33
35
|
assemblyWalk,
|
|
@@ -87,7 +89,6 @@ import {
|
|
|
87
89
|
parseRegion,
|
|
88
90
|
pathColorsLegible,
|
|
89
91
|
pathLegend,
|
|
90
|
-
pathOrigin,
|
|
91
92
|
pathRibbonOffsets,
|
|
92
93
|
placeRowGenes,
|
|
93
94
|
rampHue,
|
|
@@ -107,7 +108,11 @@ import {
|
|
|
107
108
|
runPaint,
|
|
108
109
|
schemeCss,
|
|
109
110
|
screenToLayout,
|
|
111
|
+
segmentAt,
|
|
110
112
|
serializeEl,
|
|
113
|
+
stripGeneGaps,
|
|
114
|
+
stripMarks,
|
|
115
|
+
stripRowAt,
|
|
111
116
|
svgCanvas,
|
|
112
117
|
svgPath,
|
|
113
118
|
translateCurves,
|
|
@@ -118,6 +123,7 @@ import {
|
|
|
118
123
|
walkHighlight,
|
|
119
124
|
walkKey,
|
|
120
125
|
walkLift,
|
|
126
|
+
walkMarksTree,
|
|
121
127
|
walkPosition,
|
|
122
128
|
walkRowReadout,
|
|
123
129
|
walkRows,
|
|
@@ -125,10 +131,12 @@ import {
|
|
|
125
131
|
walkRowsKey,
|
|
126
132
|
walkRowsKeyTree,
|
|
127
133
|
walkRowsTree,
|
|
134
|
+
walkStripFrame,
|
|
135
|
+
walkStripLabelsTree,
|
|
128
136
|
wellKnownSample,
|
|
129
137
|
yToXOf,
|
|
130
138
|
zoomAbout
|
|
131
|
-
} from "./chunks/chunk-
|
|
139
|
+
} from "./chunks/chunk-JJU4D2SE.js";
|
|
132
140
|
|
|
133
141
|
// src/index.ts
|
|
134
142
|
import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
|
|
@@ -1776,155 +1784,6 @@ function bubbleSubgraph(graph, segmentIds) {
|
|
|
1776
1784
|
referencePath: graph.referencePath
|
|
1777
1785
|
};
|
|
1778
1786
|
}
|
|
1779
|
-
|
|
1780
|
-
// src/layout/walkStrip.ts
|
|
1781
|
-
var PAD_PX = 6;
|
|
1782
|
-
var MIN_ROW_PX = 3;
|
|
1783
|
-
var LABELLED_ROW_PX = 12;
|
|
1784
|
-
var READOUT_ROOM = 1.3;
|
|
1785
|
-
var MARK_INK = "#111";
|
|
1786
|
-
var TICK_PX2 = 3;
|
|
1787
|
-
function stripMarks(graph, rows, node) {
|
|
1788
|
-
const visits = graph.pathVisits?.get(node.name) ?? [];
|
|
1789
|
-
const marks = [];
|
|
1790
|
-
for (const row of rows) {
|
|
1791
|
-
const axis = row.axis;
|
|
1792
|
-
if (!axis) {
|
|
1793
|
-
continue;
|
|
1794
|
-
}
|
|
1795
|
-
const path = pathOrigin(row.name).name;
|
|
1796
|
-
for (const v of visits) {
|
|
1797
|
-
if (v.path !== path) {
|
|
1798
|
-
continue;
|
|
1799
|
-
}
|
|
1800
|
-
const { start, end } = alongRow(axis, v.start, v.start + node.length);
|
|
1801
|
-
if (end > 0 && start < row.bp) {
|
|
1802
|
-
marks.push({ row: row.name, start, bp: end - start });
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
return marks;
|
|
1807
|
-
}
|
|
1808
|
-
var spanCache = /* @__PURE__ */ new WeakMap();
|
|
1809
|
-
function spansOf(graph, name) {
|
|
1810
|
-
let byName = spanCache.get(graph);
|
|
1811
|
-
if (!byName) {
|
|
1812
|
-
byName = /* @__PURE__ */ new Map();
|
|
1813
|
-
spanCache.set(graph, byName);
|
|
1814
|
-
}
|
|
1815
|
-
if (byName.has(name)) {
|
|
1816
|
-
return byName.get(name);
|
|
1817
|
-
}
|
|
1818
|
-
const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
|
|
1819
|
-
const pieces = (graph.paths ?? []).filter((p) => p.name === name);
|
|
1820
|
-
let spans;
|
|
1821
|
-
if (pieces.every((p) => p.start !== void 0)) {
|
|
1822
|
-
spans = { ids: [], starts: [], ends: [] };
|
|
1823
|
-
for (const piece of [...pieces].sort((a, b) => a.start - b.start)) {
|
|
1824
|
-
let pos = piece.start;
|
|
1825
|
-
for (const id of piece.nodeIds) {
|
|
1826
|
-
const len = lengthOf.get(id) ?? 0;
|
|
1827
|
-
spans.ids.push(id);
|
|
1828
|
-
spans.starts.push(pos);
|
|
1829
|
-
spans.ends.push(pos + len);
|
|
1830
|
-
pos += len;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
byName.set(name, spans);
|
|
1835
|
-
return spans;
|
|
1836
|
-
}
|
|
1837
|
-
function segmentAt(graph, row, offset, pxPerBp) {
|
|
1838
|
-
const axis = row.axis;
|
|
1839
|
-
const spans = axis && spansOf(graph, row.name);
|
|
1840
|
-
if (!axis || !spans || offset < 0 || offset > row.bp) {
|
|
1841
|
-
return void 0;
|
|
1842
|
-
}
|
|
1843
|
-
const at = axis.reversed ? axis.start - offset : axis.start + offset;
|
|
1844
|
-
const tol = 0.5 / pxPerBp;
|
|
1845
|
-
const bar = axis.reversed ? { start: axis.start - row.bp, end: axis.start } : { start: axis.start, end: axis.start + row.bp };
|
|
1846
|
-
const from = Math.max(at - tol, bar.start);
|
|
1847
|
-
const to = Math.min(at + tol, bar.end);
|
|
1848
|
-
let lo = 0;
|
|
1849
|
-
let hi = spans.starts.length;
|
|
1850
|
-
while (lo < hi) {
|
|
1851
|
-
const mid = lo + hi >> 1;
|
|
1852
|
-
if (spans.ends[mid] <= from) {
|
|
1853
|
-
lo = mid + 1;
|
|
1854
|
-
} else {
|
|
1855
|
-
hi = mid;
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
let best;
|
|
1859
|
-
let bestLen = -1;
|
|
1860
|
-
let bestHolds = false;
|
|
1861
|
-
for (let i = lo; i < spans.starts.length && spans.starts[i] < to; i++) {
|
|
1862
|
-
const len = spans.ends[i] - spans.starts[i];
|
|
1863
|
-
const holds = spans.starts[i] <= at && at < spans.ends[i];
|
|
1864
|
-
if (len > bestLen || len === bestLen && holds && !bestHolds) {
|
|
1865
|
-
bestLen = len;
|
|
1866
|
-
best = spans.ids[i];
|
|
1867
|
-
bestHolds = holds;
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
return best;
|
|
1871
|
-
}
|
|
1872
|
-
function walkStripFrame(bars, o) {
|
|
1873
|
-
const count = bars.rows.length + 1;
|
|
1874
|
-
const rowPx = Math.max(
|
|
1875
|
-
MIN_ROW_PX,
|
|
1876
|
-
Math.min(ROW_HEIGHT_PX, Math.floor((o.maxHeight - 2 * PAD_PX) / count))
|
|
1877
|
-
);
|
|
1878
|
-
const labelled = rowPx >= LABELLED_ROW_PX;
|
|
1879
|
-
const readouts = rowPx >= 14;
|
|
1880
|
-
const barPx = Math.max(2, Math.round(rowPx * 0.6));
|
|
1881
|
-
const longest = Math.max(bars.reference.bp, ...bars.rows.map((r) => r.bp), 1);
|
|
1882
|
-
const left = labelled ? o.labelPx : PAD_PX;
|
|
1883
|
-
const usable = Math.max(1, o.width - left - PAD_PX);
|
|
1884
|
-
const scaleX = usable / (longest * (readouts ? READOUT_ROOM : 1));
|
|
1885
|
-
return {
|
|
1886
|
-
scaleX,
|
|
1887
|
-
scaleY: 1,
|
|
1888
|
-
translateX: left - bars.origin * scaleX,
|
|
1889
|
-
translateY: PAD_PX + rowPx / 2,
|
|
1890
|
-
width: o.width,
|
|
1891
|
-
height: count * rowPx + 2 * PAD_PX,
|
|
1892
|
-
rowPx,
|
|
1893
|
-
barPx,
|
|
1894
|
-
readouts,
|
|
1895
|
-
labelled
|
|
1896
|
-
};
|
|
1897
|
-
}
|
|
1898
|
-
function stripRowAt(bars, frame, x, y) {
|
|
1899
|
-
const i = Math.round((y - frame.translateY) / frame.rowPx);
|
|
1900
|
-
const row = [bars.reference, ...bars.rows][i];
|
|
1901
|
-
const offset = (x - frame.translateX) / frame.scaleX - bars.origin;
|
|
1902
|
-
return row && i >= 0 && offset >= 0 && offset <= row.bp ? { index: i, row, offset } : void 0;
|
|
1903
|
-
}
|
|
1904
|
-
function walkMarksTree(bars, frame, marks) {
|
|
1905
|
-
const index = new Map(
|
|
1906
|
-
[bars.reference, ...bars.rows].map((row, i) => [row.name, i])
|
|
1907
|
-
);
|
|
1908
|
-
const X = (offset) => (bars.origin + offset) * frame.scaleX + frame.translateX;
|
|
1909
|
-
return el(
|
|
1910
|
-
"g",
|
|
1911
|
-
{ "data-testid": "graph-walk-marks" },
|
|
1912
|
-
...marks.flatMap((m) => {
|
|
1913
|
-
const i = index.get(m.row);
|
|
1914
|
-
if (i === void 0) {
|
|
1915
|
-
return [];
|
|
1916
|
-
}
|
|
1917
|
-
const y = i * frame.rowPx + frame.translateY;
|
|
1918
|
-
const x = X(m.start);
|
|
1919
|
-
const w = Math.max(2, m.bp * frame.scaleX);
|
|
1920
|
-
const tick = (top) => el("rect", { x, y: top, width: w, height: TICK_PX2, fill: MARK_INK });
|
|
1921
|
-
return [
|
|
1922
|
-
tick(y - frame.barPx / 2 - TICK_PX2 - 1),
|
|
1923
|
-
tick(y + frame.barPx / 2 + 1)
|
|
1924
|
-
];
|
|
1925
|
-
})
|
|
1926
|
-
);
|
|
1927
|
-
}
|
|
1928
1787
|
export {
|
|
1929
1788
|
BUBBLE_KIND_COLORS,
|
|
1930
1789
|
BUBBLE_KIND_NAMES,
|
|
@@ -1958,6 +1817,7 @@ export {
|
|
|
1958
1817
|
ON_REFERENCE as WALK_ON_REFERENCE,
|
|
1959
1818
|
OUTSIDE_CUT as WALK_OUTSIDE_CUT,
|
|
1960
1819
|
WALK_SCHEMES,
|
|
1820
|
+
WALK_STRIP_CEILING_PX,
|
|
1961
1821
|
WELL_KNOWN_SAMPLES,
|
|
1962
1822
|
alongRow,
|
|
1963
1823
|
assemblyWalk,
|
|
@@ -2051,6 +1911,7 @@ export {
|
|
|
2051
1911
|
segmentAt,
|
|
2052
1912
|
serializeEl,
|
|
2053
1913
|
stripBlockAt,
|
|
1914
|
+
stripGeneGaps,
|
|
2054
1915
|
stripMarks,
|
|
2055
1916
|
stripRowAt,
|
|
2056
1917
|
svgCanvas,
|
|
@@ -2076,6 +1937,7 @@ export {
|
|
|
2076
1937
|
walkRowsKeyTree,
|
|
2077
1938
|
walkRowsTree,
|
|
2078
1939
|
walkStripFrame,
|
|
1940
|
+
walkStripLabelsTree,
|
|
2079
1941
|
warpX,
|
|
2080
1942
|
wellKnownSample,
|
|
2081
1943
|
wheelZoomFactor,
|
|
@@ -3,7 +3,7 @@ import type { FigureOptions } from '../figure.js';
|
|
|
3
3
|
import type { GbzSource } from '../gbzCut.js';
|
|
4
4
|
import type { LayoutModeValue } from '../layoutModes.js';
|
|
5
5
|
import type { WalkLayer } from '../walkEncoding.js';
|
|
6
|
-
export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region' | 'genes' | 'spec'> {
|
|
6
|
+
export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region' | 'genes' | 'spec' | 'walkStrip'> {
|
|
7
7
|
gfa?: string;
|
|
8
8
|
gbz?: GbzSource;
|
|
9
9
|
region?: string;
|
|
@@ -12,6 +12,7 @@ export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region' | 'ge
|
|
|
12
12
|
quality?: number;
|
|
13
13
|
bubbleSpread?: BubbleSpread;
|
|
14
14
|
walks?: (string | WalkLayer)[];
|
|
15
|
+
walkStrip?: boolean;
|
|
15
16
|
genes?: {
|
|
16
17
|
file: string;
|
|
17
18
|
index?: string;
|
package/dist/types/figure.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ColorScheme } from './colorSchemes.js';
|
|
|
2
2
|
import type { FacetInput } from './facetGrid.js';
|
|
3
3
|
import type { GeneModel } from './genes/genePins.js';
|
|
4
4
|
import type { GeneGaps, RowGene, WalkRowsWithCalls } from './layout/walkRowDraw.js';
|
|
5
|
+
import type { WalkRows } from './layout/walkRows.js';
|
|
5
6
|
import type { NodeWidth } from './nodeWidths.js';
|
|
6
7
|
import type { Graph, LayoutResult } from './types.js';
|
|
7
8
|
import type { WalkLayer } from './walkEncoding.js';
|
|
@@ -30,5 +31,10 @@ export interface FigureOptions {
|
|
|
30
31
|
walkRows?: WalkRowsWithCalls;
|
|
31
32
|
rowGenes?: Map<string, RowGene[]>;
|
|
32
33
|
rowGeneGaps?: GeneGaps;
|
|
34
|
+
walkStrip?: {
|
|
35
|
+
rows: WalkRows;
|
|
36
|
+
rowGenes?: Map<string, RowGene[]>;
|
|
37
|
+
rowGeneGaps?: GeneGaps;
|
|
38
|
+
};
|
|
33
39
|
}
|
|
34
40
|
export declare function figureSvg(graph: Graph, layout: LayoutResult, o?: FigureOptions): string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export { walkRows } from './layout/walkRows.js';
|
|
|
58
58
|
export type { WalkAxis, WalkRow, WalkRows, WalkRun } from './layout/walkRows.js';
|
|
59
59
|
export { BAR_PX as WALK_BAR_PX, DISAGREES as WALK_DISAGREES, GAP_PX as WALK_GAP_PX, GENE_INK as WALK_GENE_INK, OFF_REFERENCE as WALK_OFF_REFERENCE, ON_REFERENCE as WALK_ON_REFERENCE, OUTSIDE_CUT as WALK_OUTSIDE_CUT, alongRow, coalesceRuns, placeRowGenes, readoutPlacement, rowGeneBoxes, rowSpan, runPaint, unitTicks, walkRowReadout, walkRowsKey, walkRowsKeyTree, walkRowsTree, } from './layout/walkRowDraw.js';
|
|
60
60
|
export type { GeneBox, GeneGaps, KeyEntry, KeySwatch, ReadoutCall, RowGene, WalkRowsFrame, WalkRowsWithCalls, } from './layout/walkRowDraw.js';
|
|
61
|
-
export { LABELLED_ROW_PX, segmentAt, stripMarks, stripRowAt, walkMarksTree, walkStripFrame, } from './layout/walkStrip.js';
|
|
61
|
+
export { LABELLED_ROW_PX, WALK_STRIP_CEILING_PX, segmentAt, stripGeneGaps, stripMarks, stripRowAt, walkMarksTree, walkStripFrame, walkStripLabelsTree, } from './layout/walkStrip.js';
|
|
62
62
|
export type { StripFrame, StripMark } from './layout/walkStrip.js';
|
|
63
63
|
export { el, serializeEl } from './el.js';
|
|
64
64
|
export type { El, ElAttrs } from './el.js';
|
|
@@ -50,6 +50,7 @@ export interface RowGene {
|
|
|
50
50
|
}[];
|
|
51
51
|
}
|
|
52
52
|
export declare function placeRowGenes(rows: WalkRow[], byRow: Map<string, GeneModel[]>): Map<string, RowGene[]>;
|
|
53
|
+
export declare function boxesGenes(rowPx: number, barPx: number): boolean;
|
|
53
54
|
export interface GeneBox {
|
|
54
55
|
name: string;
|
|
55
56
|
x: number;
|
|
@@ -66,7 +67,10 @@ export interface GeneBox {
|
|
|
66
67
|
size: number;
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
|
-
export declare function rowGeneBoxes(genes: RowGene[], X: (offset: number) => number, y: number
|
|
70
|
+
export declare function rowGeneBoxes(genes: RowGene[], X: (offset: number) => number, y: number, { rowPx, barPx }?: {
|
|
71
|
+
rowPx?: number | undefined;
|
|
72
|
+
barPx?: number | undefined;
|
|
73
|
+
}): GeneBox[];
|
|
70
74
|
export type KeySwatch = {
|
|
71
75
|
kind: 'bar';
|
|
72
76
|
fill: string;
|
|
@@ -82,6 +86,7 @@ export interface KeyEntry {
|
|
|
82
86
|
note?: true;
|
|
83
87
|
}
|
|
84
88
|
export interface GeneGaps {
|
|
89
|
+
crowded?: boolean;
|
|
85
90
|
untracked: number;
|
|
86
91
|
unplaced?: number;
|
|
87
92
|
unread: number;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { WalkRowsFrame } from './walkRowDraw.js';
|
|
1
|
+
import type { GeneGaps, RowGene, WalkRowsFrame } from './walkRowDraw.js';
|
|
2
2
|
import type { WalkRow, WalkRows } from './walkRows.js';
|
|
3
3
|
import type { Graph, GraphNode } from '../types.js';
|
|
4
|
+
export declare const WALK_STRIP_CEILING_PX = 260;
|
|
4
5
|
export declare const LABELLED_ROW_PX = 12;
|
|
5
6
|
export interface StripMark {
|
|
6
7
|
row: string;
|
|
@@ -14,11 +15,12 @@ export interface StripFrame extends WalkRowsFrame {
|
|
|
14
15
|
barPx: number;
|
|
15
16
|
readouts: boolean;
|
|
16
17
|
labelled: boolean;
|
|
18
|
+
boxesGenes: boolean;
|
|
17
19
|
}
|
|
18
20
|
export declare function walkStripFrame(bars: WalkRows, o: {
|
|
19
21
|
width: number;
|
|
20
|
-
maxHeight
|
|
21
|
-
labelPx
|
|
22
|
+
maxHeight?: number;
|
|
23
|
+
labelPx?: number;
|
|
22
24
|
}): StripFrame;
|
|
23
25
|
export declare function stripRowAt(bars: WalkRows, frame: StripFrame, x: number, y: number): {
|
|
24
26
|
index: number;
|
|
@@ -26,3 +28,5 @@ export declare function stripRowAt(bars: WalkRows, frame: StripFrame, x: number,
|
|
|
26
28
|
offset: number;
|
|
27
29
|
} | undefined;
|
|
28
30
|
export declare function walkMarksTree(bars: WalkRows, frame: StripFrame, marks: StripMark[]): import("../index.js").El;
|
|
31
|
+
export declare function walkStripLabelsTree(bars: WalkRows, frame: StripFrame, bold?: ReadonlySet<string>): import("../index.js").El;
|
|
32
|
+
export declare function stripGeneGaps(frame: StripFrame, gaps: GeneGaps | undefined, rowGenes?: Map<string, RowGene[]>): GeneGaps | undefined;
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "4.
|
|
1
|
+
export declare const version = "4.4.1";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/bandage-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.1",
|
|
4
4
|
"description": "Pangenome graph layout and drawing with no host: GFA in, the Bandage force-directed engine and reference-anchored layouts, a Canvas2D renderer, hit testing and labels out",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|