@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,135 @@
1
+ // src/gfa-core/gfaParser.ts
2
+ function parseTag(tag, tags) {
3
+ const nameEnd = tag.indexOf(":");
4
+ const typeEnd = tag.indexOf(":", nameEnd + 1);
5
+ if (nameEnd !== -1 && typeEnd !== -1) {
6
+ const name = tag.slice(0, nameEnd);
7
+ const type = tag.slice(nameEnd + 1, typeEnd);
8
+ const val = tag.slice(typeEnd + 1);
9
+ if (type === "i" || type === "f") {
10
+ tags[name] = +val;
11
+ } else if (type === "Z") {
12
+ tags[name] = val;
13
+ }
14
+ }
15
+ }
16
+ function parseTags(fields) {
17
+ const tags = {};
18
+ for (const field of fields) {
19
+ parseTag(field, tags);
20
+ }
21
+ return tags;
22
+ }
23
+ function stableCoordinate(node) {
24
+ const { SN, SO, SR } = node.tags;
25
+ return typeof SN === "string" && typeof SO === "number" && typeof SR === "number" ? { refName: SN, start: SO, rank: SR } : void 0;
26
+ }
27
+ var GT = 62;
28
+ var LT = 60;
29
+ function parseWalkBody(body) {
30
+ const segments = [];
31
+ let strand = "+";
32
+ let start = 0;
33
+ function push(end) {
34
+ if (end > start) {
35
+ segments.push({ id: body.slice(start, end), strand });
36
+ }
37
+ }
38
+ for (let i = 0; i < body.length; i++) {
39
+ const ch = body.charCodeAt(i);
40
+ if (ch === GT || ch === LT) {
41
+ push(i);
42
+ strand = ch === GT ? "+" : "-";
43
+ start = i + 1;
44
+ }
45
+ }
46
+ push(body.length);
47
+ return segments;
48
+ }
49
+ function parseGFA(file) {
50
+ const graph = {
51
+ nodes: [],
52
+ links: [],
53
+ paths: [],
54
+ walks: [],
55
+ header: [],
56
+ id: ""
57
+ };
58
+ for (const rawLine of file.split("\n")) {
59
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
60
+ if (line.startsWith("H")) {
61
+ const [, ...rest] = line.split(" ");
62
+ graph.header.push(parseTags(rest));
63
+ } else if (line.startsWith("S")) {
64
+ const [, name, ...rest] = line.split(" ");
65
+ let len;
66
+ let seq;
67
+ let tagfields;
68
+ let gfa1 = false;
69
+ const first = rest[0] ?? "*";
70
+ if (/^\d+$/.test(first)) {
71
+ len = +first;
72
+ seq = rest[1] ?? "*";
73
+ tagfields = rest.slice(2);
74
+ } else {
75
+ gfa1 = true;
76
+ seq = first;
77
+ len = seq === "*" ? 0 : seq.length;
78
+ tagfields = rest.slice(1);
79
+ }
80
+ const tags = parseTags(tagfields);
81
+ if (gfa1 && "LN" in tags) {
82
+ len = +tags.LN;
83
+ }
84
+ graph.nodes.push({ id: name, length: len, sequence: seq, tags });
85
+ } else if (line.startsWith("E")) {
86
+ const [, , source, target, , , , , cigar, ...rest] = line.split(" ");
87
+ if (source && target) {
88
+ graph.links.push({
89
+ source: source.slice(0, -1),
90
+ target: target.slice(0, -1),
91
+ strand1: source.at(-1),
92
+ strand2: target.at(-1),
93
+ cigar: cigar ?? "*",
94
+ tags: parseTags(rest)
95
+ });
96
+ }
97
+ } else if (line.startsWith("L")) {
98
+ const [, source, strand1, target, strand2, cigar, ...rest] = line.split(" ");
99
+ if (source && target && strand2) {
100
+ graph.links.push({
101
+ source,
102
+ target,
103
+ strand1,
104
+ strand2,
105
+ cigar: cigar ?? "*",
106
+ tags: parseTags(rest)
107
+ });
108
+ }
109
+ } else if (line.startsWith("W")) {
110
+ const [, sample, hap, contig, start, end, body, ...rest] = line.split(" ");
111
+ if (sample && hap && contig && start && end && body) {
112
+ graph.walks.push({
113
+ sample,
114
+ haplotype: +hap,
115
+ contig,
116
+ start: start === "*" ? -1 : +start,
117
+ end: end === "*" ? -1 : +end,
118
+ segments: parseWalkBody(body),
119
+ tags: parseTags(rest)
120
+ });
121
+ }
122
+ } else if (line.startsWith("P")) {
123
+ const [, name, path, ...rest] = line.split(" ");
124
+ if (name && path) {
125
+ graph.paths.push({ name, path, rest });
126
+ }
127
+ }
128
+ }
129
+ return graph;
130
+ }
131
+
132
+ export {
133
+ stableCoordinate,
134
+ parseGFA
135
+ };
@@ -0,0 +1,6 @@
1
+ // src/layout/rowSpacing.ts
2
+ var ROW_HEIGHT_PX = 20;
3
+
4
+ export {
5
+ ROW_HEIGHT_PX
6
+ };
@@ -0,0 +1,241 @@
1
+ import {
2
+ tubeMapMismatches
3
+ } from "./chunk-EJ5OZTCK.js";
4
+
5
+ // src/tubeMap/draw.ts
6
+ import { curvePaths, nodeOutlinePath } from "@gmod/tubemap-core";
7
+ function parsePath(d) {
8
+ const tokens = d.trim().split(/[\s,]+/);
9
+ const commands = [];
10
+ let i = 0;
11
+ const num = () => Number(tokens[i++]);
12
+ while (i < tokens.length) {
13
+ const op = tokens[i++];
14
+ if (op === "M" || op === "L") {
15
+ commands.push({ op, x: num(), y: num() });
16
+ } else if (op === "H") {
17
+ commands.push({ op, x: num() });
18
+ } else if (op === "V") {
19
+ commands.push({ op, y: num() });
20
+ } else if (op === "Q") {
21
+ commands.push({ op, x1: num(), y1: num(), x: num(), y: num() });
22
+ } else if (op === "C") {
23
+ commands.push({
24
+ op,
25
+ x1: num(),
26
+ y1: num(),
27
+ x2: num(),
28
+ y2: num(),
29
+ x: num(),
30
+ y: num()
31
+ });
32
+ } else if (op === "Z") {
33
+ commands.push({ op });
34
+ }
35
+ }
36
+ return commands;
37
+ }
38
+ function extentOf(commands) {
39
+ let x0 = Infinity;
40
+ let x1 = -Infinity;
41
+ for (const c of commands) {
42
+ if ("x" in c) {
43
+ x0 = Math.min(x0, c.x);
44
+ x1 = Math.max(x1, c.x);
45
+ }
46
+ if ("x1" in c) {
47
+ x0 = Math.min(x0, c.x1);
48
+ x1 = Math.max(x1, c.x1);
49
+ }
50
+ }
51
+ return { x0, x1 };
52
+ }
53
+ function shapeOf(d, color, alpha, track) {
54
+ const commands = parsePath(d);
55
+ return { commands, color, track, alpha, ...extentOf(commands) };
56
+ }
57
+ function layerOf(layout, type) {
58
+ const { shapes } = layout;
59
+ const trackOf = (s) => type === "haplotype" ? s.id : void 0;
60
+ const rects = [...shapes.rectangles, ...shapes.verticalRectangles].filter((r) => r.type === type).map((r) => ({
61
+ color: r.color,
62
+ track: trackOf(r),
63
+ alpha: r.alpha ?? 1,
64
+ x0: r.xStart,
65
+ x1: r.xEnd + 1,
66
+ y0: r.yStart,
67
+ y1: r.yEnd + 1
68
+ }));
69
+ const curves = curvePaths(shapes.curves, type).map(
70
+ (c) => shapeOf(c.path, c.color, c.alpha ?? 1, trackOf(c))
71
+ );
72
+ const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, c.color, 1, trackOf(c)));
73
+ return { rects, shapes: [...curves, ...corners] };
74
+ }
75
+ function tubeMapPicture(layout) {
76
+ const nodes = [];
77
+ layout.nodes.forEach((node) => {
78
+ if (node.order >= 0) {
79
+ const commands = parsePath(nodeOutlinePath(node));
80
+ nodes.push({ name: node.name, commands, ...extentOf(commands) });
81
+ }
82
+ });
83
+ return {
84
+ bounds: layout.bounds,
85
+ layers: [layerOf(layout, "haplotype"), layerOf(layout, "read")],
86
+ nodes,
87
+ mismatches: tubeMapMismatches(layout).filter(
88
+ (m) => m.kind !== "insertion" || !m.softClip
89
+ )
90
+ };
91
+ }
92
+ function trace(ctx, commands, x, y) {
93
+ let cx = 0;
94
+ let cy = 0;
95
+ for (const c of commands) {
96
+ switch (c.op) {
97
+ case "M":
98
+ cx = c.x;
99
+ cy = c.y;
100
+ ctx.moveTo(x(cx), y(cy));
101
+ break;
102
+ case "L":
103
+ cx = c.x;
104
+ cy = c.y;
105
+ ctx.lineTo(x(cx), y(cy));
106
+ break;
107
+ case "H":
108
+ cx = c.x;
109
+ ctx.lineTo(x(cx), y(cy));
110
+ break;
111
+ case "V":
112
+ cy = c.y;
113
+ ctx.lineTo(x(cx), y(cy));
114
+ break;
115
+ case "Q":
116
+ cx = c.x;
117
+ cy = c.y;
118
+ ctx.quadraticCurveTo(x(c.x1), y(c.y1), x(cx), y(cy));
119
+ break;
120
+ case "C":
121
+ cx = c.x;
122
+ cy = c.y;
123
+ ctx.bezierCurveTo(x(c.x1), y(c.y1), x(c.x2), y(c.y2), x(cx), y(cy));
124
+ break;
125
+ case "Z":
126
+ ctx.closePath();
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ function fillByColor(ctx, items, visible, colorOf, addTo) {
132
+ const byColor = /* @__PURE__ */ new Map();
133
+ for (const item of items) {
134
+ if (visible(item)) {
135
+ const key = `${colorOf(item)}|${item.alpha}`;
136
+ const list = byColor.get(key);
137
+ if (list) {
138
+ list.push(item);
139
+ } else {
140
+ byColor.set(key, [item]);
141
+ }
142
+ }
143
+ }
144
+ for (const list of byColor.values()) {
145
+ ctx.fillStyle = colorOf(list[0]);
146
+ ctx.globalAlpha = list[0].alpha;
147
+ ctx.beginPath();
148
+ for (const item of list) {
149
+ addTo(item);
150
+ }
151
+ ctx.fill();
152
+ }
153
+ ctx.globalAlpha = 1;
154
+ }
155
+ var FULL_OUTLINE_PX = 12;
156
+ var MIN_OUTLINE_ALPHA = 0.15;
157
+ function outlineAlpha(screenWidth) {
158
+ return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX));
159
+ }
160
+ var NODE_TINT_ALPHA = 0.55;
161
+ function drawTubeMap(ctx, picture, frame) {
162
+ const { x, y, width } = frame;
163
+ const visible = (item) => x(item.x1) >= 0 && x(item.x0) <= width;
164
+ const colorOf = (item) => item.track !== void 0 && frame.tubeColors?.[item.track] || item.color;
165
+ for (const layer of picture.layers) {
166
+ fillByColor(ctx, layer.rects, visible, colorOf, (r) => {
167
+ const left = x(r.x0);
168
+ const top = y(r.y0);
169
+ ctx.rect(left, top, x(r.x1) - left, y(r.y1) - top);
170
+ });
171
+ fillByColor(ctx, layer.shapes, visible, colorOf, (s) => {
172
+ trace(ctx, s.commands, x, y);
173
+ });
174
+ }
175
+ const stroke = frame.darkMode ? "#d0d0d0" : "#000000";
176
+ const fill = frame.darkMode ? "rgba(40,40,40,0.4)" : "rgba(255,255,255,0.4)";
177
+ ctx.lineWidth = 2 * Math.max(0.25, Math.min(1, frame.yScale));
178
+ for (const node of picture.nodes) {
179
+ if (visible(node)) {
180
+ const lit = node.name === frame.highlightNode;
181
+ ctx.beginPath();
182
+ trace(ctx, node.commands, x, y);
183
+ const tint = lit ? void 0 : frame.nodeColors?.get(node.name);
184
+ ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : tint ?? fill;
185
+ ctx.strokeStyle = lit ? "#ff0000" : stroke;
186
+ ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
187
+ ctx.fill();
188
+ ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0));
189
+ ctx.stroke();
190
+ ctx.globalAlpha = 1;
191
+ }
192
+ }
193
+ drawMismatches(ctx, picture.mismatches, frame);
194
+ }
195
+ var MISMATCH_FONT_PX = 12;
196
+ var MIN_MISMATCH_FONT_PX = 6;
197
+ function mismatchesLegible(yScale) {
198
+ return MISMATCH_FONT_PX * yScale >= MIN_MISMATCH_FONT_PX;
199
+ }
200
+ function mismatchOnScreen(m, { x, width }) {
201
+ const [x0, x1] = m.kind === "insertion" ? [m.x, m.x] : [m.x0, m.x1];
202
+ return x(x1) >= 0 && x(x0) <= width;
203
+ }
204
+ function drawMismatches(ctx, marks, frame) {
205
+ const { x, y, yScale, darkMode } = frame;
206
+ if (!mismatchesLegible(yScale) || marks.length === 0) {
207
+ return;
208
+ }
209
+ const fontPx = MISMATCH_FONT_PX * yScale;
210
+ ctx.fillStyle = "grey";
211
+ ctx.beginPath();
212
+ for (const m of marks) {
213
+ if (m.kind === "deletion" && mismatchOnScreen(m, frame)) {
214
+ const left = x(m.x0);
215
+ const top = y(m.y);
216
+ ctx.rect(left, top, x(m.x1) - left, y(m.y + m.height) - top);
217
+ }
218
+ }
219
+ ctx.fill();
220
+ ctx.font = `${fontPx}px monospace`;
221
+ ctx.textBaseline = "alphabetic";
222
+ ctx.fillStyle = darkMode ? "#ffffff" : "#000000";
223
+ for (const m of marks) {
224
+ if (!mismatchOnScreen(m, frame)) {
225
+ continue;
226
+ }
227
+ if (m.kind === "substitution") {
228
+ ctx.fillText(m.seq, x(m.x0) + 1, y(m.y + m.height));
229
+ } else if (m.kind === "insertion") {
230
+ ctx.fillText("*", x(m.x) - 3, y(m.y + m.height));
231
+ }
232
+ }
233
+ }
234
+
235
+ export {
236
+ parsePath,
237
+ tubeMapPicture,
238
+ drawTubeMap,
239
+ mismatchesLegible,
240
+ mismatchOnScreen
241
+ };