@jbrowse/bandage-core 4.0.29 → 4.1.0
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-QN464POI.js → chunk-WAW4SYMY.js} +604 -95
- package/dist/cli/figure.js +1 -8
- package/dist/index.js +51 -1
- package/dist/types/el.d.ts +8 -0
- package/dist/types/figure.d.ts +4 -0
- package/dist/types/gbzCut.d.ts +2 -2
- package/dist/types/gbzWindow.d.ts +6 -10
- package/dist/types/genes/geneFiles.d.ts +1 -0
- package/dist/types/index.d.ts +7 -2
- package/dist/types/layout/walkRowDraw.d.ts +125 -0
- package/dist/types/layout/walkRows.d.ts +8 -0
- package/dist/types/layoutModes.d.ts +2 -2
- package/dist/types/referenceRampCss.d.ts +14 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types/walkKey.d.ts +1 -0
- package/package.json +2 -2
|
@@ -1946,16 +1946,52 @@ function sampleOf(path) {
|
|
|
1946
1946
|
function labelOf(path) {
|
|
1947
1947
|
return path.haplotype !== void 0 && path.sample !== void 0 ? `${path.sample}#${path.haplotype}` : sampleOf(path);
|
|
1948
1948
|
}
|
|
1949
|
-
function
|
|
1949
|
+
function stepsOf(pieces, lengthOf) {
|
|
1950
|
+
const steps = [];
|
|
1951
|
+
let end;
|
|
1952
|
+
for (const piece of pieces) {
|
|
1953
|
+
if (end !== void 0 && piece.start !== void 0 && piece.start > end) {
|
|
1954
|
+
steps.push(piece.start - end);
|
|
1955
|
+
}
|
|
1956
|
+
steps.push(...piece.nodeIds);
|
|
1957
|
+
const bp = piece.nodeIds.reduce(
|
|
1958
|
+
(sum, id) => sum + (lengthOf.get(id) ?? 0),
|
|
1959
|
+
0
|
|
1960
|
+
);
|
|
1961
|
+
end = piece.start === void 0 ? void 0 : piece.start + bp;
|
|
1962
|
+
}
|
|
1963
|
+
return steps;
|
|
1964
|
+
}
|
|
1965
|
+
function stepSpans(pieces, lengthOf) {
|
|
1966
|
+
const spans = [];
|
|
1967
|
+
let end;
|
|
1968
|
+
for (const piece of pieces) {
|
|
1969
|
+
if (piece.start === void 0 || end !== void 0 && piece.start < end) {
|
|
1970
|
+
return void 0;
|
|
1971
|
+
}
|
|
1972
|
+
if (end !== void 0 && piece.start > end) {
|
|
1973
|
+
spans.push({ start: end, end: piece.start });
|
|
1974
|
+
}
|
|
1975
|
+
let pos = piece.start;
|
|
1976
|
+
for (const id of piece.nodeIds) {
|
|
1977
|
+
const len = lengthOf.get(id) ?? 0;
|
|
1978
|
+
spans.push({ start: pos, end: pos + len });
|
|
1979
|
+
pos += len;
|
|
1980
|
+
}
|
|
1981
|
+
end = pos;
|
|
1982
|
+
}
|
|
1983
|
+
return spans;
|
|
1984
|
+
}
|
|
1985
|
+
function sliceBetween(steps, span, region, flanked = true) {
|
|
1950
1986
|
if (!region || !flanked) {
|
|
1951
|
-
return { ids:
|
|
1987
|
+
return { ids: steps, complete: true, from: -1, to: -1 };
|
|
1952
1988
|
}
|
|
1953
1989
|
let i0 = -1;
|
|
1954
1990
|
let i1 = -1;
|
|
1955
1991
|
let bestEnd = -Infinity;
|
|
1956
1992
|
let bestStart = Infinity;
|
|
1957
|
-
|
|
1958
|
-
const s = span.get(id);
|
|
1993
|
+
steps.forEach((id, i) => {
|
|
1994
|
+
const s = typeof id === "string" ? span.get(id) : void 0;
|
|
1959
1995
|
if (s) {
|
|
1960
1996
|
if (s.end <= region.start && s.end > bestEnd) {
|
|
1961
1997
|
bestEnd = s.end;
|
|
@@ -1968,25 +2004,34 @@ function sliceBetween(nodeIds, span, region, flanked = true) {
|
|
|
1968
2004
|
}
|
|
1969
2005
|
});
|
|
1970
2006
|
if (i0 < 0 || i1 < 0) {
|
|
1971
|
-
return { ids:
|
|
2007
|
+
return { ids: steps, complete: false, from: -1, to: -1 };
|
|
1972
2008
|
}
|
|
1973
|
-
const ids =
|
|
1974
|
-
return {
|
|
2009
|
+
const ids = steps.slice(Math.min(i0, i1) + 1, Math.max(i0, i1));
|
|
2010
|
+
return {
|
|
2011
|
+
ids: i0 < i1 ? ids : ids.reverse(),
|
|
2012
|
+
complete: true,
|
|
2013
|
+
from: i0,
|
|
2014
|
+
to: i1
|
|
2015
|
+
};
|
|
1975
2016
|
}
|
|
1976
2017
|
function walkRows(graph, region, unit) {
|
|
1977
2018
|
const paths = graph.paths ?? [];
|
|
1978
2019
|
const isReference2 = (p) => pathOrigin(p.name).name === graph.referencePath;
|
|
1979
|
-
const
|
|
1980
|
-
const first =
|
|
1981
|
-
const others = paths.filter((p) => !
|
|
2020
|
+
const referencePieces = paths.some(isReference2) ? paths.filter(isReference2).sort((a, b) => (a.start ?? 0) - (b.start ?? 0)) : paths.slice(0, 1);
|
|
2021
|
+
const first = referencePieces[0];
|
|
2022
|
+
const others = paths.filter((p) => !referencePieces.includes(p));
|
|
1982
2023
|
if (!first || others.length === 0) {
|
|
1983
2024
|
return void 0;
|
|
1984
2025
|
}
|
|
2026
|
+
const walks = /* @__PURE__ */ new Map();
|
|
2027
|
+
for (const path of others) {
|
|
2028
|
+
walks.set(path.name, [...walks.get(path.name) ?? [], path]);
|
|
2029
|
+
}
|
|
1985
2030
|
const lengthOf = new Map(graph.nodes.map((n2) => [n2.id, n2.length]));
|
|
1986
2031
|
const referenceStart = first.start ?? 0;
|
|
1987
2032
|
const cut = region && region.end > region.start ? region : void 0;
|
|
1988
2033
|
const span = /* @__PURE__ */ new Map();
|
|
1989
|
-
for (const piece of
|
|
2034
|
+
for (const piece of referencePieces) {
|
|
1990
2035
|
let pos = piece.start ?? 0;
|
|
1991
2036
|
for (const id of piece.nodeIds) {
|
|
1992
2037
|
const len = lengthOf.get(id) ?? 0;
|
|
@@ -1997,20 +2042,38 @@ function walkRows(graph, region, unit) {
|
|
|
1997
2042
|
}
|
|
1998
2043
|
}
|
|
1999
2044
|
const flanked = cut === void 0 || [...span.values()].some((s) => s.end <= cut.start) && [...span.values()].some((s) => s.start >= cut.end);
|
|
2000
|
-
const rowOf = (
|
|
2001
|
-
const
|
|
2045
|
+
const rowOf = (pieces) => {
|
|
2046
|
+
const path = pieces[0];
|
|
2047
|
+
const ordered = [...pieces].sort((a, b) => (a.start ?? 0) - (b.start ?? 0));
|
|
2048
|
+
const { ids, complete, from, to } = sliceBetween(
|
|
2049
|
+
stepsOf(ordered, lengthOf),
|
|
2050
|
+
span,
|
|
2051
|
+
cut,
|
|
2052
|
+
flanked
|
|
2053
|
+
);
|
|
2054
|
+
const spans = stepSpans(ordered, lengthOf);
|
|
2055
|
+
const contig = path.contig ?? panSNContig(pathOrigin(path.name).name);
|
|
2056
|
+
const axis = !spans?.length ? void 0 : from < 0 ? { contig, start: spans[0].start, reversed: false } : from < to ? { contig, start: spans[from].end, reversed: false } : { contig, start: spans[from].start, reversed: true };
|
|
2002
2057
|
const runs = [];
|
|
2003
2058
|
let bp = 0;
|
|
2004
2059
|
let offReferenceBp = 0;
|
|
2060
|
+
let gapBp = 0;
|
|
2005
2061
|
let step = 0;
|
|
2006
2062
|
for (const id of ids) {
|
|
2063
|
+
if (typeof id === "number") {
|
|
2064
|
+
runs.push({ start: bp, bp: id, onReference: false, gap: true });
|
|
2065
|
+
bp += id;
|
|
2066
|
+
gapBp += id;
|
|
2067
|
+
step = 0;
|
|
2068
|
+
continue;
|
|
2069
|
+
}
|
|
2007
2070
|
const len = lengthOf.get(id) ?? 0;
|
|
2008
2071
|
const s = span.get(id);
|
|
2009
2072
|
const last = runs.at(-1);
|
|
2010
2073
|
const at = last?.referenceStart;
|
|
2011
2074
|
const forward = s && at !== void 0 && step >= 0 && at + last.bp === s.start;
|
|
2012
2075
|
const backward = s && at !== void 0 && step <= 0 && s.end === at;
|
|
2013
|
-
if (!s && last && !last.onReference) {
|
|
2076
|
+
if (!s && last && !last.onReference && !last.gap) {
|
|
2014
2077
|
last.bp += len;
|
|
2015
2078
|
} else if (forward || backward) {
|
|
2016
2079
|
last.bp += len;
|
|
@@ -2040,19 +2103,18 @@ function walkRows(graph, region, unit) {
|
|
|
2040
2103
|
haplotype: path.haplotype,
|
|
2041
2104
|
bp,
|
|
2042
2105
|
offReferenceBp,
|
|
2106
|
+
gapBp,
|
|
2043
2107
|
complete,
|
|
2044
|
-
runs
|
|
2108
|
+
runs,
|
|
2109
|
+
axis
|
|
2045
2110
|
};
|
|
2046
2111
|
};
|
|
2047
2112
|
const origin = cut ? cut.start : referenceStart;
|
|
2048
2113
|
return {
|
|
2049
2114
|
origin,
|
|
2050
2115
|
unit,
|
|
2051
|
-
reference: rowOf(
|
|
2052
|
-
|
|
2053
|
-
nodeIds: referencePieces2.flatMap((piece) => piece.nodeIds)
|
|
2054
|
-
}),
|
|
2055
|
-
rows: others.map(rowOf).sort(
|
|
2116
|
+
reference: rowOf(referencePieces),
|
|
2117
|
+
rows: [...walks.values()].map(rowOf).sort(
|
|
2056
2118
|
(a, b) => Number(b.complete) - Number(a.complete) || b.bp - a.bp || a.label.localeCompare(b.label)
|
|
2057
2119
|
)
|
|
2058
2120
|
};
|
|
@@ -2407,7 +2469,7 @@ function placeByWalks(graph, backbone) {
|
|
|
2407
2469
|
return placed;
|
|
2408
2470
|
}
|
|
2409
2471
|
function placeBySearch(adjacent, key) {
|
|
2410
|
-
const queue = [...key.entries()].sort(([a, ka], [b,
|
|
2472
|
+
const queue = [...key.entries()].sort(([a, ka], [b, kb2]) => compareKeys(ka, kb2) || compare(a, b)).map(([id]) => id);
|
|
2411
2473
|
const placed = queue.length;
|
|
2412
2474
|
for (const id of queue) {
|
|
2413
2475
|
const from = key.get(id);
|
|
@@ -3031,7 +3093,7 @@ var LAYOUT_MODES = [
|
|
|
3031
3093
|
{
|
|
3032
3094
|
value: "walkrows",
|
|
3033
3095
|
label: "Walk rows",
|
|
3034
|
-
description: "x is each walk's own bp: one bar per haplotype,
|
|
3096
|
+
description: "x is each walk's own bp: one bar per haplotype, blue where the graph aligns it to the reference and purple where it does not, so a repeat expansion reads as bar length, and each bar boxes its haplotype's own genes where the session has them. Needs W or P lines.",
|
|
3035
3097
|
run: walkRowLayout,
|
|
3036
3098
|
cutMargins: false,
|
|
3037
3099
|
wholeWalks: true,
|
|
@@ -4235,7 +4297,7 @@ function walkHighlight(graph, name) {
|
|
|
4235
4297
|
});
|
|
4236
4298
|
}
|
|
4237
4299
|
const reference = isReference(graph, name);
|
|
4238
|
-
const
|
|
4300
|
+
const referencePieces = reference ? [] : (graph.paths ?? []).filter((p) => isReference(graph, p.name));
|
|
4239
4301
|
return {
|
|
4240
4302
|
name,
|
|
4241
4303
|
reference,
|
|
@@ -4245,7 +4307,7 @@ function walkHighlight(graph, name) {
|
|
|
4245
4307
|
progress,
|
|
4246
4308
|
steps: pieces.reduce((sum, piece) => sum + piece.nodeIds.length, 0),
|
|
4247
4309
|
bp,
|
|
4248
|
-
referenceBp:
|
|
4310
|
+
referenceBp: referencePieces.length > 0 ? referencePieces.reduce((sum, piece) => sum + bpOf(piece), 0) : void 0
|
|
4249
4311
|
};
|
|
4250
4312
|
}
|
|
4251
4313
|
function laneColors(graph, walk, encoding, ramp) {
|
|
@@ -4349,8 +4411,11 @@ function walkKey(walk, reference) {
|
|
|
4349
4411
|
const { field } = walk.encoding;
|
|
4350
4412
|
const own = walk.range ? rangeText(walk.range.contig, walk.range.start, walk.range.end) : void 0;
|
|
4351
4413
|
const scale = field === "progress" ? own : field === "reference" && reference ? rangeText(reference.name, reference.start, reference.end) : void 0;
|
|
4414
|
+
const spanned = walk.range ? walk.range.end - walk.range.start : walk.bp;
|
|
4415
|
+
const outside = spanned - walk.bp;
|
|
4352
4416
|
return {
|
|
4353
|
-
delta: walk.referenceBp === void 0 ||
|
|
4417
|
+
delta: walk.referenceBp === void 0 || spanned === walk.referenceBp ? "" : ` ${spanned > walk.referenceBp ? "+" : "\u2212"}${formatBp(Math.abs(spanned - walk.referenceBp))}`,
|
|
4418
|
+
outside: outside > 0 ? `, ${formatBp(outside)} outside the cut` : "",
|
|
4354
4419
|
reversed: walk.reversedBp > 0 ? `, ${formatBp(walk.reversedBp)} reversed` : "",
|
|
4355
4420
|
shades: field !== "walk",
|
|
4356
4421
|
scale,
|
|
@@ -4369,6 +4434,28 @@ function walkPosition(walk, nodeId, length) {
|
|
|
4369
4434
|
return `${range.contig}:${groupDigits(start)}-${groupDigits(start + length)}`;
|
|
4370
4435
|
}
|
|
4371
4436
|
|
|
4437
|
+
// src/el.ts
|
|
4438
|
+
function el(tag, attrs = {}, ...children) {
|
|
4439
|
+
return {
|
|
4440
|
+
tag,
|
|
4441
|
+
attrs,
|
|
4442
|
+
children: children.filter((c) => !!c || c === "")
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4445
|
+
function escapeText(s) {
|
|
4446
|
+
return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
4447
|
+
}
|
|
4448
|
+
function escapeAttr(s) {
|
|
4449
|
+
return escapeText(s).replaceAll('"', """);
|
|
4450
|
+
}
|
|
4451
|
+
function serializeEl(node) {
|
|
4452
|
+
if (typeof node === "string") {
|
|
4453
|
+
return escapeText(node);
|
|
4454
|
+
}
|
|
4455
|
+
const attrs = Object.entries(node.attrs).filter(([, v]) => v !== void 0).map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`).join("");
|
|
4456
|
+
return `<${node.tag}${attrs}>${node.children.map(serializeEl).join("")}</${node.tag}>`;
|
|
4457
|
+
}
|
|
4458
|
+
|
|
4372
4459
|
// src/facetGrid.ts
|
|
4373
4460
|
var FACET_GAP_PX = 8;
|
|
4374
4461
|
var FACET_TITLE_PX = 34;
|
|
@@ -4675,6 +4762,385 @@ function layoutLabels(m) {
|
|
|
4675
4762
|
};
|
|
4676
4763
|
}
|
|
4677
4764
|
|
|
4765
|
+
// src/referenceRampCss.ts
|
|
4766
|
+
function rampHueCss(hue) {
|
|
4767
|
+
return `hsl(${hue}, 70%, 50%)`;
|
|
4768
|
+
}
|
|
4769
|
+
function rampHue(bp, domain) {
|
|
4770
|
+
const frac = (bp - domain.start) / (domain.end - domain.start);
|
|
4771
|
+
return Math.max(0, Math.min(1, frac)) * REFERENCE_RAMP_MAX_HUE;
|
|
4772
|
+
}
|
|
4773
|
+
var HUE_STOP_DEG = 30;
|
|
4774
|
+
function rampStops({ start, bp, reversed }, domain) {
|
|
4775
|
+
const low = rampHue(start, domain);
|
|
4776
|
+
const high = rampHue(start + bp, domain);
|
|
4777
|
+
if (high - low < 1) {
|
|
4778
|
+
return [(low + high) / 2];
|
|
4779
|
+
}
|
|
4780
|
+
const n2 = Math.ceil((high - low) / HUE_STOP_DEG);
|
|
4781
|
+
return Array.from(
|
|
4782
|
+
{ length: n2 + 1 },
|
|
4783
|
+
(_, i) => rampHue(start + bp * (reversed ? n2 - i : i) / n2, domain)
|
|
4784
|
+
);
|
|
4785
|
+
}
|
|
4786
|
+
var RAMP_GRADIENT_CSS = `linear-gradient(to right, ${Array.from(
|
|
4787
|
+
{ length: 7 },
|
|
4788
|
+
(_, i) => `${rampHueCss(i / 6 * REFERENCE_RAMP_MAX_HUE)} ${i / 6 * 100}%`
|
|
4789
|
+
).join(", ")})`;
|
|
4790
|
+
|
|
4791
|
+
// src/layout/walkRowDraw.ts
|
|
4792
|
+
var ON_REFERENCE = "#2f8fd6";
|
|
4793
|
+
var OFF_REFERENCE = "#8e3fbf";
|
|
4794
|
+
var OUTSIDE_CUT = "#bdbdbd";
|
|
4795
|
+
var GENE_INK = "#1c1c22";
|
|
4796
|
+
var BAR_PX = 12;
|
|
4797
|
+
var GAP_PX = 4;
|
|
4798
|
+
var MIN_TILE_PX = 3;
|
|
4799
|
+
function kb(bp) {
|
|
4800
|
+
return `${(bp / 1e3).toFixed(bp < 1e4 ? 1 : 0)} kb`;
|
|
4801
|
+
}
|
|
4802
|
+
function units(bp, unit) {
|
|
4803
|
+
return unit ? ` \u2248 ${Math.round(bp / unit)} units` : "";
|
|
4804
|
+
}
|
|
4805
|
+
function walkRowReadout(row, reference, unit, call) {
|
|
4806
|
+
const { bp, gapBp, complete } = row;
|
|
4807
|
+
if (!reference) {
|
|
4808
|
+
return `${kb(bp)}${units(bp, unit)}`;
|
|
4809
|
+
}
|
|
4810
|
+
const delta = bp - reference.bp;
|
|
4811
|
+
const against = delta === 0 ? "" : ` (${delta > 0 ? "+" : "\u2212"}${kb(Math.abs(delta))})`;
|
|
4812
|
+
const outside = gapBp > 0 ? ` \xB7 ${kb(gapBp)} outside the cut` : "";
|
|
4813
|
+
const called = call ? ` \xB7 called ${kb(call.bp)}${call.spanningReads === 0 ? " \xB7 no spanning read" : ""}` : "";
|
|
4814
|
+
return `${kb(bp)}${units(bp, unit)}${against}${outside}${complete ? "" : " \xB7 partial walk"}${called}`;
|
|
4815
|
+
}
|
|
4816
|
+
function readoutPlacement(text, endX, width) {
|
|
4817
|
+
const x = endX + 6;
|
|
4818
|
+
const fits = x + text.length * LABEL_CHAR_PX < width;
|
|
4819
|
+
return fits ? { x, anchor: "start", halo: false } : { x: endX - 6, anchor: "end", halo: true };
|
|
4820
|
+
}
|
|
4821
|
+
function unitTicks(bp, unit, pxPerBp) {
|
|
4822
|
+
if (unit * pxPerBp < MIN_TILE_PX) {
|
|
4823
|
+
return [];
|
|
4824
|
+
}
|
|
4825
|
+
const ticks = [];
|
|
4826
|
+
for (let k = unit; k < bp; k += unit) {
|
|
4827
|
+
ticks.push(k);
|
|
4828
|
+
}
|
|
4829
|
+
return ticks;
|
|
4830
|
+
}
|
|
4831
|
+
function runPaint(run, ramp) {
|
|
4832
|
+
if (run.gap) {
|
|
4833
|
+
return { fill: OUTSIDE_CUT };
|
|
4834
|
+
}
|
|
4835
|
+
if (!ramp) {
|
|
4836
|
+
return { fill: run.onReference ? ON_REFERENCE : OFF_REFERENCE };
|
|
4837
|
+
}
|
|
4838
|
+
if (run.referenceStart === void 0) {
|
|
4839
|
+
return { fill: REFERENCE_RAMP_ALT_CSS };
|
|
4840
|
+
}
|
|
4841
|
+
const stops = rampStops({ ...run, start: run.referenceStart }, ramp);
|
|
4842
|
+
return stops.length === 1 ? { fill: rampHueCss(stops[0]) } : { stops: stops.map(rampHueCss) };
|
|
4843
|
+
}
|
|
4844
|
+
function alongRow(axis, start, end) {
|
|
4845
|
+
return axis.reversed ? { start: axis.start - end, end: axis.start - start } : { start: start - axis.start, end: end - axis.start };
|
|
4846
|
+
}
|
|
4847
|
+
function rowSpan(axis, bp) {
|
|
4848
|
+
return axis.reversed ? { start: axis.start - bp, end: axis.start } : { start: axis.start, end: axis.start + bp };
|
|
4849
|
+
}
|
|
4850
|
+
function placeRowGenes(rows, byRow) {
|
|
4851
|
+
const placed = /* @__PURE__ */ new Map();
|
|
4852
|
+
for (const row of rows) {
|
|
4853
|
+
const genes = byRow.get(row.name);
|
|
4854
|
+
if (!row.axis || !genes) {
|
|
4855
|
+
continue;
|
|
4856
|
+
}
|
|
4857
|
+
const axis = row.axis;
|
|
4858
|
+
const clip = ({ start, end }) => ({
|
|
4859
|
+
start: Math.max(0, start),
|
|
4860
|
+
end: Math.min(row.bp, end)
|
|
4861
|
+
});
|
|
4862
|
+
placed.set(
|
|
4863
|
+
row.name,
|
|
4864
|
+
genes.map((g) => ({
|
|
4865
|
+
name: g.name,
|
|
4866
|
+
...clip(alongRow(axis, g.start, g.end)),
|
|
4867
|
+
exons: g.exons.map((e) => clip(alongRow(axis, e.start, e.end))).filter((e) => e.end > e.start)
|
|
4868
|
+
})).filter((g) => g.end > g.start).sort((a, b) => a.start - b.start)
|
|
4869
|
+
);
|
|
4870
|
+
}
|
|
4871
|
+
return placed;
|
|
4872
|
+
}
|
|
4873
|
+
var GENE_FONT_PX = 9;
|
|
4874
|
+
var GENE_CHAR_PX = 5.2;
|
|
4875
|
+
function rowGeneBoxes(genes, X, y) {
|
|
4876
|
+
const boxes = genes.map((g) => {
|
|
4877
|
+
const x = X(g.start);
|
|
4878
|
+
return {
|
|
4879
|
+
g,
|
|
4880
|
+
x,
|
|
4881
|
+
w: Math.max(2, X(g.end) - x)
|
|
4882
|
+
};
|
|
4883
|
+
});
|
|
4884
|
+
const taken = [];
|
|
4885
|
+
const named = new Set(
|
|
4886
|
+
[...boxes].sort((a, b) => a.w - b.w).filter(({ g, x, w }) => {
|
|
4887
|
+
const half = g.name.length * GENE_CHAR_PX / 2;
|
|
4888
|
+
const at = [x + w / 2 - half, x + w / 2 + half];
|
|
4889
|
+
if (2 * half > w + 6 || taken.some(([a, b]) => at[0] < b + 2 && at[1] > a - 2)) {
|
|
4890
|
+
return false;
|
|
4891
|
+
}
|
|
4892
|
+
taken.push(at);
|
|
4893
|
+
return true;
|
|
4894
|
+
})
|
|
4895
|
+
);
|
|
4896
|
+
return boxes.map((box) => ({
|
|
4897
|
+
name: box.g.name,
|
|
4898
|
+
x: box.x,
|
|
4899
|
+
y: y - BAR_PX / 2 - 2,
|
|
4900
|
+
w: box.w,
|
|
4901
|
+
h: BAR_PX + 4,
|
|
4902
|
+
exons: box.g.exons.map((e) => ({
|
|
4903
|
+
x: X(e.start),
|
|
4904
|
+
w: Math.max(1, X(e.end) - X(e.start))
|
|
4905
|
+
})),
|
|
4906
|
+
label: named.has(box) ? { x: box.x + box.w / 2, y: y + 3, size: GENE_FONT_PX } : void 0
|
|
4907
|
+
}));
|
|
4908
|
+
}
|
|
4909
|
+
var rowsText = (n2) => `${n2} row${n2 === 1 ? "" : "s"}`;
|
|
4910
|
+
function walkRowsKey(bars, o = {}) {
|
|
4911
|
+
const entries = [
|
|
4912
|
+
{
|
|
4913
|
+
swatch: {
|
|
4914
|
+
kind: "bar",
|
|
4915
|
+
fill: o.ramp ? o.rampCss ?? ON_REFERENCE : ON_REFERENCE
|
|
4916
|
+
},
|
|
4917
|
+
label: `aligned to ${bars.reference.label} in the graph`
|
|
4918
|
+
},
|
|
4919
|
+
{
|
|
4920
|
+
swatch: {
|
|
4921
|
+
kind: "bar",
|
|
4922
|
+
fill: o.ramp ? REFERENCE_RAMP_ALT_CSS : OFF_REFERENCE
|
|
4923
|
+
},
|
|
4924
|
+
label: "not aligned to it in the graph"
|
|
4925
|
+
}
|
|
4926
|
+
];
|
|
4927
|
+
if ([bars.reference, ...bars.rows].some((row) => row.gapBp > 0)) {
|
|
4928
|
+
entries.push({
|
|
4929
|
+
swatch: { kind: "gap", fill: OUTSIDE_CUT },
|
|
4930
|
+
label: "walked outside the cut"
|
|
4931
|
+
});
|
|
4932
|
+
}
|
|
4933
|
+
if (o.genes) {
|
|
4934
|
+
entries.push({
|
|
4935
|
+
swatch: { kind: "gene" },
|
|
4936
|
+
label: "genes, each row's own annotation"
|
|
4937
|
+
});
|
|
4938
|
+
if (o.genes.untracked) {
|
|
4939
|
+
entries.push({
|
|
4940
|
+
swatch: { kind: "gene" },
|
|
4941
|
+
label: `no gene track for ${rowsText(o.genes.untracked)}`,
|
|
4942
|
+
note: true
|
|
4943
|
+
});
|
|
4944
|
+
}
|
|
4945
|
+
if (o.genes.unplaced) {
|
|
4946
|
+
entries.push({
|
|
4947
|
+
swatch: { kind: "gene" },
|
|
4948
|
+
label: `no contig coordinates for ${rowsText(o.genes.unplaced)}`,
|
|
4949
|
+
note: true
|
|
4950
|
+
});
|
|
4951
|
+
}
|
|
4952
|
+
if (o.genes.unread) {
|
|
4953
|
+
entries.push({
|
|
4954
|
+
swatch: { kind: "gene" },
|
|
4955
|
+
label: `genes not read for the last ${rowsText(o.genes.unread)}`,
|
|
4956
|
+
note: true
|
|
4957
|
+
});
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
return entries;
|
|
4961
|
+
}
|
|
4962
|
+
var CALL_TICK = "#111";
|
|
4963
|
+
var UNBACKED_TICK = "#9e9e9e";
|
|
4964
|
+
var DISAGREES = "#c62828";
|
|
4965
|
+
function walkRowsKeyTree(entries, x, y) {
|
|
4966
|
+
let at = 0;
|
|
4967
|
+
const parts = entries.flatMap((e) => {
|
|
4968
|
+
const swatch = e.note ? void 0 : e.swatch.kind === "gene" ? el("rect", {
|
|
4969
|
+
x: at,
|
|
4970
|
+
y: 5,
|
|
4971
|
+
width: 18,
|
|
4972
|
+
height: 8,
|
|
4973
|
+
fill: "none",
|
|
4974
|
+
stroke: GENE_INK,
|
|
4975
|
+
"stroke-width": 1.5
|
|
4976
|
+
}) : el("rect", {
|
|
4977
|
+
x: at,
|
|
4978
|
+
y: e.swatch.kind === "gap" ? 7 : 5,
|
|
4979
|
+
width: 18,
|
|
4980
|
+
height: e.swatch.kind === "gap" ? GAP_PX : 8,
|
|
4981
|
+
rx: 2,
|
|
4982
|
+
fill: e.swatch.fill
|
|
4983
|
+
});
|
|
4984
|
+
const text = el(
|
|
4985
|
+
"text",
|
|
4986
|
+
{
|
|
4987
|
+
x: e.note ? at : at + 23,
|
|
4988
|
+
y: 13,
|
|
4989
|
+
"font-style": e.note ? "italic" : void 0,
|
|
4990
|
+
fill: e.note ? "#666" : void 0
|
|
4991
|
+
},
|
|
4992
|
+
e.label
|
|
4993
|
+
);
|
|
4994
|
+
at += (e.note ? 0 : 23) + e.label.length * LABEL_CHAR_PX + 16;
|
|
4995
|
+
return swatch ? [swatch, text] : [text];
|
|
4996
|
+
});
|
|
4997
|
+
return {
|
|
4998
|
+
width: at,
|
|
4999
|
+
tree: el(
|
|
5000
|
+
"g",
|
|
5001
|
+
{
|
|
5002
|
+
transform: `translate(${x} ${y})`,
|
|
5003
|
+
"font-family": "Helvetica, Arial, sans-serif",
|
|
5004
|
+
"font-size": 11
|
|
5005
|
+
},
|
|
5006
|
+
...parts
|
|
5007
|
+
)
|
|
5008
|
+
};
|
|
5009
|
+
}
|
|
5010
|
+
function geneTree(box, y) {
|
|
5011
|
+
return el(
|
|
5012
|
+
"g",
|
|
5013
|
+
{ class: "row-gene", "data-testid": "graph-walk-gene" },
|
|
5014
|
+
el("title", {}, box.name),
|
|
5015
|
+
...box.exons.map(
|
|
5016
|
+
(e) => el("rect", {
|
|
5017
|
+
x: e.x,
|
|
5018
|
+
y: y - BAR_PX / 2,
|
|
5019
|
+
width: e.w,
|
|
5020
|
+
height: BAR_PX,
|
|
5021
|
+
fill: GENE_INK,
|
|
5022
|
+
opacity: 0.35
|
|
5023
|
+
})
|
|
5024
|
+
),
|
|
5025
|
+
el("rect", {
|
|
5026
|
+
x: box.x,
|
|
5027
|
+
y: box.y,
|
|
5028
|
+
width: box.w,
|
|
5029
|
+
height: box.h,
|
|
5030
|
+
fill: "none",
|
|
5031
|
+
stroke: GENE_INK,
|
|
5032
|
+
"stroke-width": 1.5
|
|
5033
|
+
}),
|
|
5034
|
+
box.label && el(
|
|
5035
|
+
"text",
|
|
5036
|
+
{
|
|
5037
|
+
x: box.label.x,
|
|
5038
|
+
y: box.label.y,
|
|
5039
|
+
"font-family": "sans-serif",
|
|
5040
|
+
"font-size": box.label.size,
|
|
5041
|
+
"font-weight": 600,
|
|
5042
|
+
"text-anchor": "middle",
|
|
5043
|
+
fill: GENE_INK,
|
|
5044
|
+
stroke: "white",
|
|
5045
|
+
"stroke-width": 2.5,
|
|
5046
|
+
"paint-order": "stroke"
|
|
5047
|
+
},
|
|
5048
|
+
box.name
|
|
5049
|
+
)
|
|
5050
|
+
);
|
|
5051
|
+
}
|
|
5052
|
+
function walkRowsTree(bars, frame, o = {}) {
|
|
5053
|
+
const X = (bp) => bp * frame.scaleX + frame.translateX;
|
|
5054
|
+
const Y = (row) => row * ROW_HEIGHT_PX * frame.scaleY + frame.translateY;
|
|
5055
|
+
const { origin, unit, reference, rows } = bars;
|
|
5056
|
+
const along = (offset) => X(origin + offset);
|
|
5057
|
+
const prefix = o.idPrefix ?? "walkrow";
|
|
5058
|
+
const rowTree = (row, i) => {
|
|
5059
|
+
const y = Y(i);
|
|
5060
|
+
if (y < -BAR_PX || y > frame.height + BAR_PX) {
|
|
5061
|
+
return void 0;
|
|
5062
|
+
}
|
|
5063
|
+
const runs = row.runs.flatMap((run) => {
|
|
5064
|
+
const paint2 = runPaint(run, o.ramp);
|
|
5065
|
+
const h = run.gap ? GAP_PX : BAR_PX;
|
|
5066
|
+
const id = `${prefix}-${i}-${run.start}`;
|
|
5067
|
+
const rect = el("rect", {
|
|
5068
|
+
x: along(run.start),
|
|
5069
|
+
y: y - h / 2,
|
|
5070
|
+
width: Math.max(1, run.bp * frame.scaleX),
|
|
5071
|
+
height: h,
|
|
5072
|
+
fill: "stops" in paint2 ? `url(#${id})` : paint2.fill
|
|
5073
|
+
});
|
|
5074
|
+
return "stops" in paint2 ? [
|
|
5075
|
+
el(
|
|
5076
|
+
"linearGradient",
|
|
5077
|
+
{ id },
|
|
5078
|
+
...paint2.stops.map(
|
|
5079
|
+
(color, k) => el("stop", {
|
|
5080
|
+
offset: k / (paint2.stops.length - 1),
|
|
5081
|
+
"stop-color": color
|
|
5082
|
+
})
|
|
5083
|
+
)
|
|
5084
|
+
),
|
|
5085
|
+
rect
|
|
5086
|
+
] : [rect];
|
|
5087
|
+
});
|
|
5088
|
+
const ticks = unit ? unitTicks(row.bp, unit, frame.scaleX).map(
|
|
5089
|
+
(k) => el("line", {
|
|
5090
|
+
x1: along(k),
|
|
5091
|
+
x2: along(k),
|
|
5092
|
+
y1: y - BAR_PX / 2,
|
|
5093
|
+
y2: y + BAR_PX / 2,
|
|
5094
|
+
stroke: "white",
|
|
5095
|
+
"stroke-width": 1
|
|
5096
|
+
})
|
|
5097
|
+
) : [];
|
|
5098
|
+
const { call } = row;
|
|
5099
|
+
const text = walkRowReadout(
|
|
5100
|
+
row,
|
|
5101
|
+
i === 0 ? void 0 : reference,
|
|
5102
|
+
unit,
|
|
5103
|
+
call
|
|
5104
|
+
);
|
|
5105
|
+
const at = readoutPlacement(text, along(row.bp), frame.width);
|
|
5106
|
+
return el(
|
|
5107
|
+
"g",
|
|
5108
|
+
{
|
|
5109
|
+
"data-testid": i === 0 ? "graph-walk-reference" : "graph-walk-row"
|
|
5110
|
+
},
|
|
5111
|
+
...runs,
|
|
5112
|
+
...ticks,
|
|
5113
|
+
...rowGeneBoxes(o.rowGenes?.get(row.name) ?? [], along, y).map(
|
|
5114
|
+
(box) => geneTree(box, y)
|
|
5115
|
+
),
|
|
5116
|
+
call && el("rect", {
|
|
5117
|
+
"data-testid": "graph-walk-call",
|
|
5118
|
+
x: along(call.bp) - 1,
|
|
5119
|
+
y: y - BAR_PX / 2 - 3,
|
|
5120
|
+
width: 2,
|
|
5121
|
+
height: BAR_PX + 6,
|
|
5122
|
+
fill: call.spanningReads === 0 ? UNBACKED_TICK : CALL_TICK
|
|
5123
|
+
}),
|
|
5124
|
+
el(
|
|
5125
|
+
"text",
|
|
5126
|
+
{
|
|
5127
|
+
x: at.x,
|
|
5128
|
+
y: y + 4,
|
|
5129
|
+
"font-family": "sans-serif",
|
|
5130
|
+
"font-size": 11,
|
|
5131
|
+
fill: call?.agrees === false ? DISAGREES : "#333",
|
|
5132
|
+
stroke: at.halo ? "white" : void 0,
|
|
5133
|
+
"stroke-width": at.halo ? 3 : void 0,
|
|
5134
|
+
"paint-order": "stroke",
|
|
5135
|
+
"text-anchor": at.anchor
|
|
5136
|
+
},
|
|
5137
|
+
text
|
|
5138
|
+
)
|
|
5139
|
+
);
|
|
5140
|
+
};
|
|
5141
|
+
return el("g", {}, ...[reference, ...rows].map(rowTree));
|
|
5142
|
+
}
|
|
5143
|
+
|
|
4678
5144
|
// src/renderer/svgCanvas.ts
|
|
4679
5145
|
var n = (v) => +v.toFixed(1);
|
|
4680
5146
|
function paint(css) {
|
|
@@ -4788,7 +5254,7 @@ function svgCanvas(width, height) {
|
|
|
4788
5254
|
}
|
|
4789
5255
|
|
|
4790
5256
|
// src/version.ts
|
|
4791
|
-
var version = "4.0
|
|
5257
|
+
var version = "4.1.0";
|
|
4792
5258
|
|
|
4793
5259
|
// src/viewport.ts
|
|
4794
5260
|
function axisScaleOf(scale, pixelRows) {
|
|
@@ -4833,12 +5299,12 @@ function screenToLayout(x, y, t, axis) {
|
|
|
4833
5299
|
|
|
4834
5300
|
// src/figure.ts
|
|
4835
5301
|
var FONT = 'font-family="Helvetica, Arial, sans-serif" font-size="11"';
|
|
4836
|
-
var
|
|
5302
|
+
var BAR_PX2 = 48;
|
|
4837
5303
|
var SWATCH_PX = 18;
|
|
4838
5304
|
var KEY_GAP_PX = 16;
|
|
4839
5305
|
var FADED = "rgb(160,160,160)";
|
|
4840
5306
|
var EXON_COLOR = "#daa520";
|
|
4841
|
-
var
|
|
5307
|
+
var GENE_INK2 = "#1c1c22";
|
|
4842
5308
|
var MIN_LANE_PX = 4;
|
|
4843
5309
|
var EXON_GAP_PX = 1;
|
|
4844
5310
|
var EXON_LINE_PX = 2;
|
|
@@ -4846,7 +5312,7 @@ function esc(s) {
|
|
|
4846
5312
|
return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4847
5313
|
}
|
|
4848
5314
|
function keySvg(id, x, y, stops, label, after, under, opacity = 1) {
|
|
4849
|
-
const bar = stops.length > 1 ?
|
|
5315
|
+
const bar = stops.length > 1 ? BAR_PX2 : SWATCH_PX;
|
|
4850
5316
|
const fill = stops.length > 1 ? `fill="url(#${id})"` : `fill="${stops[0].color}"${opacity < 1 ? ` fill-opacity="${opacity}"` : ""}`;
|
|
4851
5317
|
const defs = stops.length > 1 ? `<linearGradient id="${id}">${stops.map((s) => `<stop offset="${s.offset}" stop-color="${s.color}"/>`).join("")}</linearGradient>` : "";
|
|
4852
5318
|
const width = Math.max(
|
|
@@ -4866,7 +5332,7 @@ function walkKeySvg(id, x, y, walk, label, reference) {
|
|
|
4866
5332
|
y,
|
|
4867
5333
|
encodingStops(walk.encoding),
|
|
4868
5334
|
label,
|
|
4869
|
-
key.delta + key.reversed,
|
|
5335
|
+
key.delta + key.outside + key.reversed,
|
|
4870
5336
|
key.scale
|
|
4871
5337
|
);
|
|
4872
5338
|
}
|
|
@@ -4877,15 +5343,17 @@ function screenPath(d, t) {
|
|
|
4877
5343
|
);
|
|
4878
5344
|
}
|
|
4879
5345
|
function chip(x, y, w, name, note) {
|
|
4880
|
-
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="${
|
|
5346
|
+
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_INK2}" text-anchor="middle"><tspan font-style="italic" font-weight="600">${esc(name)}</tspan>${note ? esc(note) : ""}</text>`;
|
|
4881
5347
|
}
|
|
4882
5348
|
function figureSvg(graph, layout, o = {}) {
|
|
4883
5349
|
const width = o.width ?? 1200;
|
|
4884
5350
|
const room = o.height ?? 800;
|
|
4885
5351
|
const region = o.region;
|
|
4886
5352
|
const rampDomain = o.colorDomain ?? region;
|
|
5353
|
+
const rows = o.walkRows;
|
|
4887
5354
|
const bounds = drawingBounds(layout, {
|
|
4888
|
-
region: o.fitToDrawing ? void 0 : region
|
|
5355
|
+
region: o.fitToDrawing ? void 0 : region,
|
|
5356
|
+
extent: rows && layout.extent ? walkRowsExtent(rows) : void 0
|
|
4889
5357
|
});
|
|
4890
5358
|
const pixelRows = layout.pixelRows ?? false;
|
|
4891
5359
|
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
@@ -4910,7 +5378,7 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4910
5378
|
};
|
|
4911
5379
|
const contigThickness = o.contigThickness ?? 6;
|
|
4912
5380
|
const nodeWidth2 = o.nodeWidth ?? "depth";
|
|
4913
|
-
const pins = o.genes?.length && !layout.tubeMap ? genePins(graph, o.genes, layout.nodePositions) : [];
|
|
5381
|
+
const pins = o.genes?.length && !layout.tubeMap && !rows ? genePins(graph, o.genes, layout.nodePositions) : [];
|
|
4914
5382
|
const ink = nodeInk(graph, nodeById, contigThickness, nodeWidth2);
|
|
4915
5383
|
let masks = 0;
|
|
4916
5384
|
function overlays(highlight, t, w, h) {
|
|
@@ -4946,11 +5414,32 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4946
5414
|
)) {
|
|
4947
5415
|
const pinY = screen(pin.at).y + contigThickness / 2;
|
|
4948
5416
|
out.push(
|
|
4949
|
-
`<line x1="${x}" x2="${x}" y1="${y - LABEL_PX - 2}" y2="${pinY}" stroke="${
|
|
5417
|
+
`<line x1="${x}" x2="${x}" y1="${y - LABEL_PX - 2}" y2="${pinY}" stroke="${GENE_INK2}" stroke-width="0.8" stroke-opacity="0.6"/>`,
|
|
4950
5418
|
chip(x, y, cw, pin.gene.name, text.slice(pin.gene.name.length))
|
|
4951
5419
|
);
|
|
4952
5420
|
}
|
|
4953
|
-
|
|
5421
|
+
if (rows) {
|
|
5422
|
+
out.push(
|
|
5423
|
+
serializeEl(
|
|
5424
|
+
walkRowsTree(
|
|
5425
|
+
rows,
|
|
5426
|
+
{ ...t, width: w, height: h },
|
|
5427
|
+
{
|
|
5428
|
+
ramp: referenceRamp && {
|
|
5429
|
+
start: referenceRamp.start,
|
|
5430
|
+
end: referenceRamp.start + referenceRamp.span
|
|
5431
|
+
},
|
|
5432
|
+
rowGenes: o.rowGenes
|
|
5433
|
+
}
|
|
5434
|
+
)
|
|
5435
|
+
)
|
|
5436
|
+
);
|
|
5437
|
+
}
|
|
5438
|
+
const rowLabels = rows ? [rows.reference, ...rows.rows].map((r, i) => ({
|
|
5439
|
+
label: r.label,
|
|
5440
|
+
y: i * ROW_HEIGHT_PX
|
|
5441
|
+
})) : layout.rowLabels ?? [];
|
|
5442
|
+
for (const { label, y } of rowLabels) {
|
|
4954
5443
|
const sy = y * t.scaleY + t.translateY;
|
|
4955
5444
|
if (sy >= 0 && sy <= h) {
|
|
4956
5445
|
out.push(
|
|
@@ -4981,7 +5470,7 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
4981
5470
|
const renderer = new Canvas2DRenderer(canvas);
|
|
4982
5471
|
renderer.uploadGeometry(
|
|
4983
5472
|
buildGeometry({
|
|
4984
|
-
nodePositions: layout.nodePositions,
|
|
5473
|
+
nodePositions: rows ? {} : layout.nodePositions,
|
|
4985
5474
|
graph,
|
|
4986
5475
|
nodeById,
|
|
4987
5476
|
colorScheme,
|
|
@@ -5057,7 +5546,21 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
5057
5546
|
0.3
|
|
5058
5547
|
)
|
|
5059
5548
|
);
|
|
5060
|
-
}
|
|
5549
|
+
}
|
|
5550
|
+
if (rows) {
|
|
5551
|
+
const key = walkRowsKeyTree(
|
|
5552
|
+
walkRowsKey(rows, {
|
|
5553
|
+
ramp: referenceRamp && {
|
|
5554
|
+
start: referenceRamp.start,
|
|
5555
|
+
end: referenceRamp.start + referenceRamp.span
|
|
5556
|
+
},
|
|
5557
|
+
genes: o.rowGenes?.size ? o.rowGeneGaps : void 0
|
|
5558
|
+
}),
|
|
5559
|
+
x,
|
|
5560
|
+
2
|
|
5561
|
+
);
|
|
5562
|
+
push({ width: key.width, markup: serializeEl(key.tree) });
|
|
5563
|
+
} else if (!lift && referenceRamp) {
|
|
5061
5564
|
push(
|
|
5062
5565
|
keySvg(
|
|
5063
5566
|
"ramp",
|
|
@@ -5207,8 +5710,25 @@ function genesFromGff3Lines(lines) {
|
|
|
5207
5710
|
}
|
|
5208
5711
|
return genes;
|
|
5209
5712
|
}
|
|
5713
|
+
function mergeOverlappingByName(genes) {
|
|
5714
|
+
const out = [];
|
|
5715
|
+
const open = /* @__PURE__ */ new Map();
|
|
5716
|
+
for (const g of [...genes].sort((a, b) => a.start - b.start)) {
|
|
5717
|
+
const key = `${g.refName} ${g.strand} ${g.name}`;
|
|
5718
|
+
const last = open.get(key);
|
|
5719
|
+
if (last && g.start < last.end) {
|
|
5720
|
+
last.end = Math.max(last.end, g.end);
|
|
5721
|
+
last.exons = mergedIntervals([...last.exons, ...g.exons]);
|
|
5722
|
+
} else {
|
|
5723
|
+
const gene = { ...g, exons: mergedIntervals(g.exons) };
|
|
5724
|
+
out.push(gene);
|
|
5725
|
+
open.set(key, gene);
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
return out;
|
|
5729
|
+
}
|
|
5210
5730
|
function genesFromBed(text) {
|
|
5211
|
-
const
|
|
5731
|
+
const rows = [];
|
|
5212
5732
|
for (const line of text.split("\n")) {
|
|
5213
5733
|
const cols = line.replace(/\r$/, "").split(" ");
|
|
5214
5734
|
const start = Number(cols[1]);
|
|
@@ -5224,24 +5744,16 @@ function genesFromBed(text) {
|
|
|
5224
5744
|
start: start + starts[i],
|
|
5225
5745
|
end: start + starts[i] + size
|
|
5226
5746
|
})) : [{ start, end }];
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
name,
|
|
5236
|
-
refName,
|
|
5237
|
-
start,
|
|
5238
|
-
end,
|
|
5239
|
-
strand: strandOf(cols[5]),
|
|
5240
|
-
exons: mergedIntervals(blocks)
|
|
5241
|
-
});
|
|
5242
|
-
}
|
|
5747
|
+
rows.push({
|
|
5748
|
+
name,
|
|
5749
|
+
refName,
|
|
5750
|
+
start,
|
|
5751
|
+
end,
|
|
5752
|
+
strand: strandOf(cols[5]),
|
|
5753
|
+
exons: blocks
|
|
5754
|
+
});
|
|
5243
5755
|
}
|
|
5244
|
-
return
|
|
5756
|
+
return mergeOverlappingByName(rows);
|
|
5245
5757
|
}
|
|
5246
5758
|
function genesFromText(text) {
|
|
5247
5759
|
const first = text.split("\n", 1e3).find((l) => l.trim() !== "" && !/^(#|track|browser)/.test(l));
|
|
@@ -5402,7 +5914,7 @@ function parseWalk(line) {
|
|
|
5402
5914
|
end: +fields[5]
|
|
5403
5915
|
};
|
|
5404
5916
|
}
|
|
5405
|
-
var
|
|
5917
|
+
var stepsOf2 = (walk) => walk.fields[6].match(/[<>][^<>]+/g) ?? [];
|
|
5406
5918
|
function segmentLengths(lines) {
|
|
5407
5919
|
const lengths = /* @__PURE__ */ new Map();
|
|
5408
5920
|
for (const line of lines) {
|
|
@@ -5421,7 +5933,7 @@ function extend(a, b, lengths) {
|
|
|
5421
5933
|
if (b.end <= a.end) {
|
|
5422
5934
|
return a;
|
|
5423
5935
|
}
|
|
5424
|
-
const steps =
|
|
5936
|
+
const steps = stepsOf2(b);
|
|
5425
5937
|
let pos = b.start;
|
|
5426
5938
|
for (const [i, step] of steps.entries()) {
|
|
5427
5939
|
if (pos === a.end) {
|
|
@@ -5455,7 +5967,7 @@ function joinPieces(pieces, lengths, startsPath) {
|
|
|
5455
5967
|
}
|
|
5456
5968
|
var flip2 = (sign) => sign === "+" ? "-" : "+";
|
|
5457
5969
|
function* stepLinks(walk) {
|
|
5458
|
-
const steps =
|
|
5970
|
+
const steps = stepsOf2(walk).map((step) => ({
|
|
5459
5971
|
id: step.slice(1),
|
|
5460
5972
|
sign: step.startsWith(">") ? "+" : "-"
|
|
5461
5973
|
}));
|
|
@@ -5576,13 +6088,13 @@ async function referencePathQuery(db, referenceSample2, refName) {
|
|
|
5576
6088
|
haplotype: path.name.haplotype
|
|
5577
6089
|
} : void 0;
|
|
5578
6090
|
}
|
|
5579
|
-
function nodeLimitError(error, limit, windowBp
|
|
6091
|
+
function nodeLimitError(error, limit, windowBp) {
|
|
5580
6092
|
const isLimit = error instanceof Error && (error.name === "SubgraphLimitError" || /^Subgraph size limit of \d+ nodes exceeded/.test(error.message));
|
|
5581
6093
|
if (!isLimit) {
|
|
5582
6094
|
return void 0;
|
|
5583
6095
|
} else {
|
|
5584
6096
|
const walked = error.walkedBp;
|
|
5585
|
-
const fits = typeof walked === "number" && walked > 0 ? Math.floor(
|
|
6097
|
+
const fits = typeof walked === "number" && walked > 0 ? Math.floor(walked * 0.8) : Math.floor(windowBp / 2);
|
|
5586
6098
|
return new NodeLimitError(limit, windowBp, Math.max(fits, 1));
|
|
5587
6099
|
}
|
|
5588
6100
|
}
|
|
@@ -5591,44 +6103,16 @@ var GBZ_CUT_DEFAULTS = {
|
|
|
5591
6103
|
snarls: "contained",
|
|
5592
6104
|
limit: 1e5
|
|
5593
6105
|
};
|
|
5594
|
-
async function
|
|
5595
|
-
const inside = (await db.paths()).filter(
|
|
5596
|
-
({ isIndexed, name }) => isIndexed && name.sample === query.sample && name.haplotype === query.haplotype && name.contig === query.contig && name.fragment > start && name.fragment < end
|
|
5597
|
-
).map((path) => path.name.fragment);
|
|
5598
|
-
const bounds = [start, ...[...new Set(inside)].sort((a, b) => a - b), end];
|
|
5599
|
-
return bounds.slice(1).map((pieceEnd, i) => ({
|
|
5600
|
-
start: bounds[i],
|
|
5601
|
-
end: pieceEnd
|
|
5602
|
-
}));
|
|
5603
|
-
}
|
|
5604
|
-
async function cutWindowGFA(db, query, start, end, { keep, ...opts }) {
|
|
6106
|
+
async function cutWindowGFA(db, query, start, end, opts) {
|
|
5605
6107
|
if (!query) {
|
|
5606
6108
|
return "";
|
|
5607
6109
|
}
|
|
5608
|
-
const
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
(piece) => db.getSubgraphForRange(query, piece.start, piece.end, {
|
|
5612
|
-
...opts,
|
|
5613
|
-
haplotypes: "all",
|
|
5614
|
-
...keep === void 0 ? {} : { keep }
|
|
5615
|
-
}).catch((error) => {
|
|
5616
|
-
throw nodeLimitError(
|
|
5617
|
-
error,
|
|
5618
|
-
opts.limit,
|
|
5619
|
-
end - start,
|
|
5620
|
-
piece.start - start
|
|
5621
|
-
) ?? error;
|
|
5622
|
-
})
|
|
5623
|
-
)
|
|
5624
|
-
);
|
|
6110
|
+
const subgraphs = await db.getSubgraphs({ ...opts, path: query, start, end, haplotypes: "all" }).catch((error) => {
|
|
6111
|
+
throw nodeLimitError(error, opts.limit, end - start) ?? error;
|
|
6112
|
+
});
|
|
5625
6113
|
const paths = await db.paths();
|
|
5626
6114
|
return joinCuts(
|
|
5627
|
-
await Promise.all(
|
|
5628
|
-
subgraphs.flatMap(
|
|
5629
|
-
(subgraph) => subgraph ? [subgraph.toGFA({ names: "resolved" })] : []
|
|
5630
|
-
)
|
|
5631
|
-
),
|
|
6115
|
+
await Promise.all(subgraphs.map((subgraph) => subgraph.toGFA())),
|
|
5632
6116
|
(sample, haplotype, contig, at) => paths.some(
|
|
5633
6117
|
({ name }) => name.sample === sample && name.haplotype === haplotype && name.contig === contig && name.fragment === at
|
|
5634
6118
|
)
|
|
@@ -5656,7 +6140,7 @@ function parseRegion(text) {
|
|
|
5656
6140
|
return { refName: m[1], start: n2(m[2]), end: n2(m[3]) };
|
|
5657
6141
|
}
|
|
5658
6142
|
async function openGbz(db, index) {
|
|
5659
|
-
const base = await GBZBase.open(db,
|
|
6143
|
+
const base = await GBZBase.open({ source: db, haplotypeIndex: index });
|
|
5660
6144
|
return { base, referenceSamples: await referenceSamplesOf(base) };
|
|
5661
6145
|
}
|
|
5662
6146
|
function referenceSample(ref, samples) {
|
|
@@ -5687,7 +6171,7 @@ async function cutGbzRegion({ base, referenceSamples }, src, signal) {
|
|
|
5687
6171
|
snarls: src.snarls ?? GBZ_CUT_DEFAULTS.snarls,
|
|
5688
6172
|
limit: GBZ_CUT_DEFAULTS.limit,
|
|
5689
6173
|
signal,
|
|
5690
|
-
|
|
6174
|
+
keep: wanted ? (name) => haplotypeWanted(name, wanted) : void 0
|
|
5691
6175
|
});
|
|
5692
6176
|
return { text, region, sample };
|
|
5693
6177
|
}
|
|
@@ -5765,6 +6249,8 @@ export {
|
|
|
5765
6249
|
rangeText,
|
|
5766
6250
|
walkKey,
|
|
5767
6251
|
walkPosition,
|
|
6252
|
+
el,
|
|
6253
|
+
serializeEl,
|
|
5768
6254
|
FACET_GAP_PX,
|
|
5769
6255
|
FACET_TITLE_PX,
|
|
5770
6256
|
FACET_PAD_PX,
|
|
@@ -5776,6 +6262,28 @@ export {
|
|
|
5776
6262
|
LEGEND_INSET_PX,
|
|
5777
6263
|
geneCoverageNote,
|
|
5778
6264
|
layoutLabels,
|
|
6265
|
+
rampHueCss,
|
|
6266
|
+
rampHue,
|
|
6267
|
+
rampStops,
|
|
6268
|
+
RAMP_GRADIENT_CSS,
|
|
6269
|
+
ON_REFERENCE,
|
|
6270
|
+
OFF_REFERENCE,
|
|
6271
|
+
OUTSIDE_CUT,
|
|
6272
|
+
GENE_INK,
|
|
6273
|
+
BAR_PX,
|
|
6274
|
+
GAP_PX,
|
|
6275
|
+
walkRowReadout,
|
|
6276
|
+
readoutPlacement,
|
|
6277
|
+
unitTicks,
|
|
6278
|
+
runPaint,
|
|
6279
|
+
alongRow,
|
|
6280
|
+
rowSpan,
|
|
6281
|
+
placeRowGenes,
|
|
6282
|
+
rowGeneBoxes,
|
|
6283
|
+
walkRowsKey,
|
|
6284
|
+
DISAGREES,
|
|
6285
|
+
walkRowsKeyTree,
|
|
6286
|
+
walkRowsTree,
|
|
5779
6287
|
svgCanvas,
|
|
5780
6288
|
version,
|
|
5781
6289
|
axisScaleOf,
|
|
@@ -5786,6 +6294,7 @@ export {
|
|
|
5786
6294
|
screenToLayout,
|
|
5787
6295
|
figureSvg,
|
|
5788
6296
|
genesFromGff3Lines,
|
|
6297
|
+
mergeOverlappingByName,
|
|
5789
6298
|
genesFromBed,
|
|
5790
6299
|
genesFromText,
|
|
5791
6300
|
WELL_KNOWN_SAMPLES,
|