@jbrowse/bandage-core 0.1.8 → 0.1.9
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/index.js +280 -111
- package/dist/types/bubbles/bubbleLine.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/labelLayout.d.ts +1 -0
- package/dist/types/layout/tubeMapLayout.d.ts +1 -0
- package/dist/types/pansn.d.ts +1 -0
- package/dist/types/pathColors.d.ts +2 -1
- package/dist/types/referenceStrip.d.ts +45 -0
- package/dist/types/renderer/GeometryBuilder.d.ts +1 -0
- package/dist/types/tubeMap/axis.d.ts +4 -8
- package/dist/types/tubeMap/draw.d.ts +2 -0
- package/dist/types/tubeMap/reads.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -296,6 +296,10 @@ var SEP = "#";
|
|
|
296
296
|
function panSNSample(refName) {
|
|
297
297
|
return refName.split(SEP)[0];
|
|
298
298
|
}
|
|
299
|
+
var GENERIC_SAMPLES = /* @__PURE__ */ new Set(["_gbwt_ref"]);
|
|
300
|
+
function isGenericSample(sample) {
|
|
301
|
+
return GENERIC_SAMPLES.has(sample);
|
|
302
|
+
}
|
|
299
303
|
function panSNHaplotype(refName) {
|
|
300
304
|
const parts = refName.split(SEP);
|
|
301
305
|
return parts.length >= 3 ? `${parts[0]}${SEP}${parts[1]}` : void 0;
|
|
@@ -692,8 +696,8 @@ function pointAt(line, arc, s) {
|
|
|
692
696
|
if (i >= arc.length) {
|
|
693
697
|
return { ...line.at(-1) };
|
|
694
698
|
}
|
|
695
|
-
const
|
|
696
|
-
const t =
|
|
699
|
+
const span2 = arc[i] - arc[i - 1];
|
|
700
|
+
const t = span2 > 0 ? (s - arc[i - 1]) / span2 : 0;
|
|
697
701
|
const a = line[i - 1];
|
|
698
702
|
const b = line[i];
|
|
699
703
|
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
|
|
@@ -838,9 +842,9 @@ function referenceMidpoints(graph) {
|
|
|
838
842
|
const neighbors = buildNeighbors(graph);
|
|
839
843
|
const midpoints = /* @__PURE__ */ new Map();
|
|
840
844
|
for (const node of graph.nodes) {
|
|
841
|
-
const
|
|
842
|
-
if (
|
|
843
|
-
midpoints.set(node.id, (
|
|
845
|
+
const span2 = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors });
|
|
846
|
+
if (span2) {
|
|
847
|
+
midpoints.set(node.id, (span2.start + span2.end) / 2);
|
|
844
848
|
}
|
|
845
849
|
}
|
|
846
850
|
return midpoints;
|
|
@@ -1119,12 +1123,12 @@ function projectAlleles(graph) {
|
|
|
1119
1123
|
for (const node of byId.values()) {
|
|
1120
1124
|
if (node.stable.rank !== REFERENCE_RANK && !claimed.has(node.id)) {
|
|
1121
1125
|
const { members, anchors } = collectRun(node.id, byId, adj, claimed);
|
|
1122
|
-
const
|
|
1123
|
-
if (
|
|
1124
|
-
const { offsets, ordered } = runOffsets(members,
|
|
1126
|
+
const span2 = anchorSpan(anchors);
|
|
1127
|
+
if (span2) {
|
|
1128
|
+
const { offsets, ordered } = runOffsets(members, span2.entry, adj);
|
|
1125
1129
|
alleles.push({
|
|
1126
|
-
start:
|
|
1127
|
-
refSpan:
|
|
1130
|
+
start: span2.start,
|
|
1131
|
+
refSpan: span2.end - span2.start,
|
|
1128
1132
|
nodeIds: ordered.map((m) => m.id),
|
|
1129
1133
|
nodeOffsets: ordered.map((m) => offsets.get(m.id)),
|
|
1130
1134
|
pathLength: ordered.reduce(
|
|
@@ -1143,11 +1147,11 @@ var MIN_DELETION_BP = 1;
|
|
|
1143
1147
|
var MIN_ALLELE_SPAN_FRACTION = 0.015;
|
|
1144
1148
|
function placeOffReference({
|
|
1145
1149
|
graph,
|
|
1146
|
-
span,
|
|
1150
|
+
span: span2,
|
|
1147
1151
|
rowY,
|
|
1148
1152
|
positions
|
|
1149
1153
|
}) {
|
|
1150
|
-
const minSpan =
|
|
1154
|
+
const minSpan = span2 * MIN_ALLELE_SPAN_FRACTION;
|
|
1151
1155
|
const alleles = projectAlleles(graph);
|
|
1152
1156
|
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
1153
1157
|
const alleleDeletions = [];
|
|
@@ -1212,12 +1216,12 @@ function anchoredLayout(graph, region) {
|
|
|
1212
1216
|
if (backbone.length === 0) {
|
|
1213
1217
|
return void 0;
|
|
1214
1218
|
}
|
|
1215
|
-
const
|
|
1219
|
+
const span2 = referenceSpan(backbone, region);
|
|
1216
1220
|
const rows = rankRows(graph);
|
|
1217
1221
|
const nodePositions = backbonePositions(backbone);
|
|
1218
1222
|
const alleleDeletions = placeOffReference({
|
|
1219
1223
|
graph,
|
|
1220
|
-
span,
|
|
1224
|
+
span: span2,
|
|
1221
1225
|
rowY: (node) => (node.stable ? rows.get(node.stable.rank) ?? rows.size : rows.size) * ROW_HEIGHT_PX,
|
|
1222
1226
|
positions: nodePositions
|
|
1223
1227
|
});
|
|
@@ -1539,13 +1543,13 @@ function sampleRowLayout(graph, region, keep = []) {
|
|
|
1539
1543
|
if (backbone.length === 0 || samples.length === 0) {
|
|
1540
1544
|
return void 0;
|
|
1541
1545
|
}
|
|
1542
|
-
const
|
|
1546
|
+
const span2 = referenceSpan(backbone, region);
|
|
1543
1547
|
const rowOf = new Map(samples.map((s, i) => [s, i + 1]));
|
|
1544
1548
|
const nodePositions = backbonePositions(backbone);
|
|
1545
1549
|
const spareRow = samples.length + 1;
|
|
1546
1550
|
const alleleDeletions = placeOffReference({
|
|
1547
1551
|
graph,
|
|
1548
|
-
span,
|
|
1552
|
+
span: span2,
|
|
1549
1553
|
rowY: (node) => {
|
|
1550
1554
|
const stable = node.stable;
|
|
1551
1555
|
const row = stable ? rowOf.get(parsePanSN(stable.refName).sample) : void 0;
|
|
@@ -1594,9 +1598,18 @@ function pathColorsLegible(pathCount) {
|
|
|
1594
1598
|
function pathCssColor(index, count) {
|
|
1595
1599
|
return `hsl(${pathHueAt(index, count)}, ${PATH_SATURATION * 100}%, ${PATH_LIGHTNESS * 100}%)`;
|
|
1596
1600
|
}
|
|
1601
|
+
function pathGreyCssColor(index, count) {
|
|
1602
|
+
const lightness = count > 1 ? 25 + 45 * index / (count - 1) : 45;
|
|
1603
|
+
return `hsl(0, 0%, ${lightness}%)`;
|
|
1604
|
+
}
|
|
1597
1605
|
function labelTiers(name) {
|
|
1598
1606
|
const parts = pathOrigin(name).name.split("#");
|
|
1599
|
-
|
|
1607
|
+
const full = parts.join("#");
|
|
1608
|
+
if (parts.length >= 3 && isGenericSample(parts[0])) {
|
|
1609
|
+
const contig = parts.slice(2).join("#");
|
|
1610
|
+
return [contig, contig, full];
|
|
1611
|
+
}
|
|
1612
|
+
return [parts[0], parts.slice(0, 2).join("#"), full];
|
|
1600
1613
|
}
|
|
1601
1614
|
function groupDigits(n) {
|
|
1602
1615
|
return String(n).replace(/\B(?=(\d{3})+$)/g, ",");
|
|
@@ -1605,7 +1618,7 @@ function offsetTiers(name, tiers) {
|
|
|
1605
1618
|
const { start } = pathOrigin(name);
|
|
1606
1619
|
return tiers.map((t) => `${t} @${groupDigits(start)}`);
|
|
1607
1620
|
}
|
|
1608
|
-
function pathLegend(paths) {
|
|
1621
|
+
function pathLegend(paths, colors) {
|
|
1609
1622
|
const named = paths.map((p) => labelTiers(p.name));
|
|
1610
1623
|
const candidates = [0, 1, 2].flatMap((t) => [
|
|
1611
1624
|
paths.map((_, i) => named[i][t]),
|
|
@@ -1618,7 +1631,7 @@ function pathLegend(paths) {
|
|
|
1618
1631
|
return paths.map((path, i) => ({
|
|
1619
1632
|
name: path.name,
|
|
1620
1633
|
label: labels[i],
|
|
1621
|
-
color: pathCssColor(i, paths.length)
|
|
1634
|
+
color: colors?.[i] ?? pathCssColor(i, paths.length)
|
|
1622
1635
|
}));
|
|
1623
1636
|
}
|
|
1624
1637
|
|
|
@@ -1836,11 +1849,14 @@ function runTubeMap(graph) {
|
|
|
1836
1849
|
const paths = graph.paths ?? [];
|
|
1837
1850
|
const tracks = tubeMapTracks(graph);
|
|
1838
1851
|
const reads = graph.reads ? tubeMapReads(graph, graph.reads, paths.length) : [];
|
|
1839
|
-
|
|
1852
|
+
const tubeColor = reads.length > 0 ? pathGreyCssColor : pathCssColor;
|
|
1853
|
+
const pathColors = paths.map((_, i) => tubeColor(i, paths.length));
|
|
1854
|
+
const layout = layoutTubeMap(tubeMapNodes(graph), tracks, reads, {
|
|
1840
1855
|
nodeWidthOption: "compressed",
|
|
1841
1856
|
trackWidth: tubeWidth(tracks.length),
|
|
1842
|
-
trackColor: (track) => track.type === "read" ? readColor(track) :
|
|
1857
|
+
trackColor: (track) => track.type === "read" ? readColor(track) : pathColors[track.id]
|
|
1843
1858
|
});
|
|
1859
|
+
return layout ? { layout, pathColors } : void 0;
|
|
1844
1860
|
}
|
|
1845
1861
|
function drawnNodes(layout) {
|
|
1846
1862
|
const nodes = [];
|
|
@@ -1865,10 +1881,11 @@ function extentOf(layout, yOffset) {
|
|
|
1865
1881
|
};
|
|
1866
1882
|
}
|
|
1867
1883
|
function tubeMapLayout(graph) {
|
|
1868
|
-
const
|
|
1869
|
-
if (!
|
|
1884
|
+
const run = hasTubeMapPaths(graph) ? runTubeMap(graph) : void 0;
|
|
1885
|
+
if (!run) {
|
|
1870
1886
|
return void 0;
|
|
1871
1887
|
}
|
|
1888
|
+
const { layout, pathColors } = run;
|
|
1872
1889
|
const yOffset = layout.bounds.minY;
|
|
1873
1890
|
const nodePositions = {};
|
|
1874
1891
|
for (const node of drawnNodes(layout)) {
|
|
@@ -1882,7 +1899,7 @@ function tubeMapLayout(graph) {
|
|
|
1882
1899
|
const { minY, maxY } = extentOf(layout, yOffset);
|
|
1883
1900
|
return {
|
|
1884
1901
|
nodePositions,
|
|
1885
|
-
tubeMap: { layout, yOffset },
|
|
1902
|
+
tubeMap: { layout, yOffset, pathColors },
|
|
1886
1903
|
extent: {
|
|
1887
1904
|
minX: layout.bounds.minX,
|
|
1888
1905
|
maxX: layout.bounds.maxX,
|
|
@@ -1943,10 +1960,11 @@ function pathsReachBackbone(graph) {
|
|
|
1943
1960
|
return graph.nodes.some(isBackbone) && hasTubeMapPaths(graph);
|
|
1944
1961
|
}
|
|
1945
1962
|
function tubeMapReferenceLayout(graph) {
|
|
1946
|
-
const
|
|
1947
|
-
if (!
|
|
1963
|
+
const run = pathsReachBackbone(graph) ? runTubeMap(graph) : void 0;
|
|
1964
|
+
if (!run) {
|
|
1948
1965
|
return void 0;
|
|
1949
1966
|
}
|
|
1967
|
+
const { layout, pathColors } = run;
|
|
1950
1968
|
const columns = tubeMapColumns(graph, layout);
|
|
1951
1969
|
const byOrder = new Map(columns.map((c) => [c.order, c]));
|
|
1952
1970
|
const yOffset = layout.bounds.minY;
|
|
@@ -1957,7 +1975,7 @@ function tubeMapReferenceLayout(graph) {
|
|
|
1957
1975
|
}
|
|
1958
1976
|
return {
|
|
1959
1977
|
nodePositions,
|
|
1960
|
-
tubeMap: { layout, yOffset, columns },
|
|
1978
|
+
tubeMap: { layout, yOffset, columns, pathColors },
|
|
1961
1979
|
referenceAxis: true,
|
|
1962
1980
|
pixelRows: true,
|
|
1963
1981
|
extent: extentOf(layout, yOffset)
|
|
@@ -1984,7 +2002,7 @@ function sampleOf(path) {
|
|
|
1984
2002
|
function labelOf(path) {
|
|
1985
2003
|
return path.haplotype !== void 0 && path.sample !== void 0 ? `${path.sample}#${path.haplotype}` : sampleOf(path);
|
|
1986
2004
|
}
|
|
1987
|
-
function sliceBetween(nodeIds,
|
|
2005
|
+
function sliceBetween(nodeIds, span2, region, flanked = true) {
|
|
1988
2006
|
if (!region || !flanked) {
|
|
1989
2007
|
return { ids: nodeIds, complete: true };
|
|
1990
2008
|
}
|
|
@@ -1993,7 +2011,7 @@ function sliceBetween(nodeIds, span, region, flanked = true) {
|
|
|
1993
2011
|
let bestEnd = -Infinity;
|
|
1994
2012
|
let bestStart = Infinity;
|
|
1995
2013
|
nodeIds.forEach((id, i) => {
|
|
1996
|
-
const s =
|
|
2014
|
+
const s = span2.get(id);
|
|
1997
2015
|
if (s) {
|
|
1998
2016
|
if (s.end <= region.start && s.end > bestEnd) {
|
|
1999
2017
|
bestEnd = s.end;
|
|
@@ -2020,25 +2038,25 @@ function walkRows(graph, region, unit) {
|
|
|
2020
2038
|
const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
|
|
2021
2039
|
const referenceStart = graph.anchorPaths?.find((p) => p.name === pathOrigin(reference.name).name)?.start ?? 0;
|
|
2022
2040
|
const cut = region && region.end > region.start ? region : void 0;
|
|
2023
|
-
const
|
|
2041
|
+
const span2 = /* @__PURE__ */ new Map();
|
|
2024
2042
|
let pos = referenceStart;
|
|
2025
2043
|
for (const id of reference.nodeIds) {
|
|
2026
2044
|
const len = lengthOf.get(id) ?? 0;
|
|
2027
|
-
if (!
|
|
2028
|
-
|
|
2045
|
+
if (!span2.has(id)) {
|
|
2046
|
+
span2.set(id, { start: pos, end: pos + len });
|
|
2029
2047
|
}
|
|
2030
2048
|
pos += len;
|
|
2031
2049
|
}
|
|
2032
|
-
const flanked = cut === void 0 || [...
|
|
2050
|
+
const flanked = cut === void 0 || [...span2.values()].some((s) => s.end <= cut.start) && [...span2.values()].some((s) => s.start >= cut.end);
|
|
2033
2051
|
const rowOf = (path) => {
|
|
2034
|
-
const { ids, complete } = sliceBetween(path.nodeIds,
|
|
2052
|
+
const { ids, complete } = sliceBetween(path.nodeIds, span2, cut, flanked);
|
|
2035
2053
|
const runs = [];
|
|
2036
2054
|
let bp = 0;
|
|
2037
2055
|
let offReferenceBp = 0;
|
|
2038
2056
|
let step = 0;
|
|
2039
2057
|
for (const id of ids) {
|
|
2040
2058
|
const len = lengthOf.get(id) ?? 0;
|
|
2041
|
-
const s =
|
|
2059
|
+
const s = span2.get(id);
|
|
2042
2060
|
const last = runs.at(-1);
|
|
2043
2061
|
const at = last?.referenceStart;
|
|
2044
2062
|
const forward2 = s && at !== void 0 && step >= 0 && at + last.bp === s.start;
|
|
@@ -2429,8 +2447,8 @@ function spanOf(segments) {
|
|
|
2429
2447
|
}
|
|
2430
2448
|
return { min, max };
|
|
2431
2449
|
}
|
|
2432
|
-
function distanceToSpan(x,
|
|
2433
|
-
return Math.max(0,
|
|
2450
|
+
function distanceToSpan(x, span2) {
|
|
2451
|
+
return Math.max(0, span2.min - x, x - span2.max);
|
|
2434
2452
|
}
|
|
2435
2453
|
function facingSide(segments, other, tieBreak) {
|
|
2436
2454
|
const startGap = distanceToSpan(segments[0].x, other);
|
|
@@ -2465,10 +2483,10 @@ function bowAround(p1x, p1y, p2x, p2y, toward, maxBow) {
|
|
|
2465
2483
|
minAlong = Math.min(minAlong, along);
|
|
2466
2484
|
maxAlong = Math.max(maxAlong, along);
|
|
2467
2485
|
}
|
|
2468
|
-
const
|
|
2486
|
+
const span2 = toward.length ? maxAlong - minAlong : 0;
|
|
2469
2487
|
const uncapped = Math.max(
|
|
2470
2488
|
Math.abs(reach) * BOW_CLEARANCE / APEX_FRACTION,
|
|
2471
|
-
|
|
2489
|
+
span2 * ALONG_FRACTION
|
|
2472
2490
|
);
|
|
2473
2491
|
const size = maxBow === void 0 ? uncapped : Math.min(uncapped, maxBow);
|
|
2474
2492
|
return reach < 0 ? -size : size;
|
|
@@ -4022,6 +4040,123 @@ function wheelZoomFactor(e) {
|
|
|
4022
4040
|
return ZOOM_PER_NOTCH ** -Math.max(-MAX_NOTCHES_PER_EVENT, Math.min(MAX_NOTCHES_PER_EVENT, notches));
|
|
4023
4041
|
}
|
|
4024
4042
|
|
|
4043
|
+
// src/referenceStrip.ts
|
|
4044
|
+
import { abgrToCssRgba as abgrToCssRgba2 } from "@jbrowse/core/util/colorBits";
|
|
4045
|
+
var REFERENCE_STRIP_PX = 10;
|
|
4046
|
+
var REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8;
|
|
4047
|
+
var OVERHANG_PX = 5;
|
|
4048
|
+
function referenceStripBlocks(graph, {
|
|
4049
|
+
colorScheme,
|
|
4050
|
+
referenceRamp,
|
|
4051
|
+
walkNodes
|
|
4052
|
+
}) {
|
|
4053
|
+
const range = { ...computeColorSchemeRange(graph), referenceRamp };
|
|
4054
|
+
const out = [];
|
|
4055
|
+
graph.nodes.forEach((node, index) => {
|
|
4056
|
+
if (isBackbone(node)) {
|
|
4057
|
+
const own = getNodeColor(node, index, colorScheme, range);
|
|
4058
|
+
out.push({
|
|
4059
|
+
node: node.id,
|
|
4060
|
+
bp0: node.stable.start,
|
|
4061
|
+
bp1: node.stable.start + node.length,
|
|
4062
|
+
color: abgrToCssRgba2(
|
|
4063
|
+
walkNodes && !walkNodes.has(node.id) ? fadeAbgr(own, FADED_ALPHA) : own
|
|
4064
|
+
)
|
|
4065
|
+
});
|
|
4066
|
+
}
|
|
4067
|
+
});
|
|
4068
|
+
return out.sort((a, b) => a.bp0 - b.bp0);
|
|
4069
|
+
}
|
|
4070
|
+
function span(frame, bp0, bp1) {
|
|
4071
|
+
const a = bp0 * frame.scale + frame.translateX;
|
|
4072
|
+
const b = bp1 * frame.scale + frame.translateX;
|
|
4073
|
+
return a < b ? [a, b] : [b, a];
|
|
4074
|
+
}
|
|
4075
|
+
var HIT_SLOP_PX = 2;
|
|
4076
|
+
function stripOverhang(blocks, frame, width) {
|
|
4077
|
+
const a = -frame.translateX / frame.scale;
|
|
4078
|
+
const b = (width - frame.translateX) / frame.scale;
|
|
4079
|
+
const lo = Math.min(a, b);
|
|
4080
|
+
const hi = Math.max(a, b);
|
|
4081
|
+
let below = 0;
|
|
4082
|
+
let above = 0;
|
|
4083
|
+
for (const block of blocks) {
|
|
4084
|
+
below += Math.max(0, Math.min(block.bp1, lo) - block.bp0);
|
|
4085
|
+
above += Math.max(0, block.bp1 - Math.max(block.bp0, hi));
|
|
4086
|
+
}
|
|
4087
|
+
return frame.scale >= 0 ? { left: Math.round(below), right: Math.round(above) } : { left: Math.round(above), right: Math.round(below) };
|
|
4088
|
+
}
|
|
4089
|
+
function stripBlockAt(blocks, frame, sx, sy) {
|
|
4090
|
+
if (sy < 0 || sy > REFERENCE_STRIP_PX + HIT_SLOP_PX) {
|
|
4091
|
+
return void 0;
|
|
4092
|
+
}
|
|
4093
|
+
let hit;
|
|
4094
|
+
let best = HIT_SLOP_PX;
|
|
4095
|
+
for (const b of blocks) {
|
|
4096
|
+
const [x0, x1] = span(frame, b.bp0, b.bp1);
|
|
4097
|
+
const dist = Math.max(0, x0 - sx, sx - x1);
|
|
4098
|
+
if (dist <= best) {
|
|
4099
|
+
best = dist;
|
|
4100
|
+
hit = b.node;
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
return hit;
|
|
4104
|
+
}
|
|
4105
|
+
function drawOverhang(ctx, tipX, inward) {
|
|
4106
|
+
const mid = REFERENCE_STRIP_PX / 2;
|
|
4107
|
+
const baseX = tipX + inward * (OVERHANG_PX + 1);
|
|
4108
|
+
ctx.moveTo(tipX + inward, mid);
|
|
4109
|
+
ctx.lineTo(baseX, mid - OVERHANG_PX / 1.4);
|
|
4110
|
+
ctx.lineTo(baseX, mid + OVERHANG_PX / 1.4);
|
|
4111
|
+
ctx.closePath();
|
|
4112
|
+
}
|
|
4113
|
+
function drawReferenceStrip(ctx, blocks, frame, {
|
|
4114
|
+
width,
|
|
4115
|
+
lit,
|
|
4116
|
+
darkMode
|
|
4117
|
+
}) {
|
|
4118
|
+
const ink = darkMode ? "#ffffff" : "#18181c";
|
|
4119
|
+
ctx.fillStyle = darkMode ? "#1f1f1f" : "#ffffff";
|
|
4120
|
+
ctx.fillRect(0, 0, width, REFERENCE_STRIP_ZONE_PX);
|
|
4121
|
+
for (const b of blocks) {
|
|
4122
|
+
const [x0, x1] = span(frame, b.bp0, b.bp1);
|
|
4123
|
+
if (x1 >= 0 && x0 <= width) {
|
|
4124
|
+
ctx.fillStyle = b.color;
|
|
4125
|
+
ctx.fillRect(x0, 0, Math.max(x1 - x0, 1), REFERENCE_STRIP_PX);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
const overhang = stripOverhang(blocks, frame, width);
|
|
4129
|
+
if (overhang.left > 0 || overhang.right > 0) {
|
|
4130
|
+
ctx.beginPath();
|
|
4131
|
+
if (overhang.left > 0) {
|
|
4132
|
+
drawOverhang(ctx, 0, 1);
|
|
4133
|
+
}
|
|
4134
|
+
if (overhang.right > 0) {
|
|
4135
|
+
drawOverhang(ctx, width, -1);
|
|
4136
|
+
}
|
|
4137
|
+
ctx.fillStyle = ink;
|
|
4138
|
+
ctx.fill();
|
|
4139
|
+
ctx.strokeStyle = darkMode ? "#1f1f1f" : "#ffffff";
|
|
4140
|
+
ctx.lineWidth = 1;
|
|
4141
|
+
ctx.stroke();
|
|
4142
|
+
}
|
|
4143
|
+
if (lit) {
|
|
4144
|
+
const [x0, x1] = span(frame, lit.start, lit.end);
|
|
4145
|
+
const w = Math.max(x1 - x0, 3);
|
|
4146
|
+
const left = (x0 + x1) / 2 - w / 2;
|
|
4147
|
+
ctx.strokeStyle = ink;
|
|
4148
|
+
ctx.lineWidth = 1.5;
|
|
4149
|
+
ctx.strokeRect(left, 0.75, w, REFERENCE_STRIP_PX - 1.5);
|
|
4150
|
+
if (lit.anchor) {
|
|
4151
|
+
ctx.beginPath();
|
|
4152
|
+
ctx.moveTo(left + w / 2, REFERENCE_STRIP_PX);
|
|
4153
|
+
ctx.lineTo(lit.anchor.x, lit.anchor.y);
|
|
4154
|
+
ctx.lineWidth = 1;
|
|
4155
|
+
ctx.stroke();
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4025
4160
|
// src/tubeMap/draw.ts
|
|
4026
4161
|
import { curvePaths, nodeOutlinePath } from "@gmod/tubemap-core";
|
|
4027
4162
|
|
|
@@ -4259,16 +4394,23 @@ function drawTubeMap(ctx, picture, frame) {
|
|
|
4259
4394
|
}
|
|
4260
4395
|
var MISMATCH_FONT_PX = 12;
|
|
4261
4396
|
var MIN_MISMATCH_FONT_PX = 6;
|
|
4262
|
-
function
|
|
4263
|
-
|
|
4264
|
-
|
|
4397
|
+
function mismatchesLegible(yScale) {
|
|
4398
|
+
return MISMATCH_FONT_PX * yScale >= MIN_MISMATCH_FONT_PX;
|
|
4399
|
+
}
|
|
4400
|
+
function mismatchOnScreen(m, { x, width }) {
|
|
4401
|
+
const [x0, x1] = m.kind === "insertion" ? [m.x, m.x] : [m.x0, m.x1];
|
|
4402
|
+
return x(x1) >= 0 && x(x0) <= width;
|
|
4403
|
+
}
|
|
4404
|
+
function drawMismatches(ctx, marks, frame) {
|
|
4405
|
+
const { x, y, yScale, darkMode } = frame;
|
|
4406
|
+
if (!mismatchesLegible(yScale) || marks.length === 0) {
|
|
4265
4407
|
return;
|
|
4266
4408
|
}
|
|
4267
|
-
const
|
|
4409
|
+
const fontPx = MISMATCH_FONT_PX * yScale;
|
|
4268
4410
|
ctx.fillStyle = "grey";
|
|
4269
4411
|
ctx.beginPath();
|
|
4270
4412
|
for (const m of marks) {
|
|
4271
|
-
if (m.kind === "deletion" &&
|
|
4413
|
+
if (m.kind === "deletion" && mismatchOnScreen(m, frame)) {
|
|
4272
4414
|
const left = x(m.x0);
|
|
4273
4415
|
const top = y(m.y);
|
|
4274
4416
|
ctx.rect(left, top, x(m.x1) - left, y(m.y + m.height) - top);
|
|
@@ -4279,9 +4421,12 @@ function drawMismatches(ctx, marks, { x, y, yScale, width, darkMode }) {
|
|
|
4279
4421
|
ctx.textBaseline = "alphabetic";
|
|
4280
4422
|
ctx.fillStyle = darkMode ? "#ffffff" : "#000000";
|
|
4281
4423
|
for (const m of marks) {
|
|
4282
|
-
if (
|
|
4424
|
+
if (!mismatchOnScreen(m, frame)) {
|
|
4425
|
+
continue;
|
|
4426
|
+
}
|
|
4427
|
+
if (m.kind === "substitution") {
|
|
4283
4428
|
ctx.fillText(m.seq, x(m.x0) + 1, y(m.y + m.height));
|
|
4284
|
-
} else if (m.kind === "insertion"
|
|
4429
|
+
} else if (m.kind === "insertion") {
|
|
4285
4430
|
ctx.fillText("*", x(m.x) - 3, y(m.y + m.height));
|
|
4286
4431
|
}
|
|
4287
4432
|
}
|
|
@@ -4352,8 +4497,8 @@ function warpX(knots, tx) {
|
|
|
4352
4497
|
}
|
|
4353
4498
|
const a = knots[lo];
|
|
4354
4499
|
const b = knots[hi];
|
|
4355
|
-
const
|
|
4356
|
-
return
|
|
4500
|
+
const span2 = b.tx - a.tx;
|
|
4501
|
+
return span2 > 0 ? a.sx + (tx - a.tx) / span2 * (b.sx - a.sx) : a.sx;
|
|
4357
4502
|
}
|
|
4358
4503
|
|
|
4359
4504
|
// src/tubeMap/frame.ts
|
|
@@ -4746,8 +4891,8 @@ function tubeX(boxes, bp, isEnd = false) {
|
|
|
4746
4891
|
if (bp <= box.bp0) {
|
|
4747
4892
|
return box.x0;
|
|
4748
4893
|
}
|
|
4749
|
-
const
|
|
4750
|
-
return
|
|
4894
|
+
const span2 = box.bp1 - box.bp0;
|
|
4895
|
+
return span2 > 0 ? box.x0 + (Math.min(bp, box.bp1) - box.bp0) / span2 * (box.x1 - box.x0) : box.x0;
|
|
4751
4896
|
}
|
|
4752
4897
|
function tubeSpan(boxes, start, end) {
|
|
4753
4898
|
const a = tubeX(boxes, start);
|
|
@@ -4766,8 +4911,7 @@ function rulerBoxes(byRefName) {
|
|
|
4766
4911
|
var TICK_PX = 5;
|
|
4767
4912
|
var LABEL_GAP_PX2 = 10;
|
|
4768
4913
|
var TARGET_TICK_PX = 110;
|
|
4769
|
-
var
|
|
4770
|
-
var ZIGZAG_AMPLITUDE_PX = 2.5;
|
|
4914
|
+
var MIN_BRACKET_PX = 3;
|
|
4771
4915
|
function rulerInk(darkMode) {
|
|
4772
4916
|
return darkMode ? "#b0b0b8" : "#55555c";
|
|
4773
4917
|
}
|
|
@@ -4776,7 +4920,18 @@ function niceStep(raw) {
|
|
|
4776
4920
|
const m = raw / pow;
|
|
4777
4921
|
return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow;
|
|
4778
4922
|
}
|
|
4779
|
-
function
|
|
4923
|
+
function placeLabels2(candidates) {
|
|
4924
|
+
const placed = [];
|
|
4925
|
+
for (const a of candidates) {
|
|
4926
|
+
if (placed.every(
|
|
4927
|
+
(b) => Math.abs(a.at - b.at) >= (a.width + b.width) / 2 + LABEL_GAP_PX2
|
|
4928
|
+
)) {
|
|
4929
|
+
placed.push(a);
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
return placed.map(({ text, at }) => ({ text, at }));
|
|
4933
|
+
}
|
|
4934
|
+
function scaleMarks(boxes, x, width, measure) {
|
|
4780
4935
|
const first = boxes[0];
|
|
4781
4936
|
const last = boxes.at(-1);
|
|
4782
4937
|
if (!first || !last) {
|
|
@@ -4789,31 +4944,9 @@ function rulerMarks(boxes, x, width, measure) {
|
|
|
4789
4944
|
return void 0;
|
|
4790
4945
|
}
|
|
4791
4946
|
const step = niceStep(bpSpan / (to - from) * TARGET_TICK_PX);
|
|
4792
|
-
const squeezedBoxes = boxes.filter((box) => {
|
|
4793
|
-
const bp = box.bp1 - box.bp0;
|
|
4794
|
-
return bp >= step && (x(box.x1) - x(box.x0)) * step / bp < TARGET_TICK_PX / 2;
|
|
4795
|
-
});
|
|
4796
|
-
const squeezed = [];
|
|
4797
|
-
const lengths = [];
|
|
4798
|
-
for (const box of squeezedBoxes) {
|
|
4799
|
-
const x0 = x(box.x0);
|
|
4800
|
-
const x1 = x(box.x1);
|
|
4801
|
-
if (x1 >= 0 && x0 <= width) {
|
|
4802
|
-
squeezed.push({ x0, x1 });
|
|
4803
|
-
const text = formatBp(box.bp1 - box.bp0);
|
|
4804
|
-
lengths.push({ text, at: (x0 + x1) / 2, width: measure(text) });
|
|
4805
|
-
}
|
|
4806
|
-
}
|
|
4807
4947
|
const ticks = [];
|
|
4808
4948
|
const positions = [];
|
|
4809
|
-
let k = 0;
|
|
4810
4949
|
for (let bp = Math.ceil(first.bp0 / step) * step; bp <= last.bp1; bp += step) {
|
|
4811
|
-
while (k < squeezedBoxes.length && squeezedBoxes[k].bp1 <= bp) {
|
|
4812
|
-
k++;
|
|
4813
|
-
}
|
|
4814
|
-
if (k < squeezedBoxes.length && squeezedBoxes[k].bp0 < bp) {
|
|
4815
|
-
continue;
|
|
4816
|
-
}
|
|
4817
4950
|
const sx = Math.round(x(tubeX(boxes, bp))) + 0.5;
|
|
4818
4951
|
if (sx >= 0 && sx <= width) {
|
|
4819
4952
|
ticks.push(sx);
|
|
@@ -4821,35 +4954,66 @@ function rulerMarks(boxes, x, width, measure) {
|
|
|
4821
4954
|
positions.push({ text, at: sx, width: measure(text) });
|
|
4822
4955
|
}
|
|
4823
4956
|
}
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4957
|
+
return {
|
|
4958
|
+
spans: [{ x0: from, x1: to }],
|
|
4959
|
+
ticks,
|
|
4960
|
+
labels: placeLabels2(positions)
|
|
4961
|
+
};
|
|
4962
|
+
}
|
|
4963
|
+
function boxMarks(boxes, x, width, measure) {
|
|
4964
|
+
const shown = boxes.map((box) => ({ box, s0: x(box.x0), s1: x(box.x1) })).filter(({ s0, s1 }) => s1 >= 0 && s0 <= width);
|
|
4965
|
+
const first = shown[0];
|
|
4966
|
+
const last = shown.at(-1);
|
|
4967
|
+
if (!first || !last) {
|
|
4968
|
+
return void 0;
|
|
4969
|
+
}
|
|
4970
|
+
const label = (text, at) => ({
|
|
4971
|
+
text,
|
|
4972
|
+
at,
|
|
4973
|
+
width: measure(text)
|
|
4974
|
+
});
|
|
4975
|
+
const position = (bp, at) => label(bp.toLocaleString("en-US"), at);
|
|
4976
|
+
const spans = [];
|
|
4977
|
+
const ticks = [];
|
|
4978
|
+
const lengths = [];
|
|
4979
|
+
const boundaries = [];
|
|
4980
|
+
for (const { box, s0, s1 } of shown) {
|
|
4981
|
+
spans.push({ x0: s0, x1: s1 });
|
|
4982
|
+
ticks.push(Math.round(s0) + 0.5);
|
|
4983
|
+
if (s1 - s0 >= MIN_BRACKET_PX) {
|
|
4984
|
+
ticks.push(Math.round(s1) + 0.5);
|
|
4985
|
+
}
|
|
4986
|
+
const length = label(formatBp(box.bp1 - box.bp0), (s0 + s1) / 2);
|
|
4987
|
+
if (length.width <= s1 - s0) {
|
|
4988
|
+
lengths.push(length);
|
|
4989
|
+
}
|
|
4990
|
+
if (box !== first.box) {
|
|
4991
|
+
boundaries.push(position(box.bp0, s0));
|
|
4831
4992
|
}
|
|
4832
4993
|
}
|
|
4994
|
+
const ends = [
|
|
4995
|
+
position(first.box.bp0, first.s0),
|
|
4996
|
+
position(last.box.bp1, last.s1)
|
|
4997
|
+
];
|
|
4833
4998
|
return {
|
|
4834
|
-
|
|
4835
|
-
to,
|
|
4999
|
+
spans,
|
|
4836
5000
|
ticks,
|
|
4837
|
-
|
|
4838
|
-
labels: placed.map(({ text, at }) => ({ text, at }))
|
|
5001
|
+
labels: placeLabels2([...ends, ...lengths, ...boundaries])
|
|
4839
5002
|
};
|
|
4840
5003
|
}
|
|
4841
|
-
function
|
|
4842
|
-
|
|
4843
|
-
const dx = (x1 - x0) / teeth;
|
|
4844
|
-
for (let i = 0; i < teeth; i++) {
|
|
4845
|
-
ctx.lineTo(x0 + (i + 0.5) * dx, y + (i % 2 ? 1 : -1) * ZIGZAG_AMPLITUDE_PX);
|
|
4846
|
-
}
|
|
4847
|
-
ctx.lineTo(x1, y);
|
|
5004
|
+
function rulerMarks(boxes, x, width, measure, referenceAxis = false) {
|
|
5005
|
+
return (referenceAxis ? scaleMarks : boxMarks)(boxes, x, width, measure);
|
|
4848
5006
|
}
|
|
4849
|
-
function drawTubeMapRuler(ctx, boxes, frame, top) {
|
|
5007
|
+
function drawTubeMapRuler(ctx, boxes, frame, top, referenceAxis = false) {
|
|
4850
5008
|
const { x, width, darkMode } = frame;
|
|
4851
5009
|
ctx.font = "10px sans-serif";
|
|
4852
|
-
const marks = rulerMarks(
|
|
5010
|
+
const marks = rulerMarks(
|
|
5011
|
+
boxes,
|
|
5012
|
+
x,
|
|
5013
|
+
width,
|
|
5014
|
+
(text) => ctx.measureText(text).width,
|
|
5015
|
+
referenceAxis
|
|
5016
|
+
);
|
|
4853
5017
|
if (!marks) {
|
|
4854
5018
|
return;
|
|
4855
5019
|
}
|
|
@@ -4861,12 +5025,10 @@ function drawTubeMapRuler(ctx, boxes, frame, top) {
|
|
|
4861
5025
|
ctx.textAlign = "center";
|
|
4862
5026
|
ctx.textBaseline = "top";
|
|
4863
5027
|
ctx.beginPath();
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
ctx.lineTo(
|
|
4867
|
-
zigzag(ctx, x0, x1, y);
|
|
5028
|
+
for (const { x0, x1 } of marks.spans) {
|
|
5029
|
+
ctx.moveTo(Math.max(0, x0), y);
|
|
5030
|
+
ctx.lineTo(Math.min(width, x1), y);
|
|
4868
5031
|
}
|
|
4869
|
-
ctx.lineTo(Math.min(width, marks.to), y);
|
|
4870
5032
|
for (const sx of marks.ticks) {
|
|
4871
5033
|
ctx.moveTo(sx, top);
|
|
4872
5034
|
ctx.lineTo(sx, top + TICK_PX);
|
|
@@ -4978,14 +5140,14 @@ function tubeMapConnectors(nodes, bpToScreen, tubeToScreen, width) {
|
|
|
4978
5140
|
}
|
|
4979
5141
|
return out;
|
|
4980
5142
|
}
|
|
4981
|
-
var
|
|
5143
|
+
var HIT_SLOP_PX2 = 3;
|
|
4982
5144
|
function connectorAt(connectors, zoneBottom, sx, sy) {
|
|
4983
5145
|
if (zoneBottom <= 0 || sy < 0 || sy > zoneBottom) {
|
|
4984
5146
|
return void 0;
|
|
4985
5147
|
}
|
|
4986
5148
|
const t = sy / zoneBottom;
|
|
4987
5149
|
let hit;
|
|
4988
|
-
let best =
|
|
5150
|
+
let best = HIT_SLOP_PX2;
|
|
4989
5151
|
for (const c of connectors) {
|
|
4990
5152
|
const a = c.top0 + (c.bottom0 - c.top0) * t;
|
|
4991
5153
|
const b = c.top1 + (c.bottom1 - c.top1) * t;
|
|
@@ -5220,10 +5382,10 @@ function walkRoutes(graph, byId, walkIndex, startId, endId) {
|
|
|
5220
5382
|
// src/bubbles/classifyBubble.ts
|
|
5221
5383
|
var REPEAT_GROWTH = 10;
|
|
5222
5384
|
function coveringRepeat(b, repeats) {
|
|
5223
|
-
const
|
|
5385
|
+
const span2 = b.end - b.start;
|
|
5224
5386
|
const growth = b.longestAlleleLength - b.shortestAlleleLength;
|
|
5225
5387
|
return repeats.find(
|
|
5226
|
-
(r) => growth <= REPEAT_GROWTH * (r.end - r.start) && (
|
|
5388
|
+
(r) => growth <= REPEAT_GROWTH * (r.end - r.start) && (span2 === 0 ? r.start <= b.start && b.start <= r.end : Math.min(b.end, r.end) - Math.max(b.start, r.start) >= span2 / 2)
|
|
5227
5389
|
);
|
|
5228
5390
|
}
|
|
5229
5391
|
var SUPERBUBBLE_SEGMENTS = 40;
|
|
@@ -5461,12 +5623,16 @@ function layoutLabels(m) {
|
|
|
5461
5623
|
const reserved = m.drawnRowLabels.map(
|
|
5462
5624
|
({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
|
|
5463
5625
|
);
|
|
5626
|
+
const stripPx = m.referenceStripZonePx ?? 0;
|
|
5627
|
+
if (stripPx > 0) {
|
|
5628
|
+
reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
|
|
5629
|
+
}
|
|
5464
5630
|
if (m.legendSize.width > 0) {
|
|
5465
5631
|
reserved.push({
|
|
5466
5632
|
x0: width - LEGEND_INSET_PX - m.legendSize.width,
|
|
5467
5633
|
x1: width,
|
|
5468
5634
|
y0: 0,
|
|
5469
|
-
y1: LEGEND_INSET_PX + m.legendSize.height
|
|
5635
|
+
y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
|
|
5470
5636
|
});
|
|
5471
5637
|
}
|
|
5472
5638
|
if (m.poppedFrom) {
|
|
@@ -5670,10 +5836,9 @@ var WELL_KNOWN_SAMPLES = /* @__PURE__ */ new Map([
|
|
|
5670
5836
|
function wellKnownSample(assemblyName) {
|
|
5671
5837
|
return WELL_KNOWN_SAMPLES.get(assemblyName.toLowerCase());
|
|
5672
5838
|
}
|
|
5673
|
-
var GENERIC_SAMPLES = /* @__PURE__ */ new Set(["_gbwt_ref"]);
|
|
5674
5839
|
function sampleOf2(refName) {
|
|
5675
5840
|
const sample = refName.includes("#") ? panSNSample(refName) : void 0;
|
|
5676
|
-
return sample === void 0 ||
|
|
5841
|
+
return sample === void 0 || isGenericSample(sample) ? void 0 : sample;
|
|
5677
5842
|
}
|
|
5678
5843
|
function haplotypeOf(refName) {
|
|
5679
5844
|
return panSNHaplotype(refName) ?? panSNSample(refName);
|
|
@@ -5720,10 +5885,10 @@ function graphBackbone(graph) {
|
|
|
5720
5885
|
if (isBackbone(node)) {
|
|
5721
5886
|
const { refName, start } = node.stable;
|
|
5722
5887
|
const end = start + node.length;
|
|
5723
|
-
const
|
|
5724
|
-
if (
|
|
5725
|
-
|
|
5726
|
-
|
|
5888
|
+
const span2 = spans.get(refName);
|
|
5889
|
+
if (span2) {
|
|
5890
|
+
span2.start = Math.min(span2.start, start);
|
|
5891
|
+
span2.end = Math.max(span2.end, end);
|
|
5727
5892
|
} else {
|
|
5728
5893
|
spans.set(refName, {
|
|
5729
5894
|
refName,
|
|
@@ -5931,6 +6096,7 @@ export {
|
|
|
5931
6096
|
LEGEND_INSET_PX,
|
|
5932
6097
|
NODE_WIDTHS,
|
|
5933
6098
|
REFERENCE_RAMP_MAX_HUE,
|
|
6099
|
+
REFERENCE_STRIP_ZONE_PX,
|
|
5934
6100
|
ROW_HEIGHT_PX,
|
|
5935
6101
|
WELL_KNOWN_SAMPLES,
|
|
5936
6102
|
assemblyWalk,
|
|
@@ -5948,6 +6114,7 @@ export {
|
|
|
5948
6114
|
contains,
|
|
5949
6115
|
cutWindowGFA,
|
|
5950
6116
|
deletionEdges,
|
|
6117
|
+
drawReferenceStrip,
|
|
5951
6118
|
drawTubeMap,
|
|
5952
6119
|
drawTubeMapConnectors,
|
|
5953
6120
|
drawTubeMapGenes,
|
|
@@ -5987,10 +6154,12 @@ export {
|
|
|
5987
6154
|
referenceNodes,
|
|
5988
6155
|
referencePathQuery,
|
|
5989
6156
|
referenceSamplesOf,
|
|
6157
|
+
referenceStripBlocks,
|
|
5990
6158
|
resolveColorScheme,
|
|
5991
6159
|
resolveReferenceSample,
|
|
5992
6160
|
rulerBoxes,
|
|
5993
6161
|
screenToLayout,
|
|
6162
|
+
stripBlockAt,
|
|
5994
6163
|
tubeMapConnectors,
|
|
5995
6164
|
tubeMapFrame,
|
|
5996
6165
|
tubeMapGenes,
|
|
@@ -19,6 +19,7 @@ export interface BubbleRoute {
|
|
|
19
19
|
walks: string[];
|
|
20
20
|
}
|
|
21
21
|
export declare function parseBubbleLine(line: string): MinigraphBubble;
|
|
22
|
+
export declare function sameBubble(a: MinigraphBubble, b: MinigraphBubble): boolean;
|
|
22
23
|
export declare function bubbleLabel(bubble: MinigraphBubble): string;
|
|
23
24
|
export declare const SATURATED_PATH_COUNT = 2147483647;
|
|
24
25
|
export declare function bubbleDescription(bubble: MinigraphBubble): string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export { findHoveredEdge, findHoveredNode } from './util/hitDetection.js';
|
|
|
16
16
|
export type { NodeInk } from './util/hitDetection.js';
|
|
17
17
|
export type { AxisScale } from './util/geometry.js';
|
|
18
18
|
export { wheelZoomFactor } from './util/wheelZoom.js';
|
|
19
|
+
export { REFERENCE_STRIP_ZONE_PX, drawReferenceStrip, referenceStripBlocks, stripBlockAt, } from './referenceStrip.js';
|
|
20
|
+
export type { StripBlock } from './referenceStrip.js';
|
|
19
21
|
export { drawTubeMap, tubeMapPicture } from './tubeMap/draw.js';
|
|
20
22
|
export type { TubeMapFrame, TubeMapPicture } from './tubeMap/draw.js';
|
|
21
23
|
export { referenceKnots, warpX } from './tubeMap/warp.js';
|
package/dist/types/pansn.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
2
|
export declare function panSNSample(refName: string): string;
|
|
3
|
+
export declare function isGenericSample(sample: string): boolean;
|
|
3
4
|
export declare function panSNHaplotype(refName: string): string | undefined;
|
|
4
5
|
export declare function panSNContig(refName: string): string;
|
|
5
6
|
export declare function panSNMatchesPrefix(refName: string, prefix: string | undefined): boolean;
|
|
@@ -6,9 +6,10 @@ export declare const PATH_LIGHTNESS = 0.5;
|
|
|
6
6
|
export declare const MAX_PATH_COLORS = 16;
|
|
7
7
|
export declare function pathColorsLegible(pathCount: number): boolean;
|
|
8
8
|
export declare function pathCssColor(index: number, count: number): string;
|
|
9
|
+
export declare function pathGreyCssColor(index: number, count: number): string;
|
|
9
10
|
export interface PathLegendEntry {
|
|
10
11
|
name: string;
|
|
11
12
|
label: string;
|
|
12
13
|
color: string;
|
|
13
14
|
}
|
|
14
|
-
export declare function pathLegend(paths: GraphPath[]): PathLegendEntry[];
|
|
15
|
+
export declare function pathLegend(paths: GraphPath[], colors?: readonly string[]): PathLegendEntry[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ResolvedColorScheme } from './colorSchemes.js';
|
|
2
|
+
import type { ReferenceRamp } from './renderer/GeometryBuilder.js';
|
|
3
|
+
import type { Graph, NodeSegment } from './types.js';
|
|
4
|
+
export declare const REFERENCE_STRIP_PX = 10;
|
|
5
|
+
export declare const REFERENCE_STRIP_ZONE_PX: number;
|
|
6
|
+
export interface StripBlock {
|
|
7
|
+
node: string;
|
|
8
|
+
bp0: number;
|
|
9
|
+
bp1: number;
|
|
10
|
+
color: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function referenceStripBlocks(graph: Graph, { colorScheme, referenceRamp, walkNodes, }: {
|
|
13
|
+
colorScheme: ResolvedColorScheme;
|
|
14
|
+
referenceRamp?: ReferenceRamp;
|
|
15
|
+
walkNodes?: ReadonlySet<string>;
|
|
16
|
+
}): StripBlock[];
|
|
17
|
+
export interface BpFrame {
|
|
18
|
+
scale: number;
|
|
19
|
+
translateX: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function stripOverhang(blocks: readonly StripBlock[], frame: BpFrame, width: number): {
|
|
22
|
+
left: number;
|
|
23
|
+
right: number;
|
|
24
|
+
};
|
|
25
|
+
export declare function stripBlockAt(blocks: readonly StripBlock[], frame: BpFrame, sx: number, sy: number): string | undefined;
|
|
26
|
+
export declare function nodeAnchor(segments: readonly NodeSegment[] | undefined, toScreen: (p: NodeSegment) => {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
}): {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
} | undefined;
|
|
33
|
+
export interface StripLit {
|
|
34
|
+
start: number;
|
|
35
|
+
end: number;
|
|
36
|
+
anchor?: {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export declare function drawReferenceStrip(ctx: CanvasRenderingContext2D, blocks: readonly StripBlock[], frame: BpFrame, { width, lit, darkMode, }: {
|
|
42
|
+
width: number;
|
|
43
|
+
lit?: StripLit;
|
|
44
|
+
darkMode?: boolean;
|
|
45
|
+
}): void;
|
|
@@ -4,6 +4,7 @@ import type { Graph, GraphNode, NodeSegment } from '../types.js';
|
|
|
4
4
|
import type { WalkHighlight } from '../walkHighlight.js';
|
|
5
5
|
import type { RenderBatch } from './types.js';
|
|
6
6
|
import type { AxisScale, BezierCurve } from '../util/geometry.js';
|
|
7
|
+
export declare const FADED_ALPHA = 0.18;
|
|
7
8
|
export interface BuildOptions {
|
|
8
9
|
nodePositions: Record<string, NodeSegment[]>;
|
|
9
10
|
graph: Graph;
|
|
@@ -15,21 +15,17 @@ export declare function tubeSpan(boxes: readonly Box[], start: number, end: numb
|
|
|
15
15
|
x1: number;
|
|
16
16
|
};
|
|
17
17
|
export declare function rulerBoxes(byRefName: ReferenceBoxes): Box[] | undefined;
|
|
18
|
-
export declare const ZIGZAG_PX = 4;
|
|
19
|
-
export declare const ZIGZAG_AMPLITUDE_PX = 2.5;
|
|
20
18
|
export declare function rulerInk(darkMode?: boolean): "#b0b0b8" | "#55555c";
|
|
21
19
|
export interface RulerMarks {
|
|
22
|
-
|
|
23
|
-
to: number;
|
|
24
|
-
ticks: number[];
|
|
25
|
-
squeezed: {
|
|
20
|
+
spans: {
|
|
26
21
|
x0: number;
|
|
27
22
|
x1: number;
|
|
28
23
|
}[];
|
|
24
|
+
ticks: number[];
|
|
29
25
|
labels: {
|
|
30
26
|
text: string;
|
|
31
27
|
at: number;
|
|
32
28
|
}[];
|
|
33
29
|
}
|
|
34
|
-
export declare function rulerMarks(boxes: readonly Box[], x: (tx: number) => number, width: number, measure: (text: string) => number): RulerMarks | undefined;
|
|
35
|
-
export declare function drawTubeMapRuler(ctx: CanvasRenderingContext2D, boxes: readonly Box[], frame: TubeMapFrame, top: number): void;
|
|
30
|
+
export declare function rulerMarks(boxes: readonly Box[], x: (tx: number) => number, width: number, measure: (text: string) => number, referenceAxis?: boolean): RulerMarks | undefined;
|
|
31
|
+
export declare function drawTubeMapRuler(ctx: CanvasRenderingContext2D, boxes: readonly Box[], frame: TubeMapFrame, top: number, referenceAxis?: boolean): void;
|
|
@@ -64,4 +64,6 @@ export interface TubeMapFrame extends TubeMapTransform {
|
|
|
64
64
|
darkMode?: boolean;
|
|
65
65
|
}
|
|
66
66
|
export declare function drawTubeMap(ctx: CanvasRenderingContext2D, picture: TubeMapPicture, frame: TubeMapFrame): void;
|
|
67
|
+
export declare function mismatchesLegible(yScale: number): boolean;
|
|
68
|
+
export declare function mismatchOnScreen(m: TubeMapMismatch, { x, width }: Pick<TubeMapFrame, 'x' | 'width'>): boolean;
|
|
67
69
|
export {};
|
|
@@ -4,6 +4,8 @@ import type { InputTrack } from '@gmod/tubemap-core';
|
|
|
4
4
|
export declare function canonicalStrand(node: GraphNode): "+" | "-";
|
|
5
5
|
export declare function stepName(node: GraphNode, strand: '+' | '-'): string;
|
|
6
6
|
export declare function tubeMapReads(graph: Graph, records: readonly GafRecord[], firstId: number): InputTrack[];
|
|
7
|
+
export declare const FORWARD_READ_COLORS: string[];
|
|
8
|
+
export declare const REVERSE_READ_COLORS: string[];
|
|
7
9
|
export declare function readColor(read: {
|
|
8
10
|
id: number;
|
|
9
11
|
is_reverse?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/bandage-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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": {
|