@jbrowse/bandage-core 6.0.1 → 6.0.2

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.
Files changed (162) hide show
  1. package/README.md +23 -7
  2. package/dist/alleleProjection/projectAlleles.js +10 -0
  3. package/dist/anchoredNodes.js +24 -0
  4. package/dist/bubbleSpreads.js +11 -0
  5. package/dist/bubbles/bubbleHalos.js +11 -0
  6. package/dist/bubbles/bubbleLine.js +14 -0
  7. package/dist/bubbles/bubblesFromGraph.js +10 -0
  8. package/dist/bubbles/classifyBubble.js +15 -0
  9. package/dist/bubbles/popBubble.js +9 -0
  10. package/dist/chunks/chunk-3C2LGJKY.js +108 -0
  11. package/dist/chunks/chunk-3PLYZLYV.js +513 -0
  12. package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
  13. package/dist/chunks/chunk-3WHKR5TY.js +726 -0
  14. package/dist/chunks/chunk-47DYCZOK.js +115 -0
  15. package/dist/chunks/chunk-4S47XRYA.js +149 -0
  16. package/dist/chunks/chunk-527AI7CP.js +122 -0
  17. package/dist/chunks/chunk-57ZIXHZA.js +90 -0
  18. package/dist/chunks/chunk-5QT6NPSO.js +40 -0
  19. package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
  20. package/dist/chunks/chunk-6Q6HFBJT.js +170 -0
  21. package/dist/chunks/chunk-6SBUFFTB.js +97 -0
  22. package/dist/chunks/chunk-6YRRNX2P.js +61 -0
  23. package/dist/chunks/chunk-72N5DJHZ.js +155 -0
  24. package/dist/chunks/chunk-73RSLGZT.js +73 -0
  25. package/dist/chunks/chunk-7CHJKABZ.js +82 -0
  26. package/dist/chunks/chunk-7XHHVYG7.js +13 -0
  27. package/dist/chunks/chunk-AB35TJST.js +67 -0
  28. package/dist/chunks/chunk-AE3UQZY3.js +43 -0
  29. package/dist/chunks/chunk-BJV2W4KZ.js +257 -0
  30. package/dist/chunks/chunk-BPLIXMH7.js +52 -0
  31. package/dist/chunks/chunk-BXEPA4KS.js +179 -0
  32. package/dist/chunks/chunk-C44YRP5F.js +85 -0
  33. package/dist/chunks/chunk-CDLPYR5O.js +135 -0
  34. package/dist/chunks/chunk-CPMHA4W5.js +6 -0
  35. package/dist/chunks/chunk-CR7EGAFE.js +241 -0
  36. package/dist/chunks/chunk-CZ54VG2H.js +431 -0
  37. package/dist/chunks/chunk-D3M7SBKL.js +210 -0
  38. package/dist/chunks/chunk-EJ5OZTCK.js +52 -0
  39. package/dist/chunks/chunk-ES2N6V56.js +83 -0
  40. package/dist/chunks/chunk-EZPBJPK6.js +54 -0
  41. package/dist/chunks/chunk-FYQWPEEY.js +65 -0
  42. package/dist/chunks/chunk-GQ2AOHU4.js +112 -0
  43. package/dist/chunks/chunk-HB6PDCOU.js +177 -0
  44. package/dist/chunks/chunk-HCANBJDD.js +326 -0
  45. package/dist/chunks/chunk-HIJVRRYE.js +174 -0
  46. package/dist/chunks/chunk-HNQPEMYE.js +297 -0
  47. package/dist/chunks/chunk-IGVV37VI.js +60 -0
  48. package/dist/chunks/chunk-IMW3AK27.js +102 -0
  49. package/dist/chunks/chunk-JKJWSY4V.js +32 -0
  50. package/dist/chunks/chunk-KCW7L3NV.js +6 -0
  51. package/dist/chunks/chunk-KQFOCDFY.js +73 -0
  52. package/dist/chunks/chunk-L3O7AWLI.js +52 -0
  53. package/dist/chunks/chunk-L5RYUPZI.js +53 -0
  54. package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
  55. package/dist/chunks/chunk-LTUWJKHD.js +112 -0
  56. package/dist/chunks/chunk-LVZZH2NI.js +181 -0
  57. package/dist/chunks/chunk-MFKORG3F.js +44 -0
  58. package/dist/chunks/chunk-MHGHYHTL.js +205 -0
  59. package/dist/chunks/chunk-MI35EUIE.js +76 -0
  60. package/dist/chunks/chunk-MNUSQCLG.js +29 -0
  61. package/dist/chunks/chunk-NNODIW4U.js +26 -0
  62. package/dist/chunks/chunk-P7B3GDAY.js +232 -0
  63. package/dist/chunks/chunk-PBENCR5L.js +60 -0
  64. package/dist/chunks/chunk-PUG7LXDF.js +70 -0
  65. package/dist/chunks/chunk-Q3KCTA4N.js +202 -0
  66. package/dist/chunks/chunk-QCYM44UL.js +476 -0
  67. package/dist/chunks/chunk-QYBZZI5H.js +0 -0
  68. package/dist/chunks/chunk-RAKOB6IV.js +169 -0
  69. package/dist/chunks/chunk-RVS2QCOX.js +135 -0
  70. package/dist/chunks/chunk-SDGM243E.js +190 -0
  71. package/dist/chunks/chunk-ST2IBUAN.js +119 -0
  72. package/dist/chunks/chunk-SV27AJCC.js +101 -0
  73. package/dist/chunks/chunk-SZNZ2T6D.js +198 -0
  74. package/dist/chunks/chunk-TDQLINVI.js +16 -0
  75. package/dist/chunks/chunk-TEDQJPMI.js +131 -0
  76. package/dist/chunks/chunk-TZFWUT2C.js +66 -0
  77. package/dist/chunks/chunk-UVBGRJQX.js +50 -0
  78. package/dist/chunks/chunk-VHW7FDHU.js +42 -0
  79. package/dist/chunks/chunk-VKB57ZYE.js +218 -0
  80. package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
  81. package/dist/chunks/chunk-WOO7QYKB.js +121 -0
  82. package/dist/chunks/chunk-WSIFAZU7.js +132 -0
  83. package/dist/chunks/chunk-X32PABB7.js +117 -0
  84. package/dist/chunks/chunk-YJQ7CT7F.js +23 -0
  85. package/dist/cli/figure.js +84 -17
  86. package/dist/colorSchemes.js +10 -0
  87. package/dist/deletionEdges.js +13 -0
  88. package/dist/el.js +8 -0
  89. package/dist/facetGrid.js +16 -0
  90. package/dist/figure.js +51 -0
  91. package/dist/gaf/parseGaf.js +12 -0
  92. package/dist/gbzCut.js +19 -0
  93. package/dist/gbzJoin.js +6 -0
  94. package/dist/gbzWindow.js +28 -0
  95. package/dist/genes/exonOutline.js +11 -0
  96. package/dist/genes/geneFiles.js +14 -0
  97. package/dist/genes/genePins.js +10 -0
  98. package/dist/gfa/gfaConverter.js +11 -0
  99. package/dist/gfa-core/gfaParser.js +8 -0
  100. package/dist/gfa-core/index.js +9 -0
  101. package/dist/graphLabels.js +24 -0
  102. package/dist/index.js +262 -1407
  103. package/dist/labelLayout.js +24 -0
  104. package/dist/layout/anchoredLayout.js +11 -0
  105. package/dist/layout/deletionRoutes.js +10 -0
  106. package/dist/layout/drawnScale.js +22 -0
  107. package/dist/layout/mergeRuns.js +15 -0
  108. package/dist/layout/orderedLayout.js +12 -0
  109. package/dist/layout/orientToReference.js +7 -0
  110. package/dist/layout/placeOffReference.js +9 -0
  111. package/dist/layout/referenceSeeds.js +11 -0
  112. package/dist/layout/rowSpacing.js +6 -0
  113. package/dist/layout/sampleRowLayout.js +11 -0
  114. package/dist/layout/trimToWindow.js +99 -0
  115. package/dist/layout/tubeMapLayout.js +28 -0
  116. package/dist/layout/walkRowDraw.js +65 -0
  117. package/dist/layout/walkRowLayout.js +13 -0
  118. package/dist/layout/walkRows.js +11 -0
  119. package/dist/layout/walkStrip.js +40 -0
  120. package/dist/layoutModes.js +35 -0
  121. package/dist/loadBandage.js +6 -0
  122. package/dist/nodeWidths.js +18 -0
  123. package/dist/overlayLabels.js +18 -0
  124. package/dist/pansn.js +18 -0
  125. package/dist/pathAnchoring.js +18 -0
  126. package/dist/pathColors.js +31 -0
  127. package/dist/pipeline.js +40 -0
  128. package/dist/reference.js +24 -0
  129. package/dist/referenceRampCss.js +29 -0
  130. package/dist/referenceSpan.js +11 -0
  131. package/dist/referenceStrip.js +35 -0
  132. package/dist/renderer/Canvas2DRenderer.js +9 -0
  133. package/dist/renderer/GeometryBuilder.js +42 -0
  134. package/dist/renderer/colorBits.js +8 -0
  135. package/dist/renderer/recordingCanvas.js +44 -0
  136. package/dist/renderer/svgCanvas.js +6 -0
  137. package/dist/renderer/types.js +0 -0
  138. package/dist/tubeMap/axis.js +25 -0
  139. package/dist/tubeMap/coarsen.js +361 -0
  140. package/dist/tubeMap/connectors.js +13 -0
  141. package/dist/tubeMap/deviations.js +85 -0
  142. package/dist/tubeMap/draw.js +15 -0
  143. package/dist/tubeMap/frame.js +9 -0
  144. package/dist/tubeMap/genes.js +20 -0
  145. package/dist/tubeMap/mismatches.js +6 -0
  146. package/dist/tubeMap/nodeColors.js +21 -0
  147. package/dist/tubeMap/reads.js +17 -0
  148. package/dist/tubeMap/warp.js +8 -0
  149. package/dist/types/version.d.ts +1 -1
  150. package/dist/types.js +0 -0
  151. package/dist/util/SpatialIndex.js +13 -0
  152. package/dist/util/edgeCurves.js +10 -0
  153. package/dist/util/geometry.js +39 -0
  154. package/dist/util/hitDetection.js +20 -0
  155. package/dist/util/wheelZoom.js +6 -0
  156. package/dist/version.js +6 -0
  157. package/dist/viewport.js +33 -0
  158. package/dist/walkEncoding.js +39 -0
  159. package/dist/walkHighlight.js +26 -0
  160. package/dist/walkKey.js +17 -0
  161. package/package.json +38 -5
  162. package/dist/chunks/chunk-EXSFRX2F.js +0 -7400
@@ -0,0 +1,71 @@
1
+ import {
2
+ placeOffReference
3
+ } from "./chunk-TZFWUT2C.js";
4
+ import {
5
+ parsePanSN
6
+ } from "./chunk-RVS2QCOX.js";
7
+ import {
8
+ ROW_HEIGHT_PX
9
+ } from "./chunk-CPMHA4W5.js";
10
+ import {
11
+ backboneNodes,
12
+ backbonePositions,
13
+ isOffReference,
14
+ referenceSpan
15
+ } from "./chunk-FYQWPEEY.js";
16
+
17
+ // src/layout/sampleRowLayout.ts
18
+ function contributingSamples(graph, keep) {
19
+ const carried = /* @__PURE__ */ new Map();
20
+ for (const node of graph.nodes) {
21
+ if (isOffReference(node)) {
22
+ const { sample } = parsePanSN(node.stable.refName);
23
+ carried.set(sample, (carried.get(sample) ?? 0) + node.length);
24
+ }
25
+ }
26
+ const held = new Map(keep.map((sample, row) => [sample, row]));
27
+ return [...carried.keys()].sort(
28
+ (a, b) => (held.get(a) ?? Infinity) - (held.get(b) ?? Infinity) || carried.get(b) - carried.get(a) || a.localeCompare(b)
29
+ );
30
+ }
31
+ function sampleRowLayout(graph, region, keep = []) {
32
+ const backbone = backboneNodes(graph);
33
+ const samples = contributingSamples(graph, keep);
34
+ if (backbone.length === 0 || samples.length === 0) {
35
+ return void 0;
36
+ }
37
+ const span = referenceSpan(backbone, region);
38
+ const rowOf = new Map(samples.map((s, i) => [s, i + 1]));
39
+ const nodePositions = backbonePositions(backbone);
40
+ const spareRow = samples.length + 1;
41
+ const alleleDeletions = placeOffReference({
42
+ graph,
43
+ span,
44
+ rowY: (node) => {
45
+ const stable = node.stable;
46
+ const row = stable ? rowOf.get(parsePanSN(stable.refName).sample) : void 0;
47
+ return (row ?? spareRow) * ROW_HEIGHT_PX;
48
+ },
49
+ positions: nodePositions
50
+ });
51
+ const referenceSample = parsePanSN(backbone[0].stable.refName).sample;
52
+ const rowLabels = [
53
+ { label: referenceSample, y: 0 },
54
+ ...samples.map((sample) => ({
55
+ label: sample,
56
+ y: rowOf.get(sample) * ROW_HEIGHT_PX
57
+ }))
58
+ ];
59
+ return {
60
+ nodePositions,
61
+ rowLabels,
62
+ sampleRows: samples,
63
+ referenceAxis: true,
64
+ pixelRows: true,
65
+ alleleDeletions
66
+ };
67
+ }
68
+
69
+ export {
70
+ sampleRowLayout
71
+ };
@@ -0,0 +1,112 @@
1
+ import {
2
+ joinCuts
3
+ } from "./chunk-WSIFAZU7.js";
4
+ import {
5
+ wellKnownSample
6
+ } from "./chunk-RAKOB6IV.js";
7
+ import {
8
+ panSNMatchesPrefix,
9
+ panSNSample
10
+ } from "./chunk-BPLIXMH7.js";
11
+
12
+ // src/gbzWindow.ts
13
+ var NoReferenceSampleError = class extends Error {
14
+ constructor(anchor, referenceSamples) {
15
+ super(
16
+ referenceSamples.length === 0 ? `the graph names no reference sample (gbwt_reference_samples) and the anchor "${anchor}" maps to none; set referenceSample` : `the anchor "${anchor}" is none of the graph's reference samples (${referenceSamples.join(", ")}); set referenceSample or map it through assemblyNameToPanSN`
17
+ );
18
+ this.name = "NoReferenceSampleError";
19
+ }
20
+ };
21
+ var NodeLimitError = class extends Error {
22
+ constructor(limit, windowBp, fitsBp) {
23
+ super(
24
+ `this ${windowBp.toLocaleString()} bp window reads more than nodeLimit (${limit.toLocaleString()}) graph nodes; zoom in to about ${fitsBp.toLocaleString()} bp or raise nodeLimit`
25
+ );
26
+ this.fitsBp = fitsBp;
27
+ this.name = "NodeLimitError";
28
+ this.regionTooLarge = true;
29
+ }
30
+ };
31
+ function haplotypePrefix(name) {
32
+ return `${name.sample}#${name.haplotype}`;
33
+ }
34
+ function haplotypeWanted(name, wanted) {
35
+ const prefix = haplotypePrefix(name);
36
+ return wanted === void 0 || wanted.some((candidate) => panSNMatchesPrefix(prefix, candidate));
37
+ }
38
+ async function referenceSamplesOf(db) {
39
+ return (await db.tag("gbwt_reference_samples") ?? "").split(/\s+/).filter((sample) => sample !== "");
40
+ }
41
+ function aliasedSample(anchorSample, referenceSamples) {
42
+ const lower = anchorSample.toLowerCase();
43
+ const alias = wellKnownSample(anchorSample)?.toLowerCase();
44
+ return referenceSamples.find((s) => s === anchorSample) ?? referenceSamples.find((s) => s.toLowerCase() === lower) ?? referenceSamples.find((s) => s.toLowerCase() === alias);
45
+ }
46
+ function resolveReferenceSample({
47
+ configured,
48
+ anchorPrefix,
49
+ referenceSamples
50
+ }) {
51
+ if (configured !== "") {
52
+ return configured;
53
+ }
54
+ const sample = aliasedSample(panSNSample(anchorPrefix), referenceSamples) ?? (referenceSamples.length === 1 ? referenceSamples[0] : void 0);
55
+ if (sample === void 0) {
56
+ throw new NoReferenceSampleError(anchorPrefix, referenceSamples);
57
+ }
58
+ return sample;
59
+ }
60
+ async function referencePathQuery(db, referenceSample, refName) {
61
+ const path = (await db.paths()).find(
62
+ (p) => p.isIndexed && p.name.sample === referenceSample && p.name.contig === refName
63
+ );
64
+ return path ? {
65
+ sample: path.name.sample,
66
+ contig: refName,
67
+ haplotype: path.name.haplotype
68
+ } : void 0;
69
+ }
70
+ function nodeLimitError(error, limit, windowBp) {
71
+ const isLimit = error instanceof Error && (error.name === "SubgraphLimitError" || /^Subgraph size limit of \d+ nodes exceeded/.test(error.message));
72
+ if (!isLimit) {
73
+ return void 0;
74
+ } else {
75
+ const walked = error.walkedBp;
76
+ const fits = typeof walked === "number" && walked > 0 ? Math.floor(walked * 0.8) : Math.floor(windowBp / 2);
77
+ return new NodeLimitError(limit, windowBp, Math.max(fits, 1));
78
+ }
79
+ }
80
+ var GBZ_CUT_DEFAULTS = {
81
+ context: 1e3,
82
+ snarls: "contained",
83
+ limit: 1e5
84
+ };
85
+ async function cutWindowGFA(db, query, start, end, opts) {
86
+ if (!query) {
87
+ return "";
88
+ }
89
+ const subgraphs = await db.getSubgraphs({ ...opts, path: query, start, end, haplotypes: "all" }).catch((error) => {
90
+ throw nodeLimitError(error, opts.limit, end - start) ?? error;
91
+ });
92
+ const paths = await db.paths();
93
+ return joinCuts(
94
+ await Promise.all(subgraphs.map((subgraph) => subgraph.toGFA())),
95
+ (sample, haplotype, contig, at) => paths.some(
96
+ ({ name }) => name.sample === sample && name.haplotype === haplotype && name.contig === contig && name.fragment === at
97
+ )
98
+ );
99
+ }
100
+
101
+ export {
102
+ NoReferenceSampleError,
103
+ NodeLimitError,
104
+ haplotypePrefix,
105
+ haplotypeWanted,
106
+ referenceSamplesOf,
107
+ resolveReferenceSample,
108
+ referencePathQuery,
109
+ nodeLimitError,
110
+ GBZ_CUT_DEFAULTS,
111
+ cutWindowGFA
112
+ };
@@ -0,0 +1,181 @@
1
+ import {
2
+ baseEdgeCurves
3
+ } from "./chunk-6YRRNX2P.js";
4
+ import {
5
+ curveBounds,
6
+ pathRibbonOffsets
7
+ } from "./chunk-3PLYZLYV.js";
8
+
9
+ // src/util/SpatialIndex.ts
10
+ var FALLBACK_CELL_SIZE = 50;
11
+ function meanExtentCellSize(extentTotal, count) {
12
+ return count > 0 ? Math.max(1, extentTotal / count) : FALLBACK_CELL_SIZE;
13
+ }
14
+ function rowPitchOf(boxes) {
15
+ const distinct = /* @__PURE__ */ new Set();
16
+ let min = Infinity;
17
+ let max = -Infinity;
18
+ for (const box of boxes) {
19
+ distinct.add(box.minY);
20
+ min = Math.min(min, box.minY);
21
+ max = Math.max(max, box.minY);
22
+ }
23
+ return distinct.size > 1 ? (max - min) / (distinct.size - 1) : void 0;
24
+ }
25
+ function cellSizeFrom(boxes, extentX, extentY) {
26
+ const x = meanExtentCellSize(extentX, boxes.length);
27
+ return {
28
+ x,
29
+ y: extentY > 0 ? meanExtentCellSize(extentY, boxes.length) : rowPitchOf(boxes) ?? x
30
+ };
31
+ }
32
+ function resolveCellSize(given, boxes, extentX, extentY) {
33
+ return given === void 0 ? cellSizeFrom(boxes, extentX, extentY) : typeof given === "number" ? { x: given, y: given } : given;
34
+ }
35
+ function getOrCreateCell(cells, cx, cy) {
36
+ let row = cells.get(cx);
37
+ if (!row) {
38
+ row = /* @__PURE__ */ new Map();
39
+ cells.set(cx, row);
40
+ }
41
+ let cell = row.get(cy);
42
+ if (!cell) {
43
+ cell = [];
44
+ row.set(cy, cell);
45
+ }
46
+ return cell;
47
+ }
48
+ function addToGrid(cells, cellSize, minX, minY, maxX, maxY, entry) {
49
+ const cx0 = Math.floor(minX / cellSize.x);
50
+ const cx1 = Math.floor(maxX / cellSize.x);
51
+ const cy0 = Math.floor(minY / cellSize.y);
52
+ const cy1 = Math.floor(maxY / cellSize.y);
53
+ for (let cx = cx0; cx <= cx1; cx++) {
54
+ for (let cy = cy0; cy <= cy1; cy++) {
55
+ getOrCreateCell(cells, cx, cy).push(entry);
56
+ }
57
+ }
58
+ }
59
+ function queryGrid(cells, cellSize, x, y, radiusX, radiusY) {
60
+ const cx0 = Math.floor((x - radiusX) / cellSize.x);
61
+ const cx1 = Math.floor((x + radiusX) / cellSize.x);
62
+ const cy0 = Math.floor((y - radiusY) / cellSize.y);
63
+ const cy1 = Math.floor((y + radiusY) / cellSize.y);
64
+ const results = [];
65
+ const seen = /* @__PURE__ */ new Set();
66
+ for (let cx = cx0; cx <= cx1; cx++) {
67
+ const row = cells.get(cx);
68
+ if (row) {
69
+ for (let cy = cy0; cy <= cy1; cy++) {
70
+ const cell = row.get(cy);
71
+ if (cell) {
72
+ for (const entry of cell) {
73
+ if (!seen.has(entry)) {
74
+ seen.add(entry);
75
+ results.push(entry);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return results;
83
+ }
84
+ var SpatialIndex = class {
85
+ constructor(nodePositions, cellSize) {
86
+ this.cells = /* @__PURE__ */ new Map();
87
+ const boxes = [];
88
+ let extentX = 0;
89
+ let extentY = 0;
90
+ for (const [nodeId, segments] of Object.entries(nodePositions)) {
91
+ for (let i = 0; i < segments.length - 1; i++) {
92
+ const seg = segments[i];
93
+ const next = segments[i + 1];
94
+ const minX = Math.min(seg.x, next.x);
95
+ const minY = Math.min(seg.y, next.y);
96
+ const maxX = Math.max(seg.x, next.x);
97
+ const maxY = Math.max(seg.y, next.y);
98
+ extentX += maxX - minX;
99
+ extentY += maxY - minY;
100
+ boxes.push({ entry: { nodeId, segmentIdx: i }, minX, minY, maxX, maxY });
101
+ }
102
+ }
103
+ this.cellSize = resolveCellSize(cellSize, boxes, extentX, extentY);
104
+ for (const b of boxes) {
105
+ addToGrid(
106
+ this.cells,
107
+ this.cellSize,
108
+ b.minX,
109
+ b.minY,
110
+ b.maxX,
111
+ b.maxY,
112
+ b.entry
113
+ );
114
+ }
115
+ }
116
+ query(x, y, radiusX, radiusY = radiusX) {
117
+ return queryGrid(this.cells, this.cellSize, x, y, radiusX, radiusY);
118
+ }
119
+ };
120
+ var EdgeSpatialIndex = class {
121
+ constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions, version = 0, routes, stranded = false) {
122
+ this.cells = /* @__PURE__ */ new Map();
123
+ this.edgeCurves = baseEdgeCurves(
124
+ nodePositions,
125
+ graph,
126
+ axis,
127
+ deletions,
128
+ version,
129
+ routes,
130
+ stranded
131
+ );
132
+ const boxes = [];
133
+ let extentX = 0;
134
+ let extentY = 0;
135
+ for (let ei = 0; ei < graph.edges.length; ei++) {
136
+ const edge = graph.edges[ei];
137
+ const fromSegments = nodePositions[edge.from];
138
+ const toSegments = nodePositions[edge.to];
139
+ const curves = this.edgeCurves.get(ei);
140
+ if (fromSegments?.length && toSegments?.length && curves) {
141
+ let { minX, minY, maxX, maxY } = curveBounds(curves);
142
+ const numPaths = edge.pathIds?.length ?? 0;
143
+ if (drawPaths && numPaths > 0) {
144
+ let padX = 0;
145
+ let padY = 0;
146
+ for (const o of pathRibbonOffsets(
147
+ fromSegments,
148
+ toSegments,
149
+ numPaths,
150
+ axis
151
+ )) {
152
+ padX = Math.max(padX, Math.abs(o.x));
153
+ padY = Math.max(padY, Math.abs(o.y));
154
+ }
155
+ minX -= padX;
156
+ minY -= padY;
157
+ maxX += padX;
158
+ maxY += padY;
159
+ }
160
+ extentX += maxX - minX;
161
+ extentY += maxY - minY;
162
+ boxes.push({ ei, minX, minY, maxX, maxY });
163
+ }
164
+ }
165
+ this.cellSize = resolveCellSize(cellSize, boxes, extentX, extentY);
166
+ for (const b of boxes) {
167
+ addToGrid(this.cells, this.cellSize, b.minX, b.minY, b.maxX, b.maxY, b.ei);
168
+ }
169
+ }
170
+ getCurves(ei) {
171
+ return this.edgeCurves.get(ei);
172
+ }
173
+ query(x, y, radiusX, radiusY = radiusX) {
174
+ return queryGrid(this.cells, this.cellSize, x, y, radiusX, radiusY);
175
+ }
176
+ };
177
+
178
+ export {
179
+ SpatialIndex,
180
+ EdgeSpatialIndex
181
+ };
@@ -0,0 +1,44 @@
1
+ // src/bubbles/bubbleLine.ts
2
+ function sequence(field) {
3
+ return field === void 0 || field === "*" ? void 0 : field;
4
+ }
5
+ function parseBubbleLine(line) {
6
+ const cols = line.split(" ");
7
+ return {
8
+ refName: cols[0],
9
+ start: +cols[1],
10
+ end: +cols[2],
11
+ segmentCount: +cols[3],
12
+ pathCount: +cols[4],
13
+ inversion: cols[5] === "1",
14
+ shortestAlleleLength: +cols[6],
15
+ longestAlleleLength: +cols[7],
16
+ segments: cols[11],
17
+ shortestAllele: sequence(cols[12]),
18
+ longestAllele: sequence(cols[13])
19
+ };
20
+ }
21
+ function sameBubble(a, b) {
22
+ return a.refName === b.refName && a.start === b.start && a.end === b.end;
23
+ }
24
+ function bubbleLabel(bubble) {
25
+ const { shortestAlleleLength: min, longestAlleleLength: max } = bubble;
26
+ return min === max ? `${max.toLocaleString()} bp` : `${min.toLocaleString()}-${max.toLocaleString()} bp`;
27
+ }
28
+ var SATURATED_PATH_COUNT = 2147483647;
29
+ function bubbleDescription(bubble) {
30
+ const { pathCount, segmentCount, inversion } = bubble;
31
+ return [
32
+ `${segmentCount} segments`,
33
+ pathCount === SATURATED_PATH_COUNT ? "more paths than gfatools counts" : `up to ${pathCount.toLocaleString()} paths`,
34
+ ...inversion ? ["inversion"] : []
35
+ ].join(", ");
36
+ }
37
+
38
+ export {
39
+ parseBubbleLine,
40
+ sameBubble,
41
+ bubbleLabel,
42
+ SATURATED_PATH_COUNT,
43
+ bubbleDescription
44
+ };
@@ -0,0 +1,205 @@
1
+ import {
2
+ alongRow,
3
+ boxesGenes
4
+ } from "./chunk-QCYM44UL.js";
5
+ import {
6
+ pathOrigin
7
+ } from "./chunk-ST2IBUAN.js";
8
+ import {
9
+ LABEL_CHAR_PX
10
+ } from "./chunk-AB35TJST.js";
11
+ import {
12
+ ROW_HEIGHT_PX
13
+ } from "./chunk-CPMHA4W5.js";
14
+ import {
15
+ el
16
+ } from "./chunk-NNODIW4U.js";
17
+
18
+ // src/layout/walkStrip.ts
19
+ var PAD_PX = 6;
20
+ var WALK_STRIP_CEILING_PX = 260;
21
+ var LABEL_FONT_PX = 11;
22
+ var MIN_ROW_PX = 3;
23
+ var LABELLED_ROW_PX = 12;
24
+ var READOUT_ROOM = 1.3;
25
+ var MARK_INK = "#111";
26
+ var TICK_PX = 3;
27
+ function stripMarks(graph, rows, node) {
28
+ const visits = graph.pathVisits?.get(node.name) ?? [];
29
+ const marks = [];
30
+ for (const row of rows) {
31
+ const axis = row.axis;
32
+ if (!axis) {
33
+ continue;
34
+ }
35
+ const path = pathOrigin(row.name).name;
36
+ for (const v of visits) {
37
+ if (v.path !== path) {
38
+ continue;
39
+ }
40
+ const { start, end } = alongRow(axis, v.start, v.start + node.length);
41
+ if (end > 0 && start < row.bp) {
42
+ marks.push({ row: row.name, start, bp: end - start });
43
+ }
44
+ }
45
+ }
46
+ return marks;
47
+ }
48
+ var spanCache = /* @__PURE__ */ new WeakMap();
49
+ function spansOf(graph, name) {
50
+ let byName = spanCache.get(graph);
51
+ if (!byName) {
52
+ byName = /* @__PURE__ */ new Map();
53
+ spanCache.set(graph, byName);
54
+ }
55
+ if (byName.has(name)) {
56
+ return byName.get(name);
57
+ }
58
+ const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
59
+ const pieces = (graph.paths ?? []).filter((p) => p.name === name);
60
+ let spans;
61
+ if (pieces.every((p) => p.start !== void 0)) {
62
+ spans = { ids: [], starts: [], ends: [] };
63
+ for (const piece of [...pieces].sort((a, b) => a.start - b.start)) {
64
+ let pos = piece.start;
65
+ for (const id of piece.nodeIds) {
66
+ const len = lengthOf.get(id) ?? 0;
67
+ spans.ids.push(id);
68
+ spans.starts.push(pos);
69
+ spans.ends.push(pos + len);
70
+ pos += len;
71
+ }
72
+ }
73
+ }
74
+ byName.set(name, spans);
75
+ return spans;
76
+ }
77
+ function segmentAt(graph, row, offset, pxPerBp) {
78
+ const axis = row.axis;
79
+ const spans = axis && spansOf(graph, row.name);
80
+ if (!axis || !spans || offset < 0 || offset > row.bp) {
81
+ return void 0;
82
+ }
83
+ const at = axis.reversed ? axis.start - offset : axis.start + offset;
84
+ const tol = 0.5 / pxPerBp;
85
+ const bar = axis.reversed ? { start: axis.start - row.bp, end: axis.start } : { start: axis.start, end: axis.start + row.bp };
86
+ const from = Math.max(at - tol, bar.start);
87
+ const to = Math.min(at + tol, bar.end);
88
+ let lo = 0;
89
+ let hi = spans.starts.length;
90
+ while (lo < hi) {
91
+ const mid = lo + hi >> 1;
92
+ if (spans.ends[mid] <= from) {
93
+ lo = mid + 1;
94
+ } else {
95
+ hi = mid;
96
+ }
97
+ }
98
+ let best;
99
+ let bestLen = -1;
100
+ let bestHolds = false;
101
+ for (let i = lo; i < spans.starts.length && spans.starts[i] < to; i++) {
102
+ const len = spans.ends[i] - spans.starts[i];
103
+ const holds = spans.starts[i] <= at && at < spans.ends[i];
104
+ if (len > bestLen || len === bestLen && holds && !bestHolds) {
105
+ bestLen = len;
106
+ best = spans.ids[i];
107
+ bestHolds = holds;
108
+ }
109
+ }
110
+ return best;
111
+ }
112
+ function walkStripFrame(bars, o) {
113
+ const maxHeight = o.maxHeight ?? WALK_STRIP_CEILING_PX;
114
+ const labelPx = o.labelPx ?? Math.max(...[bars.reference, ...bars.rows].map((r) => r.label.length)) * LABEL_CHAR_PX + 16;
115
+ const count = bars.rows.length + 1;
116
+ const rowPx = Math.max(
117
+ MIN_ROW_PX,
118
+ Math.min(ROW_HEIGHT_PX, Math.floor((maxHeight - 2 * PAD_PX) / count))
119
+ );
120
+ const labelled = rowPx >= LABELLED_ROW_PX;
121
+ const readouts = rowPx >= 14;
122
+ const barPx = Math.max(2, Math.round(rowPx * 0.6));
123
+ const longest = Math.max(bars.reference.bp, ...bars.rows.map((r) => r.bp), 1);
124
+ const left = labelled ? labelPx : PAD_PX;
125
+ const usable = Math.max(1, o.width - left - PAD_PX);
126
+ const scaleX = usable / (longest * (readouts ? READOUT_ROOM : 1));
127
+ return {
128
+ scaleX,
129
+ scaleY: 1,
130
+ translateX: left - bars.origin * scaleX,
131
+ translateY: PAD_PX + rowPx / 2,
132
+ width: o.width,
133
+ height: count * rowPx + 2 * PAD_PX,
134
+ rowPx,
135
+ barPx,
136
+ readouts,
137
+ labelled,
138
+ boxesGenes: boxesGenes(rowPx, barPx)
139
+ };
140
+ }
141
+ function stripRowAt(bars, frame, x, y) {
142
+ const i = Math.round((y - frame.translateY) / frame.rowPx);
143
+ const row = [bars.reference, ...bars.rows][i];
144
+ const offset = (x - frame.translateX) / frame.scaleX - bars.origin;
145
+ return row && i >= 0 && offset >= 0 && offset <= row.bp ? { index: i, row, offset } : void 0;
146
+ }
147
+ function walkMarksTree(bars, frame, marks) {
148
+ const index = new Map(
149
+ [bars.reference, ...bars.rows].map((row, i) => [row.name, i])
150
+ );
151
+ const X = (offset) => (bars.origin + offset) * frame.scaleX + frame.translateX;
152
+ return el(
153
+ "g",
154
+ { "data-testid": "graph-walk-marks" },
155
+ ...marks.flatMap((m) => {
156
+ const i = index.get(m.row);
157
+ if (i === void 0) {
158
+ return [];
159
+ }
160
+ const y = i * frame.rowPx + frame.translateY;
161
+ const x = X(m.start);
162
+ const w = Math.max(2, m.bp * frame.scaleX);
163
+ const tick = (top) => el("rect", { x, y: top, width: w, height: TICK_PX, fill: MARK_INK });
164
+ return [
165
+ tick(y - frame.barPx / 2 - TICK_PX - 1),
166
+ tick(y + frame.barPx / 2 + 1)
167
+ ];
168
+ })
169
+ );
170
+ }
171
+ function walkStripLabelsTree(bars, frame, bold = /* @__PURE__ */ new Set()) {
172
+ return el(
173
+ "g",
174
+ { "font-family": "sans-serif", "font-size": LABEL_FONT_PX, fill: "#333" },
175
+ ...(frame.labelled ? [bars.reference, ...bars.rows] : []).map(
176
+ (row, i) => el(
177
+ "text",
178
+ {
179
+ x: 6,
180
+ y: i * frame.rowPx + frame.translateY + 4,
181
+ "font-weight": bold.has(row.name) ? 600 : void 0
182
+ },
183
+ row.label
184
+ )
185
+ )
186
+ );
187
+ }
188
+ function stripGeneGaps(frame, rowGenes, gaps) {
189
+ if (!rowGenes?.size) {
190
+ return void 0;
191
+ }
192
+ return frame.boxesGenes ? gaps : { untracked: 0, unread: 0, ...gaps, crowded: true };
193
+ }
194
+
195
+ export {
196
+ WALK_STRIP_CEILING_PX,
197
+ LABELLED_ROW_PX,
198
+ stripMarks,
199
+ segmentAt,
200
+ walkStripFrame,
201
+ stripRowAt,
202
+ walkMarksTree,
203
+ walkStripLabelsTree,
204
+ stripGeneGaps
205
+ };
@@ -0,0 +1,76 @@
1
+ // src/layout/deletionRoutes.ts
2
+ var ROUTE_ID = "\0deletion:";
3
+ var ROUTE_FRACTION = 0.5;
4
+ function withDeletionRoutes(graph, deletions) {
5
+ const routes = new Map(
6
+ deletions.map((d) => [d.edgeIndex, `${ROUTE_ID}${d.edgeIndex}`])
7
+ );
8
+ const edges = [];
9
+ graph.edges.forEach((edge, ei) => {
10
+ const id = routes.get(ei);
11
+ if (id === void 0) {
12
+ edges.push(edge);
13
+ } else {
14
+ const { fromStrand, toStrand } = edge;
15
+ edges.push(
16
+ { from: edge.from, to: id, ...fromStrand ? { fromStrand } : {} },
17
+ { from: id, to: edge.to, ...toStrand ? { toStrand } : {} }
18
+ );
19
+ }
20
+ });
21
+ return {
22
+ graph: {
23
+ ...graph,
24
+ nodes: [
25
+ ...graph.nodes,
26
+ ...[...routes.values()].map((id) => ({
27
+ id,
28
+ name: id,
29
+ length: 0,
30
+ depth: 0
31
+ }))
32
+ ],
33
+ edges
34
+ },
35
+ routes,
36
+ ids: new Set(routes.values())
37
+ };
38
+ }
39
+ function routeNodes(graph, deletions, routes, drawnOf, opts) {
40
+ const lengths = new Map(graph.nodes.map((n) => [n.id, n.length]));
41
+ return deletions.map((d) => {
42
+ const skipped = d.bypassed.reduce(
43
+ (sum, id2) => sum + (lengths.get(id2) ?? 0),
44
+ 0
45
+ );
46
+ const drawn = Math.max(
47
+ opts.minimumNodeLength,
48
+ drawnOf(skipped) * ROUTE_FRACTION
49
+ );
50
+ const id = routes.get(d.edgeIndex);
51
+ return {
52
+ id,
53
+ name: id,
54
+ length: Math.round(drawn * 1e6 / opts.nodeLengthPerMegabase),
55
+ depth: 0
56
+ };
57
+ });
58
+ }
59
+ function takeRoutes(positions, routes) {
60
+ const nodePositions = { ...positions };
61
+ const deletionRoutes = {};
62
+ for (const [edgeIndex, id] of routes) {
63
+ const route = nodePositions[id];
64
+ delete nodePositions[id];
65
+ if (route?.length) {
66
+ deletionRoutes[edgeIndex] = route;
67
+ }
68
+ }
69
+ return { nodePositions, deletionRoutes };
70
+ }
71
+
72
+ export {
73
+ withDeletionRoutes,
74
+ routeNodes,
75
+ takeRoutes
76
+ };