@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
package/dist/index.js CHANGED
@@ -1,1437 +1,292 @@
1
+ import {
2
+ wheelZoomFactor
3
+ } from "./chunks/chunk-TDQLINVI.js";
4
+ import {
5
+ tubeMapFrame,
6
+ tubeMapNodeAt
7
+ } from "./chunks/chunk-VZQ4FKLR.js";
8
+ import {
9
+ drawTubeMapGenes,
10
+ tubeMapGenes
11
+ } from "./chunks/chunk-57ZIXHZA.js";
12
+ import {
13
+ tubeMapNodeColors
14
+ } from "./chunks/chunk-YJQ7CT7F.js";
15
+ import {
16
+ referenceKnots,
17
+ warpX
18
+ } from "./chunks/chunk-KQFOCDFY.js";
19
+ import {
20
+ drawTubeMapRuler,
21
+ referenceBoxes,
22
+ rulerBoxes,
23
+ tubeX
24
+ } from "./chunks/chunk-Q3KCTA4N.js";
25
+ import {
26
+ connectorAt,
27
+ drawTubeMapConnectors,
28
+ referenceNodes,
29
+ tubeMapConnectors
30
+ } from "./chunks/chunk-GQ2AOHU4.js";
31
+ import {
32
+ drawTubeMap,
33
+ tubeMapPicture
34
+ } from "./chunks/chunk-CR7EGAFE.js";
35
+ import "./chunks/chunk-EJ5OZTCK.js";
36
+ import {
37
+ bubbleHalos
38
+ } from "./chunks/chunk-3C2LGJKY.js";
39
+ import {
40
+ bubblesFromGraph
41
+ } from "./chunks/chunk-4S47XRYA.js";
42
+ import {
43
+ BUBBLE_KIND_COLORS,
44
+ BUBBLE_KIND_NAMES,
45
+ bubbleSegmentIds,
46
+ classifyBubble
47
+ } from "./chunks/chunk-WOO7QYKB.js";
48
+ import "./chunks/chunk-MFKORG3F.js";
49
+ import {
50
+ bubbleSubgraph
51
+ } from "./chunks/chunk-VHW7FDHU.js";
52
+ import {
53
+ genesFromBed,
54
+ genesFromGff3Lines,
55
+ genesFromText,
56
+ mergeOverlappingByName
57
+ } from "./chunks/chunk-BXEPA4KS.js";
58
+ import {
59
+ REFERENCE_STRIP_ZONE_PX,
60
+ drawReferenceStrip,
61
+ referenceStripBlocks,
62
+ stripBlockAt
63
+ } from "./chunks/chunk-HIJVRRYE.js";
64
+ import {
65
+ FORCE_LAYOUT_LABEL,
66
+ LAYOUT_MODES,
67
+ layoutModeByValue,
68
+ modeUsesLayoutEngine
69
+ } from "./chunks/chunk-527AI7CP.js";
70
+ import "./chunks/chunk-P7B3GDAY.js";
71
+ import "./chunks/chunk-TEDQJPMI.js";
72
+ import "./chunks/chunk-LOEA5IHZ.js";
73
+ import "./chunks/chunk-L3O7AWLI.js";
74
+ import "./chunks/chunk-TZFWUT2C.js";
75
+ import "./chunks/chunk-HNQPEMYE.js";
76
+ import "./chunks/chunk-C44YRP5F.js";
77
+ import "./chunks/chunk-RVS2QCOX.js";
78
+ import {
79
+ loadBandage
80
+ } from "./chunks/chunk-7XHHVYG7.js";
81
+ import {
82
+ figureSvg
83
+ } from "./chunks/chunk-CZ54VG2H.js";
84
+ import {
85
+ svgCanvas
86
+ } from "./chunks/chunk-47DYCZOK.js";
87
+ import {
88
+ LABELLED_ROW_PX,
89
+ WALK_STRIP_CEILING_PX,
90
+ segmentAt,
91
+ stripGeneGaps,
92
+ stripMarks,
93
+ stripRowAt,
94
+ walkMarksTree,
95
+ walkStripFrame,
96
+ walkStripLabelsTree
97
+ } from "./chunks/chunk-MHGHYHTL.js";
1
98
  import {
2
99
  BAR_PX,
3
- BUBBLE_SPREADS,
4
- COLOR_SCHEMES,
5
- Canvas2DRenderer,
6
100
  DISAGREES,
7
- EXON_COLOR,
8
- FACET_GAP_PX,
9
- FACET_PAD_PX,
10
- FACET_TITLE_PX,
11
- FIT_PADDING,
12
- FORCE_LAYOUT_LABEL,
13
101
  GAP_PX,
14
102
  GENE_INK,
15
- HALO_FACTOR,
16
- HPRC_GBZ,
17
- LABELLED_ROW_PX,
18
- LABEL_CHAR_PX,
19
- LABEL_PAD,
20
- LABEL_PX,
21
- LAYOUT_MODES,
22
- LEGEND_INSET_PX,
23
- LIFT_BACKDROP_CSS,
24
- NODE_WIDTHS,
25
103
  OFF_REFERENCE,
26
104
  ON_REFERENCE,
27
105
  OUTSIDE_CUT,
28
- RAMP_GRADIENT_CSS,
29
- REFERENCE_RAMP_MAX_HUE,
30
- ROW_HEIGHT_PX,
31
- WALK_FIELDS,
32
- WALK_SCHEMES,
33
- WALK_STRIP_CEILING_PX,
34
- WELL_KNOWN_SAMPLES,
35
106
  alongRow,
36
- assemblyWalk,
37
- axisScaleOf,
38
- backboneAssembly,
39
- buildGeometry,
40
- clampZoom,
41
107
  coalesceRuns,
42
- computeColorSchemeRange,
43
- computeReferenceRamp,
108
+ genesOnRow,
109
+ placeRowGenes,
110
+ readoutPlacement,
111
+ rowGeneBoxes,
112
+ rowSpan,
113
+ runPaint,
114
+ unitTicks,
115
+ walkRowReadout,
116
+ walkRowsKey,
117
+ walkRowsKeyTree,
118
+ walkRowsTree
119
+ } from "./chunks/chunk-QCYM44UL.js";
120
+ import {
121
+ Canvas2DRenderer
122
+ } from "./chunks/chunk-D3M7SBKL.js";
123
+ import {
124
+ genePins
125
+ } from "./chunks/chunk-SV27AJCC.js";
126
+ import {
127
+ EXON_COLOR,
128
+ exonOutlineTree,
129
+ exonStretches
130
+ } from "./chunks/chunk-3S3YVOJ7.js";
131
+ import {
132
+ version
133
+ } from "./chunks/chunk-KCW7L3NV.js";
134
+ import {
135
+ axisScaleOf,
44
136
  contains,
45
- cutGbzRegion,
46
- cutWindowGFA,
47
- deletionEdges,
48
- drawingBounds,
49
- el,
137
+ padded,
138
+ screenToLayout,
139
+ viewportOf,
140
+ zoomAbout
141
+ } from "./chunks/chunk-L5RYUPZI.js";
142
+ import {
143
+ facetLifts,
144
+ walkHighlight,
145
+ walkLift
146
+ } from "./chunks/chunk-6Q6HFBJT.js";
147
+ import {
148
+ WALK_FIELDS,
149
+ WALK_SCHEMES,
50
150
  encodingStops,
51
151
  encodingSwatchCss,
152
+ resolveEncoding,
153
+ schemeCss
154
+ } from "./chunks/chunk-IMW3AK27.js";
155
+ import {
156
+ rangeText,
157
+ walkKey,
158
+ walkPosition
159
+ } from "./chunks/chunk-AE3UQZY3.js";
160
+ import {
161
+ FIT_PADDING,
162
+ clampZoom,
163
+ drawingBounds,
52
164
  engineKey,
53
165
  engineRequest,
54
- exonOutlineTree,
55
- exonStretches,
56
- facetCells,
57
- facetGrid,
58
- facetLifts,
59
- facetSettingOf,
60
- featuresOnBackbone,
61
- figureSvg,
62
- filterSamples,
63
- findHoveredEdge,
64
- findHoveredNode,
65
166
  fitTransform,
66
167
  forceLayout,
67
- formatBp,
68
- geneCoverageNote,
69
- genePins,
70
- genesFromBed,
71
- genesFromGff3Lines,
72
- genesFromText,
73
- genesOnRow,
74
- getNodeColor,
75
- graphBackbone,
76
- haplotypeWanted,
77
- isBackbone,
78
168
  layoutExtent,
79
- layoutLabels,
80
- layoutModeByValue,
81
- loadBandage,
82
- loadGraph,
169
+ loadGraph
170
+ } from "./chunks/chunk-VKB57ZYE.js";
171
+ import "./chunks/chunk-PUG7LXDF.js";
172
+ import "./chunks/chunk-IGVV37VI.js";
173
+ import "./chunks/chunk-72N5DJHZ.js";
174
+ import "./chunks/chunk-QYBZZI5H.js";
175
+ import "./chunks/chunk-CDLPYR5O.js";
176
+ import "./chunks/chunk-MI35EUIE.js";
177
+ import {
178
+ RAMP_GRADIENT_CSS,
179
+ rampHue,
180
+ rampHueCss,
181
+ rampStops
182
+ } from "./chunks/chunk-5QT6NPSO.js";
183
+ import {
184
+ REFERENCE_RAMP_MAX_HUE,
185
+ buildGeometry,
186
+ computeReferenceRamp
187
+ } from "./chunks/chunk-3WHKR5TY.js";
188
+ import "./chunks/chunk-MNUSQCLG.js";
189
+ import {
190
+ HALO_FACTOR,
191
+ LEGEND_INSET_PX,
192
+ geneCoverageNote,
193
+ layoutLabels
194
+ } from "./chunks/chunk-HB6PDCOU.js";
195
+ import {
196
+ findHoveredEdge,
197
+ findHoveredNode
198
+ } from "./chunks/chunk-BJV2W4KZ.js";
199
+ import "./chunks/chunk-LVZZH2NI.js";
200
+ import "./chunks/chunk-6YRRNX2P.js";
201
+ import {
202
+ walkRowsExtent
203
+ } from "./chunks/chunk-EZPBJPK6.js";
204
+ import {
205
+ filterSamples,
206
+ walkRows
207
+ } from "./chunks/chunk-SZNZ2T6D.js";
208
+ import {
209
+ pathColorsLegible,
210
+ pathLegend
211
+ } from "./chunks/chunk-7CHJKABZ.js";
212
+ import "./chunks/chunk-ST2IBUAN.js";
213
+ import "./chunks/chunk-ES2N6V56.js";
214
+ import {
215
+ formatBp
216
+ } from "./chunks/chunk-HCANBJDD.js";
217
+ import {
218
+ LABEL_CHAR_PX,
219
+ LABEL_PAD,
220
+ LABEL_PX
221
+ } from "./chunks/chunk-AB35TJST.js";
222
+ import "./chunks/chunk-SDGM243E.js";
223
+ import {
224
+ NODE_WIDTHS,
83
225
  maxNodeWidthPx,
84
226
  meanDepth,
85
- mergeOverlappingByName,
86
- modeUsesLayoutEngine,
87
227
  nodeInk,
88
- nodeWidthPx,
228
+ nodeWidthPx
229
+ } from "./chunks/chunk-PBENCR5L.js";
230
+ import {
231
+ BUBBLE_SPREADS
232
+ } from "./chunks/chunk-UVBGRJQX.js";
233
+ import "./chunks/chunk-6JEWNXCQ.js";
234
+ import {
235
+ COLOR_SCHEMES,
236
+ resolveColorScheme
237
+ } from "./chunks/chunk-JKJWSY4V.js";
238
+ import {
239
+ deletionEdges
240
+ } from "./chunks/chunk-X32PABB7.js";
241
+ import "./chunks/chunk-3PLYZLYV.js";
242
+ import {
243
+ ROW_HEIGHT_PX
244
+ } from "./chunks/chunk-CPMHA4W5.js";
245
+ import {
246
+ el,
247
+ serializeEl
248
+ } from "./chunks/chunk-NNODIW4U.js";
249
+ import {
250
+ FACET_GAP_PX,
251
+ FACET_PAD_PX,
252
+ FACET_TITLE_PX,
253
+ facetCells,
254
+ facetGrid,
255
+ facetSettingOf
256
+ } from "./chunks/chunk-6SBUFFTB.js";
257
+ import {
258
+ HPRC_GBZ,
259
+ cutGbzRegion,
89
260
  openGbz,
90
- padded,
91
- panSNContig,
92
- panSNHaplotype,
93
- panSNSample,
94
- parseRegion,
95
- pathColorsLegible,
96
- pathLegend,
97
- placeRowGenes,
98
- rampHue,
99
- rampHueCss,
100
- rampStops,
101
- rangeText,
102
- readoutPlacement,
103
- refNameBinding,
104
- referenceLabel,
105
- referenceOrder,
261
+ parseRegion
262
+ } from "./chunks/chunk-73RSLGZT.js";
263
+ import {
264
+ cutWindowGFA,
265
+ haplotypeWanted,
106
266
  referencePathQuery,
107
267
  referenceSamplesOf,
108
- resolveColorScheme,
109
- resolveEncoding,
110
- resolveReferenceSample,
111
- rowGeneBoxes,
112
- rowSpan,
113
- runPaint,
114
- schemeCss,
115
- screenToLayout,
116
- segmentAt,
117
- serializeEl,
118
- stripGeneGaps,
119
- stripMarks,
120
- stripRowAt,
121
- svgCanvas,
122
- svgPath,
123
- trimOrigins,
124
- unitTicks,
125
- version,
126
- viewportOf,
127
- walkHighlight,
128
- walkKey,
129
- walkLift,
130
- walkMarksTree,
131
- walkPosition,
132
- walkRowReadout,
133
- walkRows,
134
- walkRowsExtent,
135
- walkRowsKey,
136
- walkRowsKeyTree,
137
- walkRowsTree,
138
- walkStripFrame,
139
- walkStripLabelsTree,
140
- wellKnownSample,
141
- zoomAbout
142
- } from "./chunks/chunk-EXSFRX2F.js";
268
+ resolveReferenceSample
269
+ } from "./chunks/chunk-LTUWJKHD.js";
270
+ import "./chunks/chunk-WSIFAZU7.js";
271
+ import {
272
+ WELL_KNOWN_SAMPLES,
273
+ assemblyWalk,
274
+ backboneAssembly,
275
+ featuresOnBackbone,
276
+ graphBackbone,
277
+ refNameBinding,
278
+ referenceLabel,
279
+ wellKnownSample
280
+ } from "./chunks/chunk-RAKOB6IV.js";
281
+ import {
282
+ panSNContig,
283
+ panSNHaplotype,
284
+ panSNSample
285
+ } from "./chunks/chunk-BPLIXMH7.js";
286
+ import "./chunks/chunk-FYQWPEEY.js";
143
287
 
144
288
  // src/index.ts
145
289
  import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
146
-
147
- // src/util/wheelZoom.ts
148
- var ZOOM_PER_NOTCH = 1.1;
149
- var NOTCH_PX = 100;
150
- var LINE_PX = 40;
151
- var PAGE_PX = 800;
152
- var PINCH_GAIN = 10;
153
- var MAX_NOTCHES_PER_EVENT = 2;
154
- function wheelZoomFactor(e) {
155
- const px = e.deltaY * (e.deltaMode === 1 ? LINE_PX : e.deltaMode === 2 ? PAGE_PX : 1);
156
- const notches = (e.ctrlKey ? PINCH_GAIN : 1) * px / NOTCH_PX;
157
- return ZOOM_PER_NOTCH ** -Math.max(-MAX_NOTCHES_PER_EVENT, Math.min(MAX_NOTCHES_PER_EVENT, notches));
158
- }
159
-
160
- // src/referenceStrip.ts
161
- import { abgrToCssRgba } from "@jbrowse/core/util/colorBits";
162
- var REFERENCE_STRIP_PX = 12;
163
- var REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8;
164
- var OVERHANG_PX = 8;
165
- var OVERHANG_CAP_PX = OVERHANG_PX + 3;
166
- var MIN_STAGGER_PX = 12;
167
- function referenceStripBlocks(graph, {
168
- colorScheme,
169
- referenceRamp,
170
- walks
171
- }) {
172
- const range = { ...computeColorSchemeRange(graph), referenceRamp };
173
- const out = [];
174
- graph.nodes.forEach((node, index) => {
175
- if (isBackbone(node)) {
176
- const colors = walks ? walks.map((w) => {
177
- const lane = w.colors.get(node.id);
178
- return lane === void 0 ? LIFT_BACKDROP_CSS : abgrToCssRgba(lane);
179
- }) : [abgrToCssRgba(getNodeColor(node, index, colorScheme, range))];
180
- out.push({
181
- node: node.id,
182
- bp0: node.stable.start,
183
- bp1: node.stable.start + node.length,
184
- colors,
185
- faded: colors.includes(LIFT_BACKDROP_CSS)
186
- });
187
- }
188
- });
189
- return out.sort((a, b) => a.bp0 - b.bp0);
190
- }
191
- function span(frame, bp0, bp1) {
192
- const a = bp0 * frame.scale + frame.translateX;
193
- const b = bp1 * frame.scale + frame.translateX;
194
- return a < b ? [a, b] : [b, a];
195
- }
196
- var HIT_SLOP_PX = 2;
197
- function stripOverhang(blocks, frame, width) {
198
- const a = -frame.translateX / frame.scale;
199
- const b = (width - frame.translateX) / frame.scale;
200
- const lo = Math.min(a, b);
201
- const hi = Math.max(a, b);
202
- let below = 0;
203
- let above = 0;
204
- for (const block of blocks) {
205
- below += Math.max(0, Math.min(block.bp1, lo) - block.bp0);
206
- above += Math.max(0, block.bp1 - Math.max(block.bp0, hi));
207
- }
208
- return frame.scale >= 0 ? { left: Math.round(below), right: Math.round(above) } : { left: Math.round(above), right: Math.round(below) };
209
- }
210
- function stripBlockAt(blocks, frame, sx, sy) {
211
- if (sy < 0 || sy > REFERENCE_STRIP_PX + HIT_SLOP_PX) {
212
- return void 0;
213
- }
214
- let hit;
215
- let best = HIT_SLOP_PX;
216
- for (const b of blocks) {
217
- const [x0, x1] = span(frame, b.bp0, b.bp1);
218
- const dist = Math.max(0, x0 - sx, sx - x1);
219
- if (dist <= best) {
220
- best = dist;
221
- hit = b.node;
222
- }
223
- }
224
- return hit;
225
- }
226
- function stripPixels(blocks, frame, width, dpr) {
227
- const placed = blocks.map((b) => {
228
- const [x0, x1] = span(frame, b.bp0, b.bp1);
229
- const px0 = Math.round(x0 * dpr);
230
- return {
231
- colors: b.colors,
232
- x0,
233
- px0,
234
- px1: Math.max(Math.round(x1 * dpr), px0 + 1)
235
- };
236
- }).filter((b) => b.px1 > 0 && b.px0 < width * dpr).sort((a, b) => a.x0 - b.x0);
237
- const out = [];
238
- let claimed = -Infinity;
239
- const tierEnds = [-Infinity, -Infinity];
240
- for (const { colors, px0, px1 } of placed) {
241
- const start = Math.max(px0, claimed);
242
- if (px1 > start) {
243
- const tier = tierEnds[0] < start ? 0 : 1;
244
- tierEnds[tier] = px1;
245
- out.push({ colors, x0: start / dpr, x1: px1 / dpr, tier });
246
- claimed = px1;
247
- }
248
- }
249
- const covered = out.length > 0 ? out.at(-1).x1 - out[0].x0 : 0;
250
- return covered >= out.length * MIN_STAGGER_PX ? out : out.map(({ tier: _, ...b }) => b);
251
- }
252
- function drawOverhang(ctx, edgeX, inward, ink) {
253
- const capX = inward > 0 ? edgeX : edgeX - OVERHANG_CAP_PX;
254
- ctx.clearRect(capX, 0, OVERHANG_CAP_PX, REFERENCE_STRIP_PX);
255
- const tipX = edgeX + inward;
256
- const baseX = tipX + inward * OVERHANG_PX;
257
- ctx.beginPath();
258
- ctx.moveTo(tipX, REFERENCE_STRIP_PX / 2);
259
- ctx.lineTo(baseX, 0);
260
- ctx.lineTo(baseX, REFERENCE_STRIP_PX);
261
- ctx.closePath();
262
- ctx.fillStyle = ink;
263
- ctx.fill();
264
- }
265
- function rowEdge(row, rows, dpr) {
266
- return Math.round(row * REFERENCE_STRIP_PX * dpr / rows) / dpr;
267
- }
268
- function drawReferenceStrip(ctx, blocks, frame, {
269
- width,
270
- lit,
271
- darkMode,
272
- dpr = 1
273
- }) {
274
- const ink = darkMode ? "#ffffff" : "#18181c";
275
- ctx.clearRect(0, 0, width, REFERENCE_STRIP_ZONE_PX);
276
- for (const b of stripPixels(blocks, frame, width, dpr)) {
277
- const { tier } = b;
278
- const staggered = tier !== void 0 && b.colors.length === 1;
279
- const rows = staggered ? 2 : b.colors.length;
280
- b.colors.forEach((color, i) => {
281
- const row = staggered ? tier : i;
282
- const y0 = rowEdge(row, rows, dpr);
283
- ctx.fillStyle = color;
284
- ctx.fillRect(b.x0, y0, b.x1 - b.x0, rowEdge(row + 1, rows, dpr) - y0);
285
- });
286
- }
287
- const overhang = stripOverhang(blocks, frame, width);
288
- if (overhang.left > 0) {
289
- drawOverhang(ctx, 0, 1, ink);
290
- }
291
- if (overhang.right > 0) {
292
- drawOverhang(ctx, width, -1, ink);
293
- }
294
- if (lit) {
295
- const [x0, x1] = span(frame, lit.start, lit.end);
296
- const w = Math.max(x1 - x0, 3);
297
- const left = (x0 + x1) / 2 - w / 2;
298
- ctx.strokeStyle = ink;
299
- ctx.lineWidth = 1.5;
300
- ctx.strokeRect(left, 0.75, w, REFERENCE_STRIP_PX - 1.5);
301
- if (lit.anchor) {
302
- ctx.beginPath();
303
- ctx.moveTo(left + w / 2, REFERENCE_STRIP_PX);
304
- ctx.lineTo(lit.anchor.x, lit.anchor.y);
305
- ctx.lineWidth = 1;
306
- ctx.stroke();
307
- }
308
- }
309
- }
310
-
311
- // src/tubeMap/draw.ts
312
- import { curvePaths, nodeOutlinePath } from "@gmod/tubemap-core";
313
-
314
- // src/tubeMap/mismatches.ts
315
- import { forward, getXCoordinateOfBaseWithinNode } from "@gmod/tubemap-core";
316
- function readMismatches(read, layout) {
317
- const marks = [];
318
- const entries = read.sequenceNew ?? [];
319
- entries.forEach((entry, i) => {
320
- const nodeIndex = layout.nodeMap.get(forward(entry.nodeName));
321
- const node = nodeIndex === void 0 ? void 0 : layout.nodes[nodeIndex];
322
- let pathIndex = i;
323
- while (pathIndex < read.path.length && read.path[pathIndex].node !== nodeIndex) {
324
- pathIndex += 1;
325
- }
326
- const y = read.path[pathIndex]?.y;
327
- if (node && y !== void 0) {
328
- const base = { readId: read.id, y, height: read.width, nodeY: node.y };
329
- const at = (pos) => getXCoordinateOfBaseWithinNode(node, pos);
330
- for (const mm of entry.mismatches) {
331
- const x0 = at(mm.pos);
332
- if (x0 !== null) {
333
- if (mm.type === "insertion") {
334
- const softClip = i === 0 && mm.pos === read.firstNodeOffset || i === entries.length - 1 && mm.pos === read.finalNodeCoverLength;
335
- marks.push({
336
- ...base,
337
- kind: "insertion",
338
- x: x0,
339
- seq: mm.seq,
340
- softClip
341
- });
342
- } else if (mm.type === "deletion" && mm.length !== void 0) {
343
- const x1 = at(mm.pos + mm.length);
344
- if (x1 !== null) {
345
- marks.push({ ...base, kind: "deletion", x0, x1 });
346
- }
347
- } else if (mm.type === "substitution" && mm.seq !== void 0) {
348
- const x1 = at(mm.pos + mm.seq.length);
349
- if (x1 !== null) {
350
- marks.push({ ...base, kind: "substitution", x0, x1, seq: mm.seq });
351
- }
352
- }
353
- }
354
- }
355
- }
356
- });
357
- return marks;
358
- }
359
- function tubeMapMismatches(layout) {
360
- return layout.reads.flatMap((read) => readMismatches(read, layout));
361
- }
362
-
363
- // src/tubeMap/draw.ts
364
- function parsePath(d) {
365
- const tokens = d.trim().split(/[\s,]+/);
366
- const commands = [];
367
- let i = 0;
368
- const num = () => Number(tokens[i++]);
369
- while (i < tokens.length) {
370
- const op = tokens[i++];
371
- if (op === "M" || op === "L") {
372
- commands.push({ op, x: num(), y: num() });
373
- } else if (op === "H") {
374
- commands.push({ op, x: num() });
375
- } else if (op === "V") {
376
- commands.push({ op, y: num() });
377
- } else if (op === "Q") {
378
- commands.push({ op, x1: num(), y1: num(), x: num(), y: num() });
379
- } else if (op === "C") {
380
- commands.push({
381
- op,
382
- x1: num(),
383
- y1: num(),
384
- x2: num(),
385
- y2: num(),
386
- x: num(),
387
- y: num()
388
- });
389
- } else if (op === "Z") {
390
- commands.push({ op });
391
- }
392
- }
393
- return commands;
394
- }
395
- function extentOf(commands) {
396
- let x0 = Infinity;
397
- let x1 = -Infinity;
398
- for (const c of commands) {
399
- if ("x" in c) {
400
- x0 = Math.min(x0, c.x);
401
- x1 = Math.max(x1, c.x);
402
- }
403
- if ("x1" in c) {
404
- x0 = Math.min(x0, c.x1);
405
- x1 = Math.max(x1, c.x1);
406
- }
407
- }
408
- return { x0, x1 };
409
- }
410
- function shapeOf(d, color, alpha, track) {
411
- const commands = parsePath(d);
412
- return { commands, color, track, alpha, ...extentOf(commands) };
413
- }
414
- function layerOf(layout, type) {
415
- const { shapes } = layout;
416
- const trackOf = (s) => type === "haplotype" ? s.id : void 0;
417
- const rects = [...shapes.rectangles, ...shapes.verticalRectangles].filter((r) => r.type === type).map((r) => ({
418
- color: r.color,
419
- track: trackOf(r),
420
- alpha: r.alpha ?? 1,
421
- x0: r.xStart,
422
- x1: r.xEnd + 1,
423
- y0: r.yStart,
424
- y1: r.yEnd + 1
425
- }));
426
- const curves = curvePaths(shapes.curves, type).map(
427
- (c) => shapeOf(c.path, c.color, c.alpha ?? 1, trackOf(c))
428
- );
429
- const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, c.color, 1, trackOf(c)));
430
- return { rects, shapes: [...curves, ...corners] };
431
- }
432
- function tubeMapPicture(layout) {
433
- const nodes = [];
434
- layout.nodes.forEach((node) => {
435
- if (node.order >= 0) {
436
- const commands = parsePath(nodeOutlinePath(node));
437
- nodes.push({ name: node.name, commands, ...extentOf(commands) });
438
- }
439
- });
440
- return {
441
- bounds: layout.bounds,
442
- layers: [layerOf(layout, "haplotype"), layerOf(layout, "read")],
443
- nodes,
444
- mismatches: tubeMapMismatches(layout).filter(
445
- (m) => m.kind !== "insertion" || !m.softClip
446
- )
447
- };
448
- }
449
- function trace(ctx, commands, x, y) {
450
- let cx = 0;
451
- let cy = 0;
452
- for (const c of commands) {
453
- switch (c.op) {
454
- case "M":
455
- cx = c.x;
456
- cy = c.y;
457
- ctx.moveTo(x(cx), y(cy));
458
- break;
459
- case "L":
460
- cx = c.x;
461
- cy = c.y;
462
- ctx.lineTo(x(cx), y(cy));
463
- break;
464
- case "H":
465
- cx = c.x;
466
- ctx.lineTo(x(cx), y(cy));
467
- break;
468
- case "V":
469
- cy = c.y;
470
- ctx.lineTo(x(cx), y(cy));
471
- break;
472
- case "Q":
473
- cx = c.x;
474
- cy = c.y;
475
- ctx.quadraticCurveTo(x(c.x1), y(c.y1), x(cx), y(cy));
476
- break;
477
- case "C":
478
- cx = c.x;
479
- cy = c.y;
480
- ctx.bezierCurveTo(x(c.x1), y(c.y1), x(c.x2), y(c.y2), x(cx), y(cy));
481
- break;
482
- case "Z":
483
- ctx.closePath();
484
- break;
485
- }
486
- }
487
- }
488
- function fillByColor(ctx, items, visible, colorOf, addTo) {
489
- const byColor = /* @__PURE__ */ new Map();
490
- for (const item of items) {
491
- if (visible(item)) {
492
- const key = `${colorOf(item)}|${item.alpha}`;
493
- const list = byColor.get(key);
494
- if (list) {
495
- list.push(item);
496
- } else {
497
- byColor.set(key, [item]);
498
- }
499
- }
500
- }
501
- for (const list of byColor.values()) {
502
- ctx.fillStyle = colorOf(list[0]);
503
- ctx.globalAlpha = list[0].alpha;
504
- ctx.beginPath();
505
- for (const item of list) {
506
- addTo(item);
507
- }
508
- ctx.fill();
509
- }
510
- ctx.globalAlpha = 1;
511
- }
512
- var FULL_OUTLINE_PX = 12;
513
- var MIN_OUTLINE_ALPHA = 0.15;
514
- function outlineAlpha(screenWidth) {
515
- return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX));
516
- }
517
- var NODE_TINT_ALPHA = 0.55;
518
- function drawTubeMap(ctx, picture, frame) {
519
- const { x, y, width } = frame;
520
- const visible = (item) => x(item.x1) >= 0 && x(item.x0) <= width;
521
- const colorOf = (item) => item.track !== void 0 && frame.tubeColors?.[item.track] || item.color;
522
- for (const layer of picture.layers) {
523
- fillByColor(ctx, layer.rects, visible, colorOf, (r) => {
524
- const left = x(r.x0);
525
- const top = y(r.y0);
526
- ctx.rect(left, top, x(r.x1) - left, y(r.y1) - top);
527
- });
528
- fillByColor(ctx, layer.shapes, visible, colorOf, (s) => {
529
- trace(ctx, s.commands, x, y);
530
- });
531
- }
532
- const stroke = frame.darkMode ? "#d0d0d0" : "#000000";
533
- const fill = frame.darkMode ? "rgba(40,40,40,0.4)" : "rgba(255,255,255,0.4)";
534
- ctx.lineWidth = 2 * Math.max(0.25, Math.min(1, frame.yScale));
535
- for (const node of picture.nodes) {
536
- if (visible(node)) {
537
- const lit = node.name === frame.highlightNode;
538
- ctx.beginPath();
539
- trace(ctx, node.commands, x, y);
540
- const tint = lit ? void 0 : frame.nodeColors?.get(node.name);
541
- ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : tint ?? fill;
542
- ctx.strokeStyle = lit ? "#ff0000" : stroke;
543
- ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
544
- ctx.fill();
545
- ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0));
546
- ctx.stroke();
547
- ctx.globalAlpha = 1;
548
- }
549
- }
550
- drawMismatches(ctx, picture.mismatches, frame);
551
- }
552
- var MISMATCH_FONT_PX = 12;
553
- var MIN_MISMATCH_FONT_PX = 6;
554
- function mismatchesLegible(yScale) {
555
- return MISMATCH_FONT_PX * yScale >= MIN_MISMATCH_FONT_PX;
556
- }
557
- function mismatchOnScreen(m, { x, width }) {
558
- const [x0, x1] = m.kind === "insertion" ? [m.x, m.x] : [m.x0, m.x1];
559
- return x(x1) >= 0 && x(x0) <= width;
560
- }
561
- function drawMismatches(ctx, marks, frame) {
562
- const { x, y, yScale, darkMode } = frame;
563
- if (!mismatchesLegible(yScale) || marks.length === 0) {
564
- return;
565
- }
566
- const fontPx = MISMATCH_FONT_PX * yScale;
567
- ctx.fillStyle = "grey";
568
- ctx.beginPath();
569
- for (const m of marks) {
570
- if (m.kind === "deletion" && mismatchOnScreen(m, frame)) {
571
- const left = x(m.x0);
572
- const top = y(m.y);
573
- ctx.rect(left, top, x(m.x1) - left, y(m.y + m.height) - top);
574
- }
575
- }
576
- ctx.fill();
577
- ctx.font = `${fontPx}px monospace`;
578
- ctx.textBaseline = "alphabetic";
579
- ctx.fillStyle = darkMode ? "#ffffff" : "#000000";
580
- for (const m of marks) {
581
- if (!mismatchOnScreen(m, frame)) {
582
- continue;
583
- }
584
- if (m.kind === "substitution") {
585
- ctx.fillText(m.seq, x(m.x0) + 1, y(m.y + m.height));
586
- } else if (m.kind === "insertion") {
587
- ctx.fillText("*", x(m.x) - 3, y(m.y + m.height));
588
- }
589
- }
590
- }
591
-
592
- // src/tubeMap/nodeColors.ts
593
- import { abgrToCssRgba as abgrToCssRgba2 } from "@jbrowse/core/util/colorBits";
594
- function tubeMapNodeColors(graph, colorScheme, referenceRamp) {
595
- if (colorScheme === "uniform" || colorScheme === "grey") {
596
- return void 0;
597
- }
598
- const range = { ...computeColorSchemeRange(graph), referenceRamp };
599
- return new Map(
600
- graph.nodes.map((node, index) => [
601
- node.id,
602
- abgrToCssRgba2(getNodeColor(node, index, colorScheme, range))
603
- ])
604
- );
605
- }
606
-
607
- // src/tubeMap/warp.ts
608
- var MAX_GAP_PX = 24;
609
- var isReal = (c) => c.bp1 > c.bp0;
610
- function referenceKnots(columns, bpToScreen) {
611
- const real = columns.filter(isReal);
612
- if (real.length === 0) {
613
- const first = columns[0];
614
- return first ? [{ tx: first.x0, sx: bpToScreen(first.bp0) }] : [];
615
- }
616
- const screen = real.map((c) => ({
617
- c,
618
- s0: bpToScreen(c.bp0),
619
- s1: bpToScreen(c.bp1),
620
- trimLeft: 0,
621
- trimRight: 0
622
- }));
623
- for (let i = 0; i + 1 < screen.length; i++) {
624
- const left = screen[i];
625
- const right = screen[i + 1];
626
- const demand = Math.min(right.c.x0 - left.c.x1, MAX_GAP_PX);
627
- const short = demand - (right.s0 - left.s1);
628
- if (short > 0) {
629
- const capLeft = (left.s1 - left.s0) / 2;
630
- const capRight = (right.s1 - right.s0) / 2;
631
- let fromLeft = Math.min(short / 2, capLeft);
632
- const fromRight = Math.min(short - fromLeft, capRight);
633
- fromLeft = Math.min(short - fromRight, capLeft);
634
- left.trimRight = fromLeft;
635
- right.trimLeft = fromRight;
636
- }
637
- }
638
- const knots = [];
639
- for (const { c, s0, s1, trimLeft, trimRight } of screen) {
640
- knots.push(
641
- { tx: c.x0, sx: s0 + trimLeft },
642
- { tx: c.x1, sx: s1 - trimRight }
643
- );
644
- }
645
- return knots;
646
- }
647
- function warpX(knots, tx) {
648
- const n = knots.length;
649
- if (n === 0) {
650
- return tx;
651
- }
652
- const first = knots[0];
653
- const last = knots[n - 1];
654
- if (tx <= first.tx) {
655
- return first.sx + (tx - first.tx);
656
- }
657
- if (tx >= last.tx) {
658
- return last.sx + (tx - last.tx);
659
- }
660
- let lo = 0;
661
- let hi = n - 1;
662
- while (hi - lo > 1) {
663
- const mid = lo + hi >> 1;
664
- if (knots[mid].tx <= tx) {
665
- lo = mid;
666
- } else {
667
- hi = mid;
668
- }
669
- }
670
- const a = knots[lo];
671
- const b = knots[hi];
672
- const span2 = b.tx - a.tx;
673
- return span2 > 0 ? a.sx + (tx - a.tx) / span2 * (b.sx - a.sx) : a.sx;
674
- }
675
-
676
- // src/tubeMap/frame.ts
677
- var MIN_TUBE_Y_SCALE = 0.05;
678
- var NODE_BOX_PAD = 9;
679
- function squeeze(drawing, usableHeight) {
680
- const { minY, maxY } = drawing.layout.bounds;
681
- const h = maxY - minY;
682
- return drawing.columns && h > 0 ? Math.max(MIN_TUBE_Y_SCALE, Math.min(1, usableHeight / h)) : 1;
683
- }
684
- function tubeMapFrame(drawing, view) {
685
- const { scaleX, translateX, translateY } = view;
686
- const yScale = view.scaleY * squeeze(drawing, view.usableHeight);
687
- const y = (ty) => (ty - drawing.yOffset) * yScale + translateY;
688
- const { columns } = drawing;
689
- if (columns) {
690
- const knots = referenceKnots(columns, (bp) => bp * scaleX + translateX);
691
- return { x: (tx) => warpX(knots, tx), y, yScale };
692
- }
693
- return { x: (tx) => tx * scaleX + translateX, y, yScale };
694
- }
695
- function tubeMapNodeAt(drawing, { x, y }, sx, sy) {
696
- let hit = null;
697
- drawing.layout.nodes.forEach((node) => {
698
- if (node.order >= 0 && x(node.x - NODE_BOX_PAD) <= sx && sx <= x(node.x + node.pixelWidth + NODE_BOX_PAD) && y(node.y - NODE_BOX_PAD) <= sy && sy <= y(node.y + node.contentHeight + NODE_BOX_PAD)) {
699
- hit = node.name;
700
- }
701
- });
702
- return hit;
703
- }
704
-
705
- // src/tubeMap/axis.ts
706
- function referenceBoxes(graph, layout) {
707
- const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
708
- const byRefName = /* @__PURE__ */ new Map();
709
- layout.nodes.forEach((node) => {
710
- const graphNode = nodeById.get(node.name);
711
- if (node.order >= 0 && graphNode && isBackbone(graphNode)) {
712
- const { refName, start } = graphNode.stable;
713
- const boxes = byRefName.get(refName) ?? byRefName.set(refName, []).get(refName);
714
- boxes.push({
715
- bp0: start,
716
- bp1: start + node.sequenceLength,
717
- x0: node.x,
718
- x1: node.x + node.pixelWidth
719
- });
720
- }
721
- });
722
- for (const boxes of byRefName.values()) {
723
- boxes.sort((a, b) => a.bp0 - b.bp0);
724
- }
725
- return byRefName;
726
- }
727
- function tubeX(boxes, bp, isEnd = false) {
728
- let lo = 0;
729
- let hi = boxes.length - 1;
730
- while (lo < hi) {
731
- const mid = lo + hi >> 1;
732
- const bp1 = boxes[mid].bp1;
733
- if (isEnd ? bp1 < bp : bp1 <= bp) {
734
- lo = mid + 1;
735
- } else {
736
- hi = mid;
737
- }
738
- }
739
- const box = boxes[lo];
740
- if (bp <= box.bp0) {
741
- return box.x0;
742
- }
743
- const span2 = box.bp1 - box.bp0;
744
- return span2 > 0 ? box.x0 + (Math.min(bp, box.bp1) - box.bp0) / span2 * (box.x1 - box.x0) : box.x0;
745
- }
746
- function tubeSpan(boxes, start, end) {
747
- const a = tubeX(boxes, start);
748
- const b = tubeX(boxes, end, true);
749
- return { x0: Math.min(a, b), x1: Math.max(a, b) };
750
- }
751
- function rulerBoxes(byRefName) {
752
- let best;
753
- for (const boxes of byRefName.values()) {
754
- if (!best || boxes.length > best.length) {
755
- best = boxes;
756
- }
757
- }
758
- return best;
759
- }
760
- var TICK_PX = 5;
761
- var LABEL_GAP_PX = 10;
762
- var TARGET_TICK_PX = 110;
763
- var MIN_BRACKET_PX = 3;
764
- function rulerInk(darkMode) {
765
- return darkMode ? "#b0b0b8" : "#55555c";
766
- }
767
- function niceStep(raw) {
768
- const pow = 10 ** Math.floor(Math.log10(raw));
769
- const m = raw / pow;
770
- return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow;
771
- }
772
- function placeLabels(candidates) {
773
- const placed = [];
774
- for (const a of candidates) {
775
- if (placed.every(
776
- (b) => Math.abs(a.at - b.at) >= (a.width + b.width) / 2 + LABEL_GAP_PX
777
- )) {
778
- placed.push(a);
779
- }
780
- }
781
- return placed.map(({ text, at }) => ({ text, at }));
782
- }
783
- function scaleMarks(boxes, x, width, measure) {
784
- const first = boxes[0];
785
- const last = boxes.at(-1);
786
- if (!first || !last) {
787
- return void 0;
788
- }
789
- const from = x(first.x0);
790
- const to = x(last.x1);
791
- const bpSpan = last.bp1 - first.bp0;
792
- if (to <= from || bpSpan <= 0) {
793
- return void 0;
794
- }
795
- const step = niceStep(bpSpan / (to - from) * TARGET_TICK_PX);
796
- const ticks = [];
797
- const positions = [];
798
- for (let bp = Math.ceil(first.bp0 / step) * step; bp <= last.bp1; bp += step) {
799
- const sx = Math.round(x(tubeX(boxes, bp))) + 0.5;
800
- if (sx >= 0 && sx <= width) {
801
- ticks.push(sx);
802
- const text = bp.toLocaleString("en-US");
803
- positions.push({ text, at: sx, width: measure(text) });
804
- }
805
- }
806
- return {
807
- spans: [{ x0: from, x1: to }],
808
- ticks,
809
- labels: placeLabels(positions)
810
- };
811
- }
812
- function boxMarks(boxes, x, width, measure) {
813
- const shown = boxes.map((box) => ({ box, s0: x(box.x0), s1: x(box.x1) })).filter(({ s0, s1 }) => s1 >= 0 && s0 <= width);
814
- const first = shown[0];
815
- const last = shown.at(-1);
816
- if (!first || !last) {
817
- return void 0;
818
- }
819
- const label = (text, at) => ({
820
- text,
821
- at,
822
- width: measure(text)
823
- });
824
- const position = (bp, at) => label(bp.toLocaleString("en-US"), at);
825
- const spans = [];
826
- const ticks = [];
827
- const lengths = [];
828
- const boundaries = [];
829
- for (const { box, s0, s1 } of shown) {
830
- spans.push({ x0: s0, x1: s1 });
831
- ticks.push(Math.round(s0) + 0.5);
832
- if (s1 - s0 >= MIN_BRACKET_PX) {
833
- ticks.push(Math.round(s1) + 0.5);
834
- }
835
- const length = label(formatBp(box.bp1 - box.bp0), (s0 + s1) / 2);
836
- if (length.width <= s1 - s0) {
837
- lengths.push(length);
838
- }
839
- if (box !== first.box) {
840
- boundaries.push(position(box.bp0, s0));
841
- }
842
- }
843
- const ends = [
844
- position(first.box.bp0, first.s0),
845
- position(last.box.bp1, last.s1)
846
- ];
847
- return {
848
- spans,
849
- ticks,
850
- labels: placeLabels([...ends, ...lengths, ...boundaries])
851
- };
852
- }
853
- function rulerMarks(boxes, x, width, measure, referenceAxis = false) {
854
- return (referenceAxis ? scaleMarks : boxMarks)(boxes, x, width, measure);
855
- }
856
- function drawTubeMapRuler(ctx, boxes, frame, top, referenceAxis = false) {
857
- const { x, width, darkMode } = frame;
858
- ctx.font = "10px sans-serif";
859
- const marks = rulerMarks(
860
- boxes,
861
- x,
862
- width,
863
- (text) => ctx.measureText(text).width,
864
- referenceAxis
865
- );
866
- if (!marks) {
867
- return;
868
- }
869
- const ink = rulerInk(darkMode);
870
- const y = top + 0.5;
871
- ctx.strokeStyle = ink;
872
- ctx.fillStyle = ink;
873
- ctx.lineWidth = 1;
874
- ctx.textAlign = "center";
875
- ctx.textBaseline = "top";
876
- ctx.beginPath();
877
- for (const { x0, x1 } of marks.spans) {
878
- ctx.moveTo(Math.max(0, x0), y);
879
- ctx.lineTo(Math.min(width, x1), y);
880
- }
881
- for (const sx of marks.ticks) {
882
- ctx.moveTo(sx, top);
883
- ctx.lineTo(sx, top + TICK_PX);
884
- }
885
- ctx.stroke();
886
- for (const { text, at } of marks.labels) {
887
- ctx.fillText(text, at, top + TICK_PX + 2);
888
- }
889
- }
890
-
891
- // src/tubeMap/genes.ts
892
- function tubeMapGenes(byRefName, genes) {
893
- const out = [];
894
- for (const gene of genes) {
895
- const boxes = byRefName.get(gene.refName);
896
- const first = boxes?.[0];
897
- const last = boxes?.at(-1);
898
- if (!boxes || !first || !last) {
899
- continue;
900
- }
901
- const start = Math.max(gene.start, first.bp0);
902
- const end = Math.min(gene.end, last.bp1);
903
- if (end <= start) {
904
- continue;
905
- }
906
- out.push({
907
- gene,
908
- ...tubeSpan(boxes, start, end),
909
- exons: gene.exons.filter((e) => e.end > start && e.start < end).map(
910
- (e) => tubeSpan(boxes, Math.max(e.start, start), Math.min(e.end, end))
911
- )
912
- });
913
- }
914
- return out;
915
- }
916
- var GENE_ROW_PX = 22;
917
- var EXON_PX = 8;
918
- var NAME_FONT = "11px sans-serif";
919
- var NAME_GAP_PX = 8;
920
- function drawTubeMapGenes(ctx, genes, frame, bottom, top = 0) {
921
- const { x, width, darkMode } = frame;
922
- const rows = Math.max(1, Math.floor((bottom - top) / GENE_ROW_PX));
923
- const rowEnds = [];
924
- const ink = darkMode ? "#e0e0e6" : "#1c1c22";
925
- ctx.font = NAME_FONT;
926
- ctx.textAlign = "center";
927
- ctx.textBaseline = "alphabetic";
928
- ctx.fillStyle = ink;
929
- ctx.strokeStyle = ink;
930
- ctx.lineWidth = 1;
931
- const placed = genes.map((g) => ({ g, s0: x(g.x0), s1: x(g.x1) })).filter(({ s0, s1 }) => s1 >= 0 && s0 <= width).sort((a, b) => a.s0 - b.s0);
932
- for (const { g, s0, s1 } of placed) {
933
- const arrow = g.gene.strand > 0 ? " \u2192" : g.gene.strand < 0 ? "\u2190 " : "";
934
- const label = g.gene.strand < 0 ? `${arrow}${g.gene.name}` : `${g.gene.name}${arrow}`;
935
- const mid = (Math.max(s0, 0) + Math.min(s1, width)) / 2;
936
- const half = ctx.measureText(label).width / 2;
937
- const left = Math.min(s0, mid - half);
938
- const right = Math.max(s1, mid + half);
939
- let row = rowEnds.findIndex((end) => end + NAME_GAP_PX <= left);
940
- if (row === -1) {
941
- row = Math.min(rowEnds.length, rows - 1);
942
- }
943
- rowEnds[row] = Math.max(rowEnds[row] ?? -Infinity, right);
944
- const glyphY = bottom - row * GENE_ROW_PX - EXON_PX / 2;
945
- ctx.beginPath();
946
- ctx.moveTo(s0, glyphY + 0.5);
947
- ctx.lineTo(s1, glyphY + 0.5);
948
- ctx.stroke();
949
- for (const e of g.exons) {
950
- const e0 = x(e.x0);
951
- ctx.fillRect(e0, glyphY - EXON_PX / 2, Math.max(1, x(e.x1) - e0), EXON_PX);
952
- }
953
- ctx.fillText(label, mid, glyphY - EXON_PX / 2 - 2);
954
- }
955
- }
956
-
957
- // src/tubeMap/connectors.ts
958
- function referenceNodes(graph, layout) {
959
- const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
960
- const out = [];
961
- layout.nodes.forEach((node) => {
962
- const graphNode = nodeById.get(node.name);
963
- if (node.order >= 0 && graphNode && isBackbone(graphNode)) {
964
- out.push({
965
- node: node.name,
966
- bp0: graphNode.stable.start,
967
- bp1: graphNode.stable.start + node.sequenceLength,
968
- x0: node.x,
969
- x1: node.x + node.pixelWidth
970
- });
971
- }
972
- });
973
- return out;
974
- }
975
- function tubeMapConnectors(nodes, bpToScreen, tubeToScreen, width) {
976
- const out = [];
977
- for (const n of nodes) {
978
- const bottom0 = tubeToScreen(n.x0);
979
- const bottom1 = tubeToScreen(n.x1);
980
- if (bottom1 >= 0 && bottom0 <= width) {
981
- out.push({
982
- node: n.node,
983
- top0: bpToScreen(n.bp0),
984
- top1: bpToScreen(n.bp1),
985
- bottom0,
986
- bottom1
987
- });
988
- }
989
- }
990
- return out;
991
- }
992
- var HIT_SLOP_PX2 = 3;
993
- function connectorAt(connectors, zoneBottom, sx, sy) {
994
- if (zoneBottom <= 0 || sy < 0 || sy > zoneBottom) {
995
- return void 0;
996
- }
997
- const t = sy / zoneBottom;
998
- let hit;
999
- let best = HIT_SLOP_PX2;
1000
- for (const c of connectors) {
1001
- const a = c.top0 + (c.bottom0 - c.top0) * t;
1002
- const b = c.top1 + (c.bottom1 - c.top1) * t;
1003
- const dist = Math.max(0, Math.min(a, b) - sx, sx - Math.max(a, b));
1004
- if (dist < best) {
1005
- best = dist;
1006
- hit = c.node;
1007
- }
1008
- }
1009
- return hit;
1010
- }
1011
- function band(ctx, c, bottom) {
1012
- ctx.moveTo(c.top0, 0);
1013
- ctx.lineTo(c.top1, 0);
1014
- ctx.lineTo(c.bottom1, bottom);
1015
- ctx.lineTo(c.bottom0, bottom);
1016
- ctx.closePath();
1017
- }
1018
- function sides(ctx, c, bottom) {
1019
- ctx.moveTo(c.top0, 0);
1020
- ctx.lineTo(c.bottom0, bottom);
1021
- ctx.moveTo(c.top1, 0);
1022
- ctx.lineTo(c.bottom1, bottom);
1023
- }
1024
- function drawTubeMapConnectors(ctx, connectors, bottom, {
1025
- highlightNode,
1026
- darkMode
1027
- }) {
1028
- if (bottom <= 0 || connectors.length === 0) {
1029
- return;
1030
- }
1031
- const ink = darkMode ? "255,255,255" : "0,0,0";
1032
- ctx.beginPath();
1033
- for (const c of connectors) {
1034
- band(ctx, c, bottom);
1035
- }
1036
- ctx.fillStyle = `rgba(${ink},0.07)`;
1037
- ctx.fill();
1038
- ctx.beginPath();
1039
- for (const c of connectors) {
1040
- sides(ctx, c, bottom);
1041
- }
1042
- ctx.strokeStyle = `rgba(${ink},0.35)`;
1043
- ctx.lineWidth = 0.5;
1044
- ctx.stroke();
1045
- const lit = connectors.find((c) => c.node === highlightNode);
1046
- if (lit) {
1047
- ctx.beginPath();
1048
- band(ctx, lit, bottom);
1049
- ctx.fillStyle = "rgba(255,192,203,0.5)";
1050
- ctx.fill();
1051
- ctx.beginPath();
1052
- sides(ctx, lit, bottom);
1053
- ctx.strokeStyle = "#ff0000";
1054
- ctx.lineWidth = 1;
1055
- ctx.stroke();
1056
- }
1057
- }
1058
-
1059
- // src/bubbles/bubbleLine.ts
1060
- var SATURATED_PATH_COUNT = 2147483647;
1061
-
1062
- // src/bubbles/bubblesFromGraph.ts
1063
- function bubblesFromGraph(graph) {
1064
- if (!graph.nodes.some(isBackbone)) {
1065
- return [];
1066
- }
1067
- const byId = new Map(graph.nodes.map((n) => [n.id, n]));
1068
- const order = referenceOrder(graph);
1069
- const indexOf = new Map(order.map((id, i) => [id, i]));
1070
- const reach = new Int32Array(order.length + 1);
1071
- const succ = /* @__PURE__ */ new Map();
1072
- const linked = /* @__PURE__ */ new Set();
1073
- for (const e of graph.edges) {
1074
- const a = indexOf.get(e.from);
1075
- const b = indexOf.get(e.to);
1076
- if (a === void 0 || b === void 0 || a === b) {
1077
- continue;
1078
- }
1079
- linked.add(`${e.from}>${e.to}`).add(`${e.to}>${e.from}`);
1080
- const [lo, hi] = a < b ? [a, b] : [b, a];
1081
- reach[lo + 1]++;
1082
- reach[hi]--;
1083
- const [from, to] = a < b ? [e.from, e.to] : [e.to, e.from];
1084
- (succ.get(from) ?? succ.set(from, /* @__PURE__ */ new Set()).get(from)).add(to);
1085
- }
1086
- for (let i = 1; i < reach.length; i++) {
1087
- reach[i] += reach[i - 1];
1088
- }
1089
- const backbone = graph.nodes.filter(isBackbone).sort((a, b) => a.stable.start - b.stable.start);
1090
- const ends = /* @__PURE__ */ new Set([backbone[0].id, backbone.at(-1).id]);
1091
- const boundaries = backbone.map((node) => [node, indexOf.get(node.id)]).filter(([node, i]) => ends.has(node.id) || reach[i] === 0).sort(([a, ia], [b, ib]) => ia - ib || a.stable.start - b.stable.start);
1092
- const walkIndex = graph.paths?.map((p) => {
1093
- const at = /* @__PURE__ */ new Map();
1094
- p.nodeIds.forEach((id, i) => {
1095
- if (!at.has(id)) {
1096
- at.set(id, i);
1097
- }
1098
- });
1099
- return at;
1100
- });
1101
- const bubbles = [];
1102
- for (let i = 0; i + 1 < boundaries.length; i++) {
1103
- const [start, i0] = boundaries[i];
1104
- const [end, i1] = boundaries[i + 1];
1105
- const interior = order.slice(i0 + 1, i1);
1106
- if (interior.length === 0) {
1107
- continue;
1108
- }
1109
- const best = /* @__PURE__ */ new Map([[start.id, { min: 0, max: 0, n: 1 }]]);
1110
- for (const id of [start.id, ...interior]) {
1111
- const cur = best.get(id);
1112
- if (!cur) {
1113
- continue;
1114
- }
1115
- for (const t of succ.get(id) ?? []) {
1116
- const ti = indexOf.get(t);
1117
- if (ti > i1) {
1118
- continue;
1119
- }
1120
- const add = t === end.id ? 0 : byId.get(t).length;
1121
- const prev = best.get(t);
1122
- best.set(t, {
1123
- min: Math.min(prev?.min ?? Infinity, cur.min + add),
1124
- max: Math.max(prev?.max ?? -Infinity, cur.max + add),
1125
- n: Math.min(SATURATED_PATH_COUNT, cur.n + (prev?.n ?? 0))
1126
- });
1127
- }
1128
- }
1129
- const walked = walkIndex && walkRoutes(graph, byId, walkIndex, start.id, end.id);
1130
- const crossed = walked?.n ? walked : void 0;
1131
- const routes2 = crossed ?? best.get(end.id) ?? { min: 0, max: 0, n: 0 };
1132
- const walksLeave = walked !== void 0 && walked.left > 0;
1133
- const refStart = start.stable.start + start.length;
1134
- const refEnd = end.stable.start;
1135
- const chain = [
1136
- start.id,
1137
- ...interior.filter((id) => isBackbone(byId.get(id))),
1138
- end.id
1139
- ];
1140
- const chainBroken = chain.some(
1141
- (id, k) => k > 0 && !linked.has(`${chain[k - 1]}>${id}`)
1142
- );
1143
- const partial = chainBroken || walksLeave;
1144
- const fallback = chainBroken && !crossed;
1145
- bubbles.push({
1146
- refName: start.stable.refName,
1147
- start: refStart,
1148
- end: refEnd,
1149
- segmentCount: interior.length + 2,
1150
- pathCount: routes2.n,
1151
- // a real inversion test needs each walk's direction relative to the
1152
- // reference; the first visit's strand is just whichever path anchored the
1153
- // node
1154
- inversion: false,
1155
- shortestAlleleLength: fallback ? refEnd - refStart : routes2.min,
1156
- longestAlleleLength: fallback ? refEnd - refStart : routes2.max,
1157
- segments: [start, ...interior.map((id) => byId.get(id)), end].map((n) => n.name).join(","),
1158
- shortestAllele: void 0,
1159
- longestAllele: void 0,
1160
- partial,
1161
- routes: crossed?.routes
1162
- });
1163
- }
1164
- return bubbles;
1165
- }
1166
- function walkRoutes(graph, byId, walkIndex, startId, endId) {
1167
- const seen = /* @__PURE__ */ new Map();
1168
- let min = Infinity;
1169
- let max = -Infinity;
1170
- let left = 0;
1171
- graph.paths.forEach((p, k) => {
1172
- const i0 = walkIndex[k].get(startId);
1173
- const i1 = walkIndex[k].get(endId);
1174
- if (i0 === void 0 || i1 === void 0) {
1175
- if (i0 !== void 0 || i1 !== void 0) {
1176
- left++;
1177
- }
1178
- return;
1179
- }
1180
- const steps = p.nodeIds.slice(Math.min(i0, i1) + 1, Math.max(i0, i1));
1181
- if (i1 < i0) {
1182
- steps.reverse();
1183
- }
1184
- let bp = 0;
1185
- for (const id of steps) {
1186
- bp += byId.get(id)?.length ?? 0;
1187
- }
1188
- const key = steps.join(",");
1189
- const route = seen.get(key) ?? { steps, bp, walks: [] };
1190
- route.walks.push(p.name);
1191
- seen.set(key, route);
1192
- min = Math.min(min, bp);
1193
- max = Math.max(max, bp);
1194
- });
1195
- return seen.size || left ? { min, max, n: seen.size, left, routes: [...seen.values()] } : void 0;
1196
- }
1197
-
1198
- // src/bubbles/classifyBubble.ts
1199
- var REPEAT_GROWTH = 10;
1200
- function coveringRepeat(b, repeats) {
1201
- const span2 = b.end - b.start;
1202
- const growth = b.longestAlleleLength - b.shortestAlleleLength;
1203
- return repeats.find(
1204
- (r) => growth <= REPEAT_GROWTH * (r.end - r.start) && (span2 === 0 ? r.start <= b.start && b.start <= r.end : Math.min(b.end, r.end) - Math.max(b.start, r.start) >= span2 / 2)
1205
- );
1206
- }
1207
- var SUPERBUBBLE_SEGMENTS = 40;
1208
- function formatBp2(bp) {
1209
- if (bp >= 1e4) {
1210
- return `${(bp / 1e3).toFixed(0)} kb`;
1211
- }
1212
- if (bp >= 1e3) {
1213
- return `${(bp / 1e3).toFixed(1)} kb`;
1214
- }
1215
- return `${bp} bp`;
1216
- }
1217
- function formatBpRange(shortest, longest) {
1218
- const low = formatBp2(shortest);
1219
- const high = formatBp2(longest);
1220
- const unit = high.slice(high.indexOf(" "));
1221
- return low.endsWith(unit) ? `${low.slice(0, -unit.length)}\u2013${high}` : `${low}\u2013${high}`;
1222
- }
1223
- function routes(count) {
1224
- if (count >= SATURATED_PATH_COUNT) {
1225
- return "\u22652.1B routes";
1226
- }
1227
- if (count >= 1e5) {
1228
- return `${(count / 1e3).toFixed(0)}k routes`;
1229
- }
1230
- return `${count} routes`;
1231
- }
1232
- function classifyBubble(b, repeats = []) {
1233
- const c = classifyShape(b, repeats);
1234
- return b.partial ? { ...c, label: `${c.label}, partial` } : c;
1235
- }
1236
- function classifyShape(b, repeats) {
1237
- const refSpan = b.end - b.start;
1238
- const {
1239
- shortestAlleleLength: shortest,
1240
- longestAlleleLength: longest,
1241
- pathCount,
1242
- segmentCount,
1243
- inversion
1244
- } = b;
1245
- const range = formatBpRange(shortest, longest);
1246
- if (segmentCount >= SUPERBUBBLE_SEGMENTS) {
1247
- return {
1248
- kind: "superbubble",
1249
- label: `${range} superbubble, ${segmentCount} segments, ${routes(pathCount)}${inversion ? ", inv" : ""}`
1250
- };
1251
- }
1252
- if (inversion) {
1253
- return { kind: "inversion", label: `${formatBp2(longest)} inv` };
1254
- }
1255
- const repeat = longest !== shortest ? coveringRepeat(b, repeats) : void 0;
1256
- if (repeat) {
1257
- const name = repeat.name ? ` (${repeat.name})` : "";
1258
- return {
1259
- kind: "repeat",
1260
- label: `${range} repeat array${name}, ${routes(pathCount)}`
1261
- };
1262
- }
1263
- const alleles = pathCount > 2 ? `, ${pathCount} alleles` : "";
1264
- if (shortest === longest && shortest === refSpan) {
1265
- return refSpan <= 1 ? { kind: "snp", label: "SNP" } : { kind: "substitution", label: `${formatBp2(refSpan)} sub` };
1266
- }
1267
- if (refSpan === 0 || shortest === refSpan) {
1268
- return {
1269
- kind: "insertion",
1270
- label: `\u2264${formatBp2(longest - refSpan)} ins${alleles}`
1271
- };
1272
- }
1273
- if (longest === refSpan) {
1274
- return {
1275
- kind: "deletion",
1276
- label: `${formatBp2(refSpan - shortest)} del${alleles}`
1277
- };
1278
- }
1279
- return {
1280
- kind: "complex",
1281
- label: `${formatBp2(refSpan)} ref \u2192 ${range}, ${pathCount} alleles`
1282
- };
1283
- }
1284
- var BUBBLE_KIND_COLORS = {
1285
- snp: "#5b6b7a",
1286
- substitution: "#5b6b7a",
1287
- insertion: "#2f8fd6",
1288
- deletion: "#c94040",
1289
- inversion: "#e07b00",
1290
- repeat: "#8e3fbf",
1291
- superbubble: "#8e3fbf",
1292
- complex: "#8e3fbf"
1293
- };
1294
- var BUBBLE_KIND_NAMES = {
1295
- snp: "SNP",
1296
- substitution: "substitution",
1297
- insertion: "insertion",
1298
- deletion: "deletion",
1299
- inversion: "inversion",
1300
- repeat: "repeat array",
1301
- superbubble: "superbubble",
1302
- complex: "site"
1303
- };
1304
- function bubbleSegmentIds(b) {
1305
- return b.segments.split(",").filter(Boolean);
1306
- }
1307
-
1308
- // src/bubbles/bubbleHalos.ts
1309
- var WHOLE_FRACTION = 0.9;
1310
- var NAMED_WALKS = 2;
1311
- var MIN_CHIPPED_BP = 1e3;
1312
- function bubbleHalos(graph, bubbles, positions, walkLabel = (name) => name, repeats = []) {
1313
- const byName = new Map(graph.nodes.map((n) => [n.name, n]));
1314
- const halos = [];
1315
- for (const bubble of bubbles) {
1316
- const parts = [];
1317
- let minX = Infinity;
1318
- let maxX = -Infinity;
1319
- let minY = Infinity;
1320
- const nodeIds = [];
1321
- const ends = [];
1322
- for (const name of bubbleSegmentIds(bubble)) {
1323
- const node = byName.get(name);
1324
- const line = node && positions[node.id];
1325
- if (!node || !line?.length) {
1326
- continue;
1327
- }
1328
- if (isBackbone(node) && (node.stable.start < bubble.start || node.stable.start >= bubble.end)) {
1329
- ends.push(line[Math.floor(line.length / 2)]);
1330
- continue;
1331
- }
1332
- nodeIds.push(node.id);
1333
- parts.push(svgPath(line.length === 1 ? [line[0], line[0]] : line));
1334
- for (const p of line) {
1335
- minX = Math.min(minX, p.x);
1336
- maxX = Math.max(maxX, p.x);
1337
- minY = Math.min(minY, p.y);
1338
- }
1339
- }
1340
- if (nodeIds.length === 0) {
1341
- continue;
1342
- }
1343
- const labelAt = { x: (minX + maxX) / 2, y: minY };
1344
- const anchor = ends.length ? {
1345
- x: ends.reduce((s, p) => s + p.x, 0) / ends.length,
1346
- y: ends.reduce((s, p) => s + p.y, 0) / ends.length
1347
- } : labelAt;
1348
- const sharing = /* @__PURE__ */ new Map();
1349
- for (const route of bubble.routes ?? []) {
1350
- for (const id of new Set(route.steps)) {
1351
- sharing.set(id, (sharing.get(id) ?? 0) + 1);
1352
- }
1353
- }
1354
- const routes2 = [];
1355
- for (const route of bubble.routes ?? []) {
1356
- if (bubble.longestAlleleLength < MIN_CHIPPED_BP) {
1357
- break;
1358
- }
1359
- if (route.steps.length === 0) {
1360
- continue;
1361
- }
1362
- const rarest = Math.min(...route.steps.map((id) => sharing.get(id)));
1363
- let at;
1364
- let far = -1;
1365
- for (const id of route.steps) {
1366
- if (sharing.get(id) !== rarest) {
1367
- continue;
1368
- }
1369
- for (const p of positions[id] ?? []) {
1370
- const d = Math.hypot(p.x - anchor.x, p.y - anchor.y);
1371
- if (d > far) {
1372
- far = d;
1373
- at = p;
1374
- }
1375
- }
1376
- }
1377
- if (at) {
1378
- routes2.push({ route, at, text: routeText(route, walkLabel) });
1379
- }
1380
- }
1381
- halos.push({
1382
- bubble,
1383
- ...classifyBubble(bubble, repeats),
1384
- path: parts.join(""),
1385
- labelAt,
1386
- members: nodeIds.length,
1387
- nodeIds,
1388
- whole: nodeIds.length >= WHOLE_FRACTION * graph.nodes.length,
1389
- routes: routes2
1390
- });
1391
- }
1392
- return halos;
1393
- }
1394
- function routeText(route, walkLabel) {
1395
- const names = [...new Set(route.walks.map(walkLabel))].sort();
1396
- const shown = names.slice(0, NAMED_WALKS).join(", ");
1397
- const more = names.length > NAMED_WALKS ? ` +${names.length - NAMED_WALKS}` : "";
1398
- return `${shown}${more} \xB7 ${formatBp2(route.bp)}`;
1399
- }
1400
-
1401
- // src/bubbles/popBubble.ts
1402
- function bubbleSubgraph(graph, segmentIds) {
1403
- const keep = new Set(segmentIds);
1404
- const nodes = graph.nodes.filter((n) => keep.has(n.name));
1405
- const ids = new Set(nodes.map((n) => n.id));
1406
- const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
1407
- const kept = /* @__PURE__ */ new Map();
1408
- const paths = graph.paths?.flatMap((p) => {
1409
- const first = p.nodeIds.findIndex((id) => ids.has(id));
1410
- if (first < 0) {
1411
- return [];
1412
- }
1413
- const nodeIds = p.nodeIds.filter((id) => ids.has(id));
1414
- kept.set(p.name, { first, nodeIds });
1415
- const before = p.nodeIds.slice(0, first).reduce((sum, id) => sum + (lengthOf.get(id) ?? 0), 0);
1416
- return [
1417
- {
1418
- ...p,
1419
- nodeIds,
1420
- ...p.start === void 0 ? {} : { start: p.start + before }
1421
- }
1422
- ];
1423
- });
1424
- return {
1425
- name: graph.name,
1426
- nodes,
1427
- edges: graph.edges.filter((e) => ids.has(e.from) && ids.has(e.to)),
1428
- ...paths?.length ? { paths } : {},
1429
- anchorPaths: trimOrigins(graph, kept),
1430
- pathVisits: graph.pathVisits,
1431
- anchoredBy: graph.anchoredBy,
1432
- referencePath: graph.referencePath
1433
- };
1434
- }
1435
290
  export {
1436
291
  BUBBLE_KIND_COLORS,
1437
292
  BUBBLE_KIND_NAMES,