@jbrowse/bandage-core 6.0.1 → 6.0.3

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-2CVHXFK7.js +530 -0
  11. package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
  12. package/dist/chunks/chunk-47DYCZOK.js +115 -0
  13. package/dist/chunks/chunk-4EMPBO7S.js +117 -0
  14. package/dist/chunks/chunk-4S47XRYA.js +149 -0
  15. package/dist/chunks/chunk-5XQDMQXU.js +431 -0
  16. package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
  17. package/dist/chunks/chunk-6SBUFFTB.js +97 -0
  18. package/dist/chunks/chunk-6SXMCK6I.js +476 -0
  19. package/dist/chunks/chunk-72N5DJHZ.js +155 -0
  20. package/dist/chunks/chunk-73RSLGZT.js +73 -0
  21. package/dist/chunks/chunk-7XHHVYG7.js +13 -0
  22. package/dist/chunks/chunk-AB35TJST.js +67 -0
  23. package/dist/chunks/chunk-BPLIXMH7.js +52 -0
  24. package/dist/chunks/chunk-BUBZ32IC.js +101 -0
  25. package/dist/chunks/chunk-BXEPA4KS.js +179 -0
  26. package/dist/chunks/chunk-C44YRP5F.js +85 -0
  27. package/dist/chunks/chunk-CDLPYR5O.js +135 -0
  28. package/dist/chunks/chunk-CJ2EE4AF.js +40 -0
  29. package/dist/chunks/chunk-CPMHA4W5.js +6 -0
  30. package/dist/chunks/chunk-CR7EGAFE.js +241 -0
  31. package/dist/chunks/chunk-CS3E4ETM.js +202 -0
  32. package/dist/chunks/chunk-CTSBC2O6.js +90 -0
  33. package/dist/chunks/chunk-D3M7SBKL.js +210 -0
  34. package/dist/chunks/chunk-DKOB2OPC.js +177 -0
  35. package/dist/chunks/chunk-EJ5OZTCK.js +52 -0
  36. package/dist/chunks/chunk-EK4HGYO7.js +174 -0
  37. package/dist/chunks/chunk-EPZORFTI.js +23 -0
  38. package/dist/chunks/chunk-ES2N6V56.js +83 -0
  39. package/dist/chunks/chunk-EZPBJPK6.js +54 -0
  40. package/dist/chunks/chunk-FYQWPEEY.js +65 -0
  41. package/dist/chunks/chunk-GGQRADFS.js +170 -0
  42. package/dist/chunks/chunk-GOY6REMT.js +43 -0
  43. package/dist/chunks/chunk-GQ2AOHU4.js +112 -0
  44. package/dist/chunks/chunk-HNQPEMYE.js +297 -0
  45. package/dist/chunks/chunk-IGVV37VI.js +60 -0
  46. package/dist/chunks/chunk-IO2PCPZI.js +218 -0
  47. package/dist/chunks/chunk-ISEI2FH3.js +108 -0
  48. package/dist/chunks/chunk-JKJWSY4V.js +32 -0
  49. package/dist/chunks/chunk-KQFOCDFY.js +73 -0
  50. package/dist/chunks/chunk-L3O7AWLI.js +52 -0
  51. package/dist/chunks/chunk-LLJUG2H7.js +6 -0
  52. package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
  53. package/dist/chunks/chunk-LTUWJKHD.js +112 -0
  54. package/dist/chunks/chunk-MFKORG3F.js +44 -0
  55. package/dist/chunks/chunk-MI35EUIE.js +76 -0
  56. package/dist/chunks/chunk-MNUSQCLG.js +29 -0
  57. package/dist/chunks/chunk-MQJUG2D2.js +53 -0
  58. package/dist/chunks/chunk-MREQ7GIA.js +181 -0
  59. package/dist/chunks/chunk-N62KBLFC.js +326 -0
  60. package/dist/chunks/chunk-NNODIW4U.js +26 -0
  61. package/dist/chunks/chunk-PBENCR5L.js +60 -0
  62. package/dist/chunks/chunk-PCZ4PTYC.js +726 -0
  63. package/dist/chunks/chunk-PUG7LXDF.js +70 -0
  64. package/dist/chunks/chunk-QYBZZI5H.js +0 -0
  65. package/dist/chunks/chunk-R6ZUK4Y3.js +232 -0
  66. package/dist/chunks/chunk-RAKOB6IV.js +169 -0
  67. package/dist/chunks/chunk-ROF2R3RF.js +205 -0
  68. package/dist/chunks/chunk-RVS2QCOX.js +135 -0
  69. package/dist/chunks/chunk-ST2IBUAN.js +119 -0
  70. package/dist/chunks/chunk-SZNZ2T6D.js +198 -0
  71. package/dist/chunks/chunk-T43W7NKK.js +122 -0
  72. package/dist/chunks/chunk-TAMCZHLW.js +190 -0
  73. package/dist/chunks/chunk-TDQLINVI.js +16 -0
  74. package/dist/chunks/chunk-TEDQJPMI.js +131 -0
  75. package/dist/chunks/chunk-TZFWUT2C.js +66 -0
  76. package/dist/chunks/chunk-UVBGRJQX.js +50 -0
  77. package/dist/chunks/chunk-VHW7FDHU.js +42 -0
  78. package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
  79. package/dist/chunks/chunk-WFX3MAKA.js +102 -0
  80. package/dist/chunks/chunk-WOO7QYKB.js +121 -0
  81. package/dist/chunks/chunk-WSIFAZU7.js +132 -0
  82. package/dist/chunks/chunk-X5L37W4J.js +82 -0
  83. package/dist/chunks/chunk-Y3IOTZJA.js +61 -0
  84. package/dist/chunks/chunk-YMSSF2RK.js +257 -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,431 @@
1
+ import {
2
+ svgCanvas
3
+ } from "./chunk-47DYCZOK.js";
4
+ import {
5
+ stripGeneGaps,
6
+ walkStripFrame,
7
+ walkStripLabelsTree
8
+ } from "./chunk-ROF2R3RF.js";
9
+ import {
10
+ walkRowsKey,
11
+ walkRowsKeyTree,
12
+ walkRowsTree
13
+ } from "./chunk-6SXMCK6I.js";
14
+ import {
15
+ Canvas2DRenderer
16
+ } from "./chunk-D3M7SBKL.js";
17
+ import {
18
+ genePins
19
+ } from "./chunk-BUBZ32IC.js";
20
+ import {
21
+ EXON_COLOR,
22
+ exonOutlineTree,
23
+ exonStretches
24
+ } from "./chunk-3S3YVOJ7.js";
25
+ import {
26
+ version
27
+ } from "./chunk-LLJUG2H7.js";
28
+ import {
29
+ axisScaleOf
30
+ } from "./chunk-MQJUG2D2.js";
31
+ import {
32
+ facetLifts,
33
+ walkLift
34
+ } from "./chunk-GGQRADFS.js";
35
+ import {
36
+ encodingStops
37
+ } from "./chunk-WFX3MAKA.js";
38
+ import {
39
+ rangeText,
40
+ walkKey
41
+ } from "./chunk-GOY6REMT.js";
42
+ import {
43
+ FIT_PADDING,
44
+ drawingBounds,
45
+ fitTransform
46
+ } from "./chunk-IO2PCPZI.js";
47
+ import {
48
+ buildGeometry,
49
+ computeReferenceRamp
50
+ } from "./chunk-PCZ4PTYC.js";
51
+ import {
52
+ geneLabelCandidates
53
+ } from "./chunk-DKOB2OPC.js";
54
+ import {
55
+ nodesUnderBox
56
+ } from "./chunk-YMSSF2RK.js";
57
+ import {
58
+ walkRowsExtent
59
+ } from "./chunk-EZPBJPK6.js";
60
+ import {
61
+ pathLegend
62
+ } from "./chunk-X5L37W4J.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-4EMPBO7S.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,90 @@
1
+ // src/layout/drawnScale.ts
2
+ var MEAN_NODE_LENGTH = 40;
3
+ var MIN_TOTAL_GRAPH_LENGTH = 500;
4
+ var BANDAGE_MINIMUM_NODE_LENGTH = 5;
5
+ function drawnNodeLength(opts, length) {
6
+ return Math.max(
7
+ opts.nodeLengthPerMegabase * length / 1e6,
8
+ opts.minimumNodeLength
9
+ );
10
+ }
11
+ function bandageAutoScale(graph, minNodeLength = BANDAGE_MINIMUM_NODE_LENGTH) {
12
+ const totalLength = graph.nodes.reduce((sum, n) => sum + n.length, 0);
13
+ const megabases = totalLength / 1e6;
14
+ const target = Math.max(
15
+ graph.nodes.length * MEAN_NODE_LENGTH,
16
+ MIN_TOTAL_GRAPH_LENGTH
17
+ );
18
+ return {
19
+ nodeLengthPerMegabase: megabases > 0 ? target / megabases : 1e4,
20
+ minimumNodeLength: Math.max(BANDAGE_MINIMUM_NODE_LENGTH, minNodeLength),
21
+ edgeLength: 5,
22
+ nodeSegmentLength: 20
23
+ };
24
+ }
25
+ var PROPORTIONAL_LENGTH = { power: 1, effect: 1 };
26
+ function isProportional(law) {
27
+ return law.power === 1 && law.effect === 1;
28
+ }
29
+ function drawnLengthFor(graph, law) {
30
+ const count = graph.nodes.length;
31
+ const totalLength = graph.nodes.reduce((sum, n) => sum + n.length, 0);
32
+ const meanBp = count > 0 ? totalLength / count : 0;
33
+ const meanDrawn = count > 0 ? Math.max(count * MEAN_NODE_LENGTH, MIN_TOTAL_GRAPH_LENGTH) / count : MEAN_NODE_LENGTH;
34
+ return (bp) => {
35
+ if (meanBp <= 0) {
36
+ return meanDrawn;
37
+ }
38
+ const relative = (bp / meanBp) ** law.power;
39
+ return Math.max(
40
+ meanDrawn * ((relative - 1) * law.effect + 1),
41
+ BANDAGE_MINIMUM_NODE_LENGTH
42
+ );
43
+ };
44
+ }
45
+ var LAYOUT_LENGTH_PRECISION = 1e3;
46
+ var PROPORTIONAL_SPREAD = {
47
+ law: PROPORTIONAL_LENGTH,
48
+ minNodeLength: 0
49
+ };
50
+ function layoutScaling(graph, spread = PROPORTIONAL_SPREAD) {
51
+ const { law, minNodeLength } = spread;
52
+ const opts = bandageAutoScale(graph, minNodeLength);
53
+ if (isProportional(law)) {
54
+ return { nodes: graph.nodes, opts };
55
+ }
56
+ const drawn = drawnLengthFor(graph, law);
57
+ return {
58
+ nodes: graph.nodes.map((node) => ({
59
+ ...node,
60
+ length: Math.round(drawn(node.length) * LAYOUT_LENGTH_PRECISION)
61
+ })),
62
+ opts: {
63
+ ...opts,
64
+ nodeLengthPerMegabase: 1e6 / LAYOUT_LENGTH_PRECISION,
65
+ // Bandage's own floor only. A law and a raised floor are alternatives
66
+ // rather than a stack: the law has already lifted the small end, and a
67
+ // floor on top of it would flatten exactly what it just spread.
68
+ minimumNodeLength: BANDAGE_MINIMUM_NODE_LENGTH
69
+ }
70
+ };
71
+ }
72
+ function drawnLengthOf(graph, spread = PROPORTIONAL_SPREAD) {
73
+ if (isProportional(spread.law)) {
74
+ const opts = bandageAutoScale(graph, spread.minNodeLength);
75
+ return (bp) => drawnNodeLength(opts, bp);
76
+ }
77
+ return drawnLengthFor(graph, spread.law);
78
+ }
79
+
80
+ export {
81
+ MEAN_NODE_LENGTH,
82
+ drawnNodeLength,
83
+ bandageAutoScale,
84
+ PROPORTIONAL_LENGTH,
85
+ isProportional,
86
+ drawnLengthFor,
87
+ PROPORTIONAL_SPREAD,
88
+ layoutScaling,
89
+ drawnLengthOf
90
+ };
@@ -0,0 +1,97 @@
1
+ // src/facetGrid.ts
2
+ var FACET_GAP_PX = 8;
3
+ var FACET_TITLE_PX = 34;
4
+ var FACET_PAD_PX = 12;
5
+ var MIN_FACET_PANEL_PX = 60;
6
+ function facetGrid({
7
+ count,
8
+ bounds,
9
+ pixelRows,
10
+ width,
11
+ room,
12
+ columns
13
+ }) {
14
+ function tile(across) {
15
+ const rows = Math.ceil(count / across);
16
+ const panelWidth = Math.floor(
17
+ (width - (across - 1) * FACET_GAP_PX) / across
18
+ );
19
+ const panelRoom = (room - rows * FACET_TITLE_PX - (rows - 1) * FACET_GAP_PX) / rows;
20
+ const fitX = (panelWidth - 2 * FACET_PAD_PX) / bounds.w;
21
+ const fitY = bounds.h > 0 ? (panelRoom - 2 * FACET_PAD_PX) / bounds.h : Infinity;
22
+ const scale = pixelRows ? fitX : Math.min(fitX, fitY);
23
+ const drawn = pixelRows ? bounds.h : bounds.h * scale;
24
+ const height = Math.floor(
25
+ Math.max(
26
+ MIN_FACET_PANEL_PX,
27
+ Math.min(drawn + 2 * FACET_PAD_PX, panelRoom)
28
+ )
29
+ );
30
+ return {
31
+ columns: across,
32
+ width: panelWidth,
33
+ height,
34
+ scale,
35
+ total: rows * (height + FACET_TITLE_PX) + (rows - 1) * FACET_GAP_PX
36
+ };
37
+ }
38
+ if (columns !== void 0) {
39
+ return tile(Math.max(1, Math.min(count, Math.round(columns))));
40
+ }
41
+ let best = tile(1);
42
+ for (let across = 2; across <= count; across++) {
43
+ const grid = tile(across);
44
+ if (grid.scale > best.scale) {
45
+ best = grid;
46
+ }
47
+ }
48
+ return best;
49
+ }
50
+ function facetSettingOf(facet) {
51
+ const raw = typeof facet === "string" ? { field: facet } : facet ?? {};
52
+ const field = raw.field === "walk" || raw.field === "sample" ? raw.field : "";
53
+ return {
54
+ field,
55
+ domain: [...raw.domain ?? []],
56
+ ...raw.columns === void 0 ? {} : { columns: raw.columns }
57
+ };
58
+ }
59
+ function domainFirst(names, domain) {
60
+ const rank = new Map(domain.map((name, i) => [name, i]));
61
+ const listed = (name) => rank.get(name) ?? domain.length;
62
+ return names.map((name, i) => ({ name, i })).sort((a, b) => listed(a.name) - listed(b.name) || a.i - b.i).map(({ name }) => name);
63
+ }
64
+ function facetCells(names, by, domain = []) {
65
+ const wrapOrder = domainFirst(names, domain);
66
+ const wrap = {
67
+ columns: void 0,
68
+ count: names.length,
69
+ cells: names.map((name) => wrapOrder.indexOf(name))
70
+ };
71
+ if (by === "walk") {
72
+ return wrap;
73
+ }
74
+ const places = names.map((name) => {
75
+ const [sample, haplotype, contig] = name.split("#");
76
+ const n = Number(haplotype);
77
+ return contig !== void 0 && Number.isInteger(n) && n >= 0 ? { sample, column: Math.max(0, n - 1) } : void 0;
78
+ });
79
+ const rows = domainFirst(
80
+ [...new Set(places.flatMap((p) => p ? [p.sample] : []))],
81
+ domain
82
+ );
83
+ const columns = Math.max(1, ...places.map((p) => p ? p.column + 1 : 1));
84
+ const cells = places.map(
85
+ (p) => p ? rows.indexOf(p.sample) * columns + p.column : -1
86
+ );
87
+ return cells.includes(-1) || new Set(cells).size < cells.length ? wrap : { columns, count: rows.length * columns, cells };
88
+ }
89
+
90
+ export {
91
+ FACET_GAP_PX,
92
+ FACET_TITLE_PX,
93
+ FACET_PAD_PX,
94
+ facetGrid,
95
+ facetSettingOf,
96
+ facetCells
97
+ };