@jbrowse/bandage-core 6.0.0 → 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-OHMWSW6U.js +0 -7400
@@ -0,0 +1,431 @@
1
+ import {
2
+ svgCanvas
3
+ } from "./chunk-47DYCZOK.js";
4
+ import {
5
+ stripGeneGaps,
6
+ walkStripFrame,
7
+ walkStripLabelsTree
8
+ } from "./chunk-MHGHYHTL.js";
9
+ import {
10
+ walkRowsKey,
11
+ walkRowsKeyTree,
12
+ walkRowsTree
13
+ } from "./chunk-QCYM44UL.js";
14
+ import {
15
+ Canvas2DRenderer
16
+ } from "./chunk-D3M7SBKL.js";
17
+ import {
18
+ genePins
19
+ } from "./chunk-SV27AJCC.js";
20
+ import {
21
+ EXON_COLOR,
22
+ exonOutlineTree,
23
+ exonStretches
24
+ } from "./chunk-3S3YVOJ7.js";
25
+ import {
26
+ version
27
+ } from "./chunk-KCW7L3NV.js";
28
+ import {
29
+ axisScaleOf
30
+ } from "./chunk-L5RYUPZI.js";
31
+ import {
32
+ facetLifts,
33
+ walkLift
34
+ } from "./chunk-6Q6HFBJT.js";
35
+ import {
36
+ encodingStops
37
+ } from "./chunk-IMW3AK27.js";
38
+ import {
39
+ rangeText,
40
+ walkKey
41
+ } from "./chunk-AE3UQZY3.js";
42
+ import {
43
+ FIT_PADDING,
44
+ drawingBounds,
45
+ fitTransform
46
+ } from "./chunk-VKB57ZYE.js";
47
+ import {
48
+ buildGeometry,
49
+ computeReferenceRamp
50
+ } from "./chunk-3WHKR5TY.js";
51
+ import {
52
+ geneLabelCandidates
53
+ } from "./chunk-HB6PDCOU.js";
54
+ import {
55
+ nodesUnderBox
56
+ } from "./chunk-BJV2W4KZ.js";
57
+ import {
58
+ walkRowsExtent
59
+ } from "./chunk-EZPBJPK6.js";
60
+ import {
61
+ pathLegend
62
+ } from "./chunk-7CHJKABZ.js";
63
+ import {
64
+ LABEL_CHAR_PX,
65
+ LABEL_PAD,
66
+ LABEL_PX,
67
+ occupancy,
68
+ placeLabels
69
+ } from "./chunk-AB35TJST.js";
70
+ import {
71
+ nodeInk
72
+ } from "./chunk-PBENCR5L.js";
73
+ import {
74
+ resolveColorScheme
75
+ } from "./chunk-JKJWSY4V.js";
76
+ import {
77
+ deletionEdges
78
+ } from "./chunk-X32PABB7.js";
79
+ import {
80
+ ROW_HEIGHT_PX
81
+ } from "./chunk-CPMHA4W5.js";
82
+ import {
83
+ serializeEl
84
+ } from "./chunk-NNODIW4U.js";
85
+ import {
86
+ FACET_GAP_PX,
87
+ FACET_PAD_PX,
88
+ FACET_TITLE_PX,
89
+ facetCells,
90
+ facetGrid,
91
+ facetSettingOf
92
+ } from "./chunk-6SBUFFTB.js";
93
+ import {
94
+ referenceLabel
95
+ } from "./chunk-RAKOB6IV.js";
96
+
97
+ // src/figure.ts
98
+ var FONT = 'font-family="Helvetica, Arial, sans-serif" font-size="11"';
99
+ var BAR_PX = 48;
100
+ var SWATCH_PX = 18;
101
+ var KEY_GAP_PX = 16;
102
+ var STRIP_KEY_PX = 22;
103
+ var FADED = "rgb(160,160,160)";
104
+ var GENE_INK = "#1c1c22";
105
+ function esc(s) {
106
+ return s.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
107
+ }
108
+ function keySvg(id, x, y, stops, label, after, under, opacity = 1) {
109
+ const bar = stops.length > 1 ? BAR_PX : SWATCH_PX;
110
+ const fill = stops.length > 1 ? `fill="url(#${id})"` : `fill="${stops[0].color}"${opacity < 1 ? ` fill-opacity="${opacity}"` : ""}`;
111
+ const defs = stops.length > 1 ? `<linearGradient id="${id}">${stops.map((s) => `<stop offset="${s.offset}" stop-color="${s.color}"/>`).join("")}</linearGradient>` : "";
112
+ const width = Math.max(
113
+ bar + 5 + (label.length + after.length) * LABEL_CHAR_PX,
114
+ (under?.length ?? 0) * LABEL_CHAR_PX
115
+ );
116
+ return {
117
+ width,
118
+ markup: `${defs}<g transform="translate(${x} ${y})" ${FONT}><rect y="5" width="${bar}" height="8" rx="2" ${fill}/><text x="${bar + 5}" y="13"><tspan font-weight="bold">${esc(label)}</tspan>${esc(after)}</text>${under ? `<text y="28">${esc(under)}</text>` : ""}</g>`
119
+ };
120
+ }
121
+ function exonKeySvg(x, y) {
122
+ return {
123
+ width: SWATCH_PX + 5 + 4 * LABEL_CHAR_PX,
124
+ markup: `<g transform="translate(${x} ${y})" ${FONT}><rect x="1" y="4" width="${SWATCH_PX - 2}" height="10" rx="4" fill="none" stroke="${EXON_COLOR}" stroke-width="2"/><text x="${SWATCH_PX + 5}" y="13">exon</text></g>`
125
+ };
126
+ }
127
+ function walkKeySvg(id, x, y, walk, label, reference) {
128
+ const key = walkKey(walk, reference);
129
+ return keySvg(
130
+ id,
131
+ x,
132
+ y,
133
+ encodingStops(walk.encoding),
134
+ label,
135
+ key.delta + key.outside + key.reversed,
136
+ key.scale
137
+ );
138
+ }
139
+ function screenPath(d, t) {
140
+ return d.replaceAll(
141
+ /([ML])(-?[\d.e-]+),(-?[\d.e-]+)/g,
142
+ (_, cmd, x, y) => `${cmd}${+(Number(x) * t.scaleX + t.translateX).toFixed(1)},${+(Number(y) * t.scaleY + t.translateY).toFixed(1)}`
143
+ );
144
+ }
145
+ function chip(x, y, w, name, note) {
146
+ 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_INK}" text-anchor="middle"><tspan font-style="italic" font-weight="600">${esc(name)}</tspan>${note ? esc(note) : ""}</text>`;
147
+ }
148
+ function figureSvg(graph, layout, o = {}) {
149
+ const width = o.width ?? 1200;
150
+ const room = o.height ?? 800;
151
+ const region = o.region;
152
+ const rampDomain = o.colorDomain ?? region;
153
+ const rows = o.walkRows;
154
+ const bounds = drawingBounds(layout, {
155
+ region: o.fitToDrawing ? void 0 : region,
156
+ extent: rows && layout.extent ? walkRowsExtent(rows) : void 0
157
+ });
158
+ const pixelRows = layout.pixelRows ?? false;
159
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
160
+ const colorScheme = resolveColorScheme(o.colorScheme ?? "auto", graph);
161
+ const referenceRamp = colorScheme === "reference-position" && !layout.tubeMap ? computeReferenceRamp(graph, rampDomain) : void 0;
162
+ const rampInterval = referenceRamp && {
163
+ start: referenceRamp.start,
164
+ end: referenceRamp.start + referenceRamp.span
165
+ };
166
+ const deletions = layout.tubeMap ? [] : deletionEdges(graph);
167
+ const layers = o.walks ?? [];
168
+ const walkRamp = layers.some((l) => l.color?.field === "reference") ? referenceRamp ?? computeReferenceRamp(graph, rampDomain) : void 0;
169
+ const lift = layers.length > 0 && !layout.tubeMap ? walkLift(graph, layers, walkRamp) : void 0;
170
+ const facet = facetSettingOf(o.facet);
171
+ const panels = facet.field && lift && lift.walks.length > 1 ? facetLifts(graph, lift, layers, walkRamp) : void 0;
172
+ const labels = new Map(
173
+ (graph.paths?.length ? pathLegend(graph.paths) : []).map((p) => [
174
+ p.name,
175
+ p.label
176
+ ])
177
+ );
178
+ const labelOf = (name) => labels.get(name) ?? name;
179
+ const referenceName = o.referenceName ?? referenceLabel(graph, region);
180
+ const reference = lift?.referenceDomain && {
181
+ ...lift.referenceDomain,
182
+ name: referenceName
183
+ };
184
+ const contigThickness = o.contigThickness ?? 6;
185
+ const nodeWidth = o.nodeWidth ?? "depth";
186
+ const pins = o.genes?.length && !layout.tubeMap && !rows ? genePins(graph, o.genes, layout.nodePositions) : [];
187
+ const ink = nodeInk(graph, nodeById, contigThickness, nodeWidth);
188
+ let masks = 0;
189
+ function overlays(highlight, t, w, h) {
190
+ const out = [];
191
+ const outlines = exonOutlineTree(
192
+ exonStretches(pins, ink.halfWidthPx, highlight, (d) => screenPath(d, t)),
193
+ { id: `exons${masks++}`, width: w, height: h }
194
+ );
195
+ if (outlines) {
196
+ out.push(serializeEl(outlines));
197
+ }
198
+ const screen = (p) => ({
199
+ x: p.x * t.scaleX + t.translateX,
200
+ y: p.y * t.scaleY + t.translateY
201
+ });
202
+ const frame = { width: w, height: h };
203
+ for (const { item: pin, x, y, w: cw, text } of placeLabels(
204
+ geneLabelCandidates(
205
+ pins,
206
+ screen,
207
+ contigThickness,
208
+ nodesUnderBox(layout.nodePositions, t)
209
+ ),
210
+ frame,
211
+ occupancy(frame)
212
+ )) {
213
+ const pinY = screen(pin.at).y + contigThickness / 2;
214
+ out.push(
215
+ `<line x1="${x}" x2="${x}" y1="${y - LABEL_PX - 2}" y2="${pinY}" stroke="${GENE_INK}" stroke-width="0.8" stroke-opacity="0.6"/>`,
216
+ chip(x, y, cw, pin.gene.name, text.slice(pin.gene.name.length))
217
+ );
218
+ }
219
+ if (rows) {
220
+ out.push(
221
+ serializeEl(
222
+ walkRowsTree(
223
+ rows,
224
+ { ...t, width: w, height: h },
225
+ {
226
+ ramp: rampInterval,
227
+ rowGenes: o.rowGenes
228
+ }
229
+ )
230
+ )
231
+ );
232
+ }
233
+ const rowLabels = rows ? [rows.reference, ...rows.rows].map((r, i) => ({
234
+ label: r.label,
235
+ y: i * ROW_HEIGHT_PX
236
+ })) : layout.rowLabels ?? [];
237
+ for (const { label, y } of rowLabels) {
238
+ const sy = y * t.scaleY + t.translateY;
239
+ if (sy >= 0 && sy <= h) {
240
+ out.push(
241
+ `<text x="6" y="${sy + 4}" ${FONT} stroke="#fff" stroke-width="3" paint-order="stroke">${esc(label)}</text>`
242
+ );
243
+ }
244
+ }
245
+ return out.join("");
246
+ }
247
+ function drawing(highlight, w, h) {
248
+ const fit = fitTransform(
249
+ bounds,
250
+ w,
251
+ h,
252
+ pixelRows,
253
+ panels ? {
254
+ padLeft: FACET_PAD_PX,
255
+ padTop: FACET_PAD_PX,
256
+ padRight: FACET_PAD_PX,
257
+ padBottom: FACET_PAD_PX
258
+ } : {}
259
+ );
260
+ if (!fit) {
261
+ return "";
262
+ }
263
+ const axis = axisScaleOf(fit.scale, pixelRows);
264
+ const { canvas, markup } = svgCanvas(w, h);
265
+ const renderer = new Canvas2DRenderer(canvas);
266
+ renderer.uploadGeometry(
267
+ buildGeometry({
268
+ nodePositions: rows ? {} : layout.nodePositions,
269
+ graph,
270
+ nodeById,
271
+ colorScheme,
272
+ contigThickness,
273
+ connectorThickness: o.connectorThickness ?? 2,
274
+ drawPaths: false,
275
+ nodeWidth,
276
+ highlight,
277
+ axis,
278
+ referenceRamp,
279
+ deletions: new Map(deletions.map((d) => [d.edgeIndex, d.bypassed])),
280
+ deletionRoutes: layout.deletionRoutes,
281
+ stranded: layout.stranded,
282
+ hiddenEdges: new Set(
283
+ o.showDeletionEdges ? [] : deletions.map((d) => d.edgeIndex)
284
+ )
285
+ })
286
+ );
287
+ const t = {
288
+ scaleX: axis.scaleX,
289
+ scaleY: axis.scaleY,
290
+ translateX: fit.translateX,
291
+ translateY: fit.translateY
292
+ };
293
+ renderer.updateTransform({ ...t, dpr: 1 });
294
+ renderer.render([1, 1, 1, 1]);
295
+ return markup() + overlays(highlight, t, w, h);
296
+ }
297
+ const parts = [];
298
+ let height;
299
+ if (panels) {
300
+ const place = facetCells(
301
+ panels.map((p) => p.walks[0].name),
302
+ facet.field || "walk",
303
+ facet.domain
304
+ );
305
+ const grid = facetGrid({
306
+ count: place.count,
307
+ bounds,
308
+ pixelRows,
309
+ width,
310
+ room,
311
+ columns: place.columns ?? facet.columns
312
+ });
313
+ panels.forEach((panel, i) => {
314
+ const cell = place.cells[i];
315
+ const x = cell % grid.columns * (grid.width + FACET_GAP_PX);
316
+ const y = Math.floor(cell / grid.columns) * (grid.height + FACET_TITLE_PX + FACET_GAP_PX);
317
+ const walk = panel.walks[0];
318
+ parts.push(
319
+ walkKeySvg(`key${i}`, x + 6, y + 2, walk, labelOf(walk.name), reference).markup,
320
+ `<svg x="${x}" y="${y + FACET_TITLE_PX}" width="${grid.width}" height="${grid.height}">${drawing(panel, grid.width, grid.height)}</svg>`
321
+ );
322
+ });
323
+ height = grid.total;
324
+ } else {
325
+ let x = 6;
326
+ const push = (key) => {
327
+ parts.push(key.markup);
328
+ x += key.width + KEY_GAP_PX;
329
+ };
330
+ if (lift) {
331
+ lift.walks.forEach((walk, i) => {
332
+ push(walkKeySvg(`key${i}`, x, 2, walk, labelOf(walk.name), reference));
333
+ });
334
+ push(
335
+ keySvg(
336
+ "faded",
337
+ x,
338
+ 2,
339
+ [{ offset: 0, color: FADED }],
340
+ "",
341
+ `not on ${lift.walks.length === 1 ? labelOf(lift.walks[0].name) : "these walks"}`,
342
+ void 0,
343
+ 0.3
344
+ )
345
+ );
346
+ }
347
+ if (rows) {
348
+ const key = walkRowsKeyTree(
349
+ walkRowsKey(rows, {
350
+ ramp: rampInterval,
351
+ genes: o.rowGenes?.size ? o.rowGeneGaps : void 0
352
+ }),
353
+ x,
354
+ 2,
355
+ "rows-key"
356
+ );
357
+ push({ width: key.width, markup: serializeEl(key.tree) });
358
+ } else if (!lift && referenceRamp) {
359
+ push(
360
+ keySvg(
361
+ "ramp",
362
+ x,
363
+ 2,
364
+ encodingStops({ field: "reference", scheme: "rainbow" }),
365
+ referenceName ? `${referenceName} position` : "Reference position",
366
+ "",
367
+ rangeText(
368
+ referenceName,
369
+ referenceRamp.start,
370
+ referenceRamp.start + referenceRamp.span
371
+ )
372
+ )
373
+ );
374
+ }
375
+ if (pins.some((pin) => pin.exonsByNode.length > 0)) {
376
+ push(exonKeySvg(x, 2));
377
+ }
378
+ const header = parts.length > 0 ? FACET_TITLE_PX : 0;
379
+ const usable = width - 2 * FIT_PADDING;
380
+ const drawn = pixelRows || !(bounds.w > 0) ? bounds.h : bounds.h * usable / bounds.w;
381
+ const h = Math.max(
382
+ 160,
383
+ Math.min(room - header, Math.ceil(drawn + 2 * FIT_PADDING))
384
+ );
385
+ parts.push(
386
+ `<svg y="${header}" width="${width}" height="${h}">${drawing(lift, width, h)}</svg>`
387
+ );
388
+ height = header + h;
389
+ }
390
+ const strip = o.walkStrip;
391
+ if (strip && !rows && !layout.tubeMap) {
392
+ const frame = walkStripFrame(strip.rows, { width });
393
+ const key = walkRowsKeyTree(
394
+ walkRowsKey(strip.rows, {
395
+ ramp: rampInterval,
396
+ genes: stripGeneGaps(frame, strip.rowGenes, strip.rowGeneGaps)
397
+ }),
398
+ 6,
399
+ height + frame.height,
400
+ "strip-key"
401
+ );
402
+ parts.push(
403
+ `<line x1="0" x2="${width}" y1="${height + 0.5}" y2="${height + 0.5}" stroke="#ddd"/>`,
404
+ `<svg y="${height}" width="${width}" height="${frame.height}">${serializeEl(
405
+ walkRowsTree(strip.rows, frame, {
406
+ ramp: rampInterval,
407
+ rowGenes: strip.rowGenes,
408
+ idPrefix: "strip"
409
+ })
410
+ )}${serializeEl(
411
+ walkStripLabelsTree(
412
+ strip.rows,
413
+ frame,
414
+ new Set(layers.map((l) => l.walk))
415
+ )
416
+ )}</svg>`,
417
+ serializeEl(key.tree)
418
+ );
419
+ height += frame.height + STRIP_KEY_PX;
420
+ }
421
+ const metadata = JSON.stringify({
422
+ generator: `@jbrowse/bandage-core@${version}`,
423
+ ...o.spec === void 0 ? {} : { spec: o.spec }
424
+ });
425
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><metadata>${esc(metadata)}</metadata><rect width="${width}" height="${height}" fill="#fff"/>${parts.join("")}</svg>
426
+ `;
427
+ }
428
+
429
+ export {
430
+ figureSvg
431
+ };
@@ -0,0 +1,210 @@
1
+ import {
2
+ brightenAbgr
3
+ } from "./chunk-MNUSQCLG.js";
4
+
5
+ // src/renderer/Canvas2DRenderer.ts
6
+ import {
7
+ abgrToCssRgba,
8
+ normalizedRgbToCssRgba
9
+ } from "@jbrowse/core/util/colorBits";
10
+ import { syncCanvasSize } from "@jbrowse/render-core/canvas2dUtils";
11
+ import { Canvas2DRenderingBackendBase } from "@jbrowse/render-core/renderingBackendBase";
12
+ var Canvas2DRenderer = class extends Canvas2DRenderingBackendBase {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.transform = null;
16
+ this.batch = null;
17
+ this.nodeHighlights = /* @__PURE__ */ new Map();
18
+ this.highlightedEdge = null;
19
+ this.highlightFactor = 1;
20
+ }
21
+ // render-core's, not a local `width * devicePixelRatio`: it clamps the
22
+ // backing store at MAX_CANVAS_DIM_PX, reads the ratio through `getDpr()` so
23
+ // this agrees with the transform the model builds, and writes the css size
24
+ // independently of the backing size.
25
+ resize(width, height) {
26
+ syncCanvasSize(this.ctx.canvas, width, height);
27
+ }
28
+ uploadGeometry(batch) {
29
+ this.batch = batch;
30
+ this.highlightedEdge = null;
31
+ }
32
+ setNodeHighlights(factors) {
33
+ this.nodeHighlights = factors;
34
+ }
35
+ setEdgeHighlight(edgeIndex, factor) {
36
+ this.highlightedEdge = edgeIndex;
37
+ this.highlightFactor = factor;
38
+ }
39
+ updateTransform(transform) {
40
+ this.transform = transform;
41
+ }
42
+ render(clearColor) {
43
+ const t = this.transform;
44
+ if (!t) {
45
+ return;
46
+ }
47
+ const ctx = this.ctx;
48
+ const { width, height } = ctx.canvas;
49
+ if (clearColor[3] < 1) {
50
+ ctx.clearRect(0, 0, width, height);
51
+ }
52
+ ctx.fillStyle = normalizedRgbToCssRgba(
53
+ [clearColor[0], clearColor[1], clearColor[2]],
54
+ clearColor[3]
55
+ );
56
+ ctx.fillRect(0, 0, width, height);
57
+ const batch = this.batch;
58
+ if (!batch) {
59
+ return;
60
+ }
61
+ ctx.lineCap = "round";
62
+ ctx.lineJoin = "round";
63
+ this.renderEdges(batch);
64
+ this.renderNodes(batch);
65
+ this.renderArrows(batch);
66
+ }
67
+ renderEdges(batch) {
68
+ const t = this.transform;
69
+ const ctx = this.ctx;
70
+ const hl = this.highlightedEdge === null ? void 0 : batch.edgeCurveRuns.get(this.highlightedEdge);
71
+ for (const { color, items } of groupByPaint(batch.edgeCurves, (e, i) => ({
72
+ color: inRun(hl, i) ? brightenAbgr(e.color, this.highlightFactor) : e.color,
73
+ weight: e.thickness,
74
+ last: inRun(hl, i)
75
+ }))) {
76
+ ctx.strokeStyle = abgrToCssRgba(color);
77
+ ctx.lineWidth = items[0].thickness * 2 * t.dpr;
78
+ ctx.beginPath();
79
+ for (const e of items) {
80
+ const first = e.curves[0];
81
+ ctx.moveTo(
82
+ first.x0 * t.scaleX + t.translateX,
83
+ first.y0 * t.scaleY + t.translateY
84
+ );
85
+ for (const c of e.curves) {
86
+ ctx.bezierCurveTo(
87
+ c.cx0 * t.scaleX + t.translateX,
88
+ c.cy0 * t.scaleY + t.translateY,
89
+ c.cx1 * t.scaleX + t.translateX,
90
+ c.cy1 * t.scaleY + t.translateY,
91
+ c.x1 * t.scaleX + t.translateX,
92
+ c.y1 * t.scaleY + t.translateY
93
+ );
94
+ }
95
+ }
96
+ ctx.stroke();
97
+ }
98
+ }
99
+ renderNodes(batch) {
100
+ const t = this.transform;
101
+ const ctx = this.ctx;
102
+ const lifted = /* @__PURE__ */ new Map();
103
+ for (const [nodeId, factor] of this.nodeHighlights) {
104
+ const run = batch.nodeStrokeRuns.get(nodeId);
105
+ if (run) {
106
+ for (let i = run.start; i < run.start + run.count; i++) {
107
+ lifted.set(i, factor);
108
+ }
109
+ }
110
+ }
111
+ for (const { color, items } of groupByPaint(batch.nodeStrokes, (s, i) => {
112
+ const factor = lifted.get(i);
113
+ return {
114
+ color: factor === void 0 ? s.color : brightenAbgr(s.color, factor),
115
+ weight: s.thickness,
116
+ last: factor !== void 0
117
+ };
118
+ })) {
119
+ ctx.strokeStyle = abgrToCssRgba(color);
120
+ ctx.lineWidth = items[0].thickness * 2 * t.dpr;
121
+ ctx.beginPath();
122
+ for (const s of items) {
123
+ const p0 = s.points[0];
124
+ ctx.moveTo(
125
+ p0.x * t.scaleX + t.translateX,
126
+ p0.y * t.scaleY + t.translateY
127
+ );
128
+ for (let i = 1, l = s.points.length; i < l; i++) {
129
+ const p = s.points[i];
130
+ ctx.lineTo(
131
+ p.x * t.scaleX + t.translateX,
132
+ p.y * t.scaleY + t.translateY
133
+ );
134
+ }
135
+ }
136
+ ctx.stroke();
137
+ }
138
+ }
139
+ renderArrows(batch) {
140
+ const t = this.transform;
141
+ const ctx = this.ctx;
142
+ const hl = this.highlightedEdge === null ? void 0 : batch.arrowRuns.get(this.highlightedEdge);
143
+ for (const { color, items } of groupByPaint(batch.arrows, (a, i) => ({
144
+ color: inRun(hl, i) ? brightenAbgr(a.color, this.highlightFactor) : a.color,
145
+ weight: 0,
146
+ last: inRun(hl, i)
147
+ }))) {
148
+ ctx.fillStyle = abgrToCssRgba(color);
149
+ ctx.beginPath();
150
+ for (const a of items) {
151
+ const [tip, left, notch, right] = arrowheadOutline(a, t);
152
+ ctx.moveTo(tip.x, tip.y);
153
+ ctx.lineTo(left.x, left.y);
154
+ ctx.lineTo(notch.x, notch.y);
155
+ ctx.lineTo(right.x, right.y);
156
+ ctx.closePath();
157
+ }
158
+ ctx.fill();
159
+ }
160
+ }
161
+ dispose() {
162
+ super.dispose();
163
+ this.batch = null;
164
+ this.nodeHighlights = /* @__PURE__ */ new Map();
165
+ this.highlightedEdge = null;
166
+ }
167
+ };
168
+ var ARROW_NOTCH = 0.25;
169
+ function arrowheadOutline(a, t) {
170
+ const ux = Math.cos(a.angle);
171
+ const uy = Math.sin(a.angle);
172
+ const tipX = a.x * t.scaleX + t.translateX;
173
+ const tipY = a.y * t.scaleY + t.translateY;
174
+ const length = a.length * t.dpr;
175
+ const halfWidth = a.halfWidth * t.dpr;
176
+ const backX = tipX - ux * length;
177
+ const backY = tipY - uy * length;
178
+ const notchBack = length * (1 - ARROW_NOTCH);
179
+ return [
180
+ { x: tipX, y: tipY },
181
+ { x: backX - uy * halfWidth, y: backY + ux * halfWidth },
182
+ { x: tipX - ux * notchBack, y: tipY - uy * notchBack },
183
+ { x: backX + uy * halfWidth, y: backY - ux * halfWidth }
184
+ ];
185
+ }
186
+ function inRun(run, i) {
187
+ return run !== void 0 && i >= run.start && i < run.start + run.count;
188
+ }
189
+ function groupByPaint(items, paintOf) {
190
+ const groups = /* @__PURE__ */ new Map();
191
+ const deferred = /* @__PURE__ */ new Map();
192
+ for (let i = 0, l = items.length; i < l; i++) {
193
+ const item = items[i];
194
+ const { color, weight, last } = paintOf(item, i);
195
+ const into = last ? deferred : groups;
196
+ const key = `${color}:${weight}`;
197
+ const group = into.get(key);
198
+ if (group) {
199
+ group.items.push(item);
200
+ } else {
201
+ into.set(key, { color, items: [item] });
202
+ }
203
+ }
204
+ return [...groups.values(), ...deferred.values()];
205
+ }
206
+
207
+ export {
208
+ Canvas2DRenderer,
209
+ arrowheadOutline
210
+ };
@@ -0,0 +1,52 @@
1
+ // src/tubeMap/mismatches.ts
2
+ import { forward, getXCoordinateOfBaseWithinNode } from "@gmod/tubemap-core";
3
+ function readMismatches(read, layout) {
4
+ const marks = [];
5
+ const entries = read.sequenceNew ?? [];
6
+ entries.forEach((entry, i) => {
7
+ const nodeIndex = layout.nodeMap.get(forward(entry.nodeName));
8
+ const node = nodeIndex === void 0 ? void 0 : layout.nodes[nodeIndex];
9
+ let pathIndex = i;
10
+ while (pathIndex < read.path.length && read.path[pathIndex].node !== nodeIndex) {
11
+ pathIndex += 1;
12
+ }
13
+ const y = read.path[pathIndex]?.y;
14
+ if (node && y !== void 0) {
15
+ const base = { readId: read.id, y, height: read.width, nodeY: node.y };
16
+ const at = (pos) => getXCoordinateOfBaseWithinNode(node, pos);
17
+ for (const mm of entry.mismatches) {
18
+ const x0 = at(mm.pos);
19
+ if (x0 !== null) {
20
+ if (mm.type === "insertion") {
21
+ const softClip = i === 0 && mm.pos === read.firstNodeOffset || i === entries.length - 1 && mm.pos === read.finalNodeCoverLength;
22
+ marks.push({
23
+ ...base,
24
+ kind: "insertion",
25
+ x: x0,
26
+ seq: mm.seq,
27
+ softClip
28
+ });
29
+ } else if (mm.type === "deletion" && mm.length !== void 0) {
30
+ const x1 = at(mm.pos + mm.length);
31
+ if (x1 !== null) {
32
+ marks.push({ ...base, kind: "deletion", x0, x1 });
33
+ }
34
+ } else if (mm.type === "substitution" && mm.seq !== void 0) {
35
+ const x1 = at(mm.pos + mm.seq.length);
36
+ if (x1 !== null) {
37
+ marks.push({ ...base, kind: "substitution", x0, x1, seq: mm.seq });
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ });
44
+ return marks;
45
+ }
46
+ function tubeMapMismatches(layout) {
47
+ return layout.reads.flatMap((read) => readMismatches(read, layout));
48
+ }
49
+
50
+ export {
51
+ tubeMapMismatches
52
+ };