@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,326 @@
1
+ import {
2
+ occupancy
3
+ } from "./chunk-AB35TJST.js";
4
+ import {
5
+ polylineMidpoint
6
+ } from "./chunk-SDGM243E.js";
7
+ import {
8
+ deletionArcCurves
9
+ } from "./chunk-X32PABB7.js";
10
+ import {
11
+ curveBounds,
12
+ curveMidpoint
13
+ } from "./chunk-3PLYZLYV.js";
14
+
15
+ // src/graphLabels.ts
16
+ var MIN_DELETION_LABEL_PX = 26;
17
+ var PX_PER_NODE_LABEL = 4e4;
18
+ var MIN_NODE_LABELS = 8;
19
+ var LEADER_GAP_PX = 8;
20
+ var LABEL_CHAR_PX = 5.7;
21
+ var LABEL_PAD_PX = 6;
22
+ var LABEL_HEIGHT_PX = 13;
23
+ var LABEL_GAP_PX = 3;
24
+ var MAX_LABEL_OVERHANG = 2;
25
+ function labelHalfWidth(text) {
26
+ return (text.length * LABEL_CHAR_PX + LABEL_PAD_PX + LABEL_GAP_PX) / 2;
27
+ }
28
+ var LABEL_HALF_HEIGHT = (LABEL_HEIGHT_PX + LABEL_GAP_PX) / 2;
29
+ var MIN_LABEL_HALF_WIDTH = labelHalfWidth("1 bp");
30
+ function boxAt(halfW, x, y) {
31
+ return {
32
+ x0: x - halfW,
33
+ x1: x + halfW,
34
+ y0: y - LABEL_HALF_HEIGHT,
35
+ y1: y + LABEL_HALF_HEIGHT
36
+ };
37
+ }
38
+ function rowLabelBox(text, screenY) {
39
+ return {
40
+ x0: 6,
41
+ x1: 6 + text.length * 6.2 + 8 + LABEL_GAP_PX,
42
+ y0: screenY - 8 - LABEL_GAP_PX / 2,
43
+ y1: screenY + 8 + LABEL_GAP_PX / 2
44
+ };
45
+ }
46
+ function clamp(v, lo, hi) {
47
+ return hi < lo ? v : Math.min(Math.max(v, lo), hi);
48
+ }
49
+ function groupDigits(n) {
50
+ return String(n).replace(/\B(?=(\d{3})+$)/g, ",");
51
+ }
52
+ function formatBp(bp) {
53
+ if (bp >= 1e6) {
54
+ return `${+(bp / 1e6).toFixed(1)} Mb`;
55
+ }
56
+ return bp >= 1e3 ? `${+(bp / 1e3).toFixed(1)} kb` : `${bp} bp`;
57
+ }
58
+ function deletionText(bp) {
59
+ return `${formatBp(bp)} del`;
60
+ }
61
+ function arcOutward(curves, apex, scaleX, scaleY) {
62
+ const first = curves[0];
63
+ const last = curves[curves.length - 1];
64
+ const dx = (apex.x - (first.x0 + last.x1) / 2) * scaleX;
65
+ const dy = (apex.y - (first.y0 + last.y1) / 2) * scaleY;
66
+ const len = Math.hypot(dx, dy);
67
+ return len === 0 ? { x: 0, y: -1 } : { x: dx / len, y: dy / len };
68
+ }
69
+ function boxSupport(box, dir) {
70
+ return Math.abs(dir.x) * (box.x1 - box.x0) / 2 + Math.abs(dir.y) * (box.y1 - box.y0) / 2;
71
+ }
72
+ function boxRayCrossing(box, dir) {
73
+ const halfW = (box.x1 - box.x0) / 2;
74
+ const halfH = (box.y1 - box.y0) / 2;
75
+ return Math.min(
76
+ dir.x === 0 ? Infinity : halfW / Math.abs(dir.x),
77
+ dir.y === 0 ? Infinity : halfH / Math.abs(dir.y)
78
+ );
79
+ }
80
+ function drawnLength(segments, scaleX, scaleY) {
81
+ let total = 0;
82
+ for (let i = 1; i < segments.length; i++) {
83
+ const a = segments[i - 1];
84
+ const b = segments[i];
85
+ total += Math.hypot((b.x - a.x) * scaleX, (b.y - a.y) * scaleY);
86
+ }
87
+ return total;
88
+ }
89
+ function runBounds(nodeIds, nodePositions) {
90
+ let minX = Infinity;
91
+ let minY = Infinity;
92
+ let maxX = -Infinity;
93
+ let maxY = -Infinity;
94
+ for (const id of nodeIds) {
95
+ for (const p of nodePositions[id] ?? []) {
96
+ minX = Math.min(minX, p.x);
97
+ maxX = Math.max(maxX, p.x);
98
+ minY = Math.min(minY, p.y);
99
+ maxY = Math.max(maxY, p.y);
100
+ }
101
+ }
102
+ return minX === Infinity ? void 0 : { minX, minY, maxX, maxY };
103
+ }
104
+ var nodeOrderCache = /* @__PURE__ */ new WeakMap();
105
+ function labelOrder(nodePositions, nodeLengths) {
106
+ const cached = nodeOrderCache.get(nodePositions);
107
+ if (cached?.nodeLengths === nodeLengths) {
108
+ return cached.order;
109
+ }
110
+ const order = Object.entries(nodePositions).filter(([id, segments]) => segments.length > 0 && nodeLengths.has(id)).sort(([a], [b]) => nodeLengths.get(b) - nodeLengths.get(a));
111
+ nodeOrderCache.set(nodePositions, { nodeLengths, order });
112
+ return order;
113
+ }
114
+ var arcCache = /* @__PURE__ */ new WeakMap();
115
+ function arcPlacements(nodePositions, deletions, axis, version, routes, stranded) {
116
+ const { scaleX, scaleY } = axis;
117
+ const cached = arcCache.get(nodePositions);
118
+ if (cached?.deletions === deletions && cached.routes === routes && cached.stranded === stranded && cached.scaleX === scaleX && cached.scaleY === scaleY && cached.version === version) {
119
+ return cached.arcs;
120
+ }
121
+ const arcs = [];
122
+ for (const deletion of [...deletions].sort((a, b) => b.bp - a.bp)) {
123
+ const curves = deletionArcCurves(
124
+ nodePositions,
125
+ deletion,
126
+ axis,
127
+ routes?.[deletion.edgeIndex],
128
+ stranded
129
+ );
130
+ const apex = curves ? curveMidpoint(curves) : void 0;
131
+ if (curves && apex) {
132
+ const { minX, minY, maxX, maxY } = curveBounds(curves);
133
+ arcs.push({
134
+ deletion,
135
+ curves,
136
+ apex,
137
+ // in screen px, so the two axes' units cannot be added by accident
138
+ extent: Math.max((maxX - minX) * scaleX, (maxY - minY) * scaleY)
139
+ });
140
+ }
141
+ }
142
+ arcCache.set(nodePositions, {
143
+ deletions,
144
+ routes,
145
+ stranded,
146
+ scaleX,
147
+ scaleY,
148
+ version,
149
+ arcs
150
+ });
151
+ return arcs;
152
+ }
153
+ function sizeLabelCandidates({
154
+ nodePositions,
155
+ nodeLengths,
156
+ deletions,
157
+ deletionRoutes,
158
+ stranded = false,
159
+ alleleDeletions,
160
+ axis,
161
+ translateX,
162
+ translateY,
163
+ width,
164
+ height,
165
+ version = 0
166
+ }) {
167
+ const { scaleX, scaleY } = axis;
168
+ const deletionCandidates = [];
169
+ const nodeCandidates = [];
170
+ for (const { deletion, curves, apex, extent } of arcPlacements(
171
+ nodePositions,
172
+ deletions,
173
+ axis,
174
+ version,
175
+ deletionRoutes,
176
+ stranded
177
+ )) {
178
+ if (extent >= MIN_DELETION_LABEL_PX) {
179
+ const text = deletionText(deletion.bp);
180
+ const halfW = labelHalfWidth(text);
181
+ const box = boxAt(halfW, 0, 0);
182
+ const arcX = apex.x * scaleX + translateX;
183
+ const arcY = apex.y * scaleY + translateY;
184
+ const fits = extent >= halfW * 2 / MAX_LABEL_OVERHANG;
185
+ const bow = arcOutward(curves, apex, scaleX, scaleY);
186
+ const offset = boxSupport(box, bow) + LEADER_GAP_PX;
187
+ const x = fits ? arcX : clamp(arcX + bow.x * offset, halfW, width - halfW);
188
+ const y = fits ? arcY : clamp(
189
+ arcY + bow.y * offset,
190
+ LABEL_HALF_HEIGHT,
191
+ height - LABEL_HALF_HEIGHT
192
+ );
193
+ const away = Math.hypot(x - arcX, y - arcY);
194
+ let leader;
195
+ if (away > 0) {
196
+ const dir = { x: (x - arcX) / away, y: (y - arcY) / away };
197
+ const crossing = boxRayCrossing(box, dir);
198
+ leader = {
199
+ arcX,
200
+ arcY,
201
+ labelX: x - dir.x * crossing,
202
+ labelY: y - dir.y * crossing
203
+ };
204
+ }
205
+ deletionCandidates.push({
206
+ label: {
207
+ key: `del:${deletion.edgeIndex}`,
208
+ text,
209
+ x,
210
+ y,
211
+ kind: "deletion",
212
+ leader
213
+ },
214
+ box: boxAt(halfW, x, y)
215
+ });
216
+ }
217
+ }
218
+ const labelledByDeletion = /* @__PURE__ */ new Set();
219
+ const alleleRuns = [...alleleDeletions ?? []].sort((a, b) => b.bp - a.bp);
220
+ for (const run of alleleRuns) {
221
+ const drawn = runBounds(run.nodeIds, nodePositions);
222
+ if (!drawn) {
223
+ continue;
224
+ }
225
+ const shownLeft = Math.max(drawn.minX * scaleX + translateX, 0);
226
+ const shownRight = Math.min(drawn.maxX * scaleX + translateX, width);
227
+ const shownTop = Math.max(drawn.minY * scaleY + translateY, 0);
228
+ const shownBottom = Math.min(drawn.maxY * scaleY + translateY, height);
229
+ if (shownRight < shownLeft || shownBottom < shownTop) {
230
+ continue;
231
+ }
232
+ const extent = Math.max(shownRight - shownLeft, shownBottom - shownTop);
233
+ const text = deletionText(run.bp);
234
+ const halfW = labelHalfWidth(text);
235
+ if (extent >= halfW * 2 / MAX_LABEL_OVERHANG) {
236
+ for (const id of run.nodeIds) {
237
+ labelledByDeletion.add(id);
238
+ }
239
+ const x = clamp((shownLeft + shownRight) / 2, halfW, width - halfW);
240
+ const y = clamp(
241
+ (shownTop + shownBottom) / 2,
242
+ LABEL_HALF_HEIGHT,
243
+ height - LABEL_HALF_HEIGHT
244
+ );
245
+ deletionCandidates.push({
246
+ label: {
247
+ key: `alleledel:${run.nodeIds.join(",")}`,
248
+ text,
249
+ x,
250
+ y,
251
+ kind: "deletion"
252
+ },
253
+ box: boxAt(halfW, x, y)
254
+ });
255
+ }
256
+ }
257
+ for (const [id, segments] of labelOrder(nodePositions, nodeLengths)) {
258
+ if (labelledByDeletion.has(id)) {
259
+ continue;
260
+ }
261
+ const { x, y } = polylineMidpoint(segments);
262
+ const screenY = y * scaleY + translateY;
263
+ if (screenY + LABEL_HALF_HEIGHT <= 0 || screenY - LABEL_HALF_HEIGHT >= height) {
264
+ continue;
265
+ }
266
+ const drawn = drawnLength(segments, scaleX, scaleY);
267
+ if (drawn < MIN_LABEL_HALF_WIDTH * 2 / MAX_LABEL_OVERHANG) {
268
+ continue;
269
+ }
270
+ const text = formatBp(nodeLengths.get(id));
271
+ const halfW = labelHalfWidth(text);
272
+ if (drawn >= halfW * 2 / MAX_LABEL_OVERHANG) {
273
+ const screenX = x * scaleX + translateX;
274
+ nodeCandidates.push({
275
+ label: {
276
+ key: `node:${id}`,
277
+ text,
278
+ x: screenX,
279
+ y: screenY,
280
+ kind: "node"
281
+ },
282
+ box: boxAt(halfW, screenX, screenY)
283
+ });
284
+ }
285
+ }
286
+ return { deletions: deletionCandidates, nodes: nodeCandidates };
287
+ }
288
+ function nodeLabelBudget(width, height) {
289
+ return Math.max(
290
+ MIN_NODE_LABELS,
291
+ Math.floor(width * height / PX_PER_NODE_LABEL)
292
+ );
293
+ }
294
+ function placeSizeLabels(candidates, take, budget = Infinity) {
295
+ const labels = [];
296
+ for (const { label, box } of candidates) {
297
+ if (labels.length >= budget) {
298
+ break;
299
+ }
300
+ if (take(box)) {
301
+ labels.push(label);
302
+ }
303
+ }
304
+ return labels;
305
+ }
306
+ function graphLabels({
307
+ reserved,
308
+ ...args
309
+ }) {
310
+ const take = occupancy(args, reserved);
311
+ const { deletions, nodes } = sizeLabelCandidates(args);
312
+ return [
313
+ ...placeSizeLabels(deletions, take),
314
+ ...placeSizeLabels(nodes, take, nodeLabelBudget(args.width, args.height))
315
+ ];
316
+ }
317
+
318
+ export {
319
+ rowLabelBox,
320
+ groupDigits,
321
+ formatBp,
322
+ sizeLabelCandidates,
323
+ nodeLabelBudget,
324
+ placeSizeLabels,
325
+ graphLabels
326
+ };
@@ -0,0 +1,174 @@
1
+ import {
2
+ LIFT_BACKDROP_CSS,
3
+ computeColorSchemeRange,
4
+ getNodeColor
5
+ } from "./chunk-3WHKR5TY.js";
6
+ import {
7
+ isBackbone
8
+ } from "./chunk-FYQWPEEY.js";
9
+
10
+ // src/referenceStrip.ts
11
+ import { abgrToCssRgba } from "@jbrowse/core/util/colorBits";
12
+ var REFERENCE_STRIP_PX = 12;
13
+ var REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8;
14
+ var OVERHANG_PX = 8;
15
+ var OVERHANG_CAP_PX = OVERHANG_PX + 3;
16
+ var MIN_STAGGER_PX = 12;
17
+ function referenceStripBlocks(graph, {
18
+ colorScheme,
19
+ referenceRamp,
20
+ walks
21
+ }) {
22
+ const range = { ...computeColorSchemeRange(graph), referenceRamp };
23
+ const out = [];
24
+ graph.nodes.forEach((node, index) => {
25
+ if (isBackbone(node)) {
26
+ const colors = walks ? walks.map((w) => {
27
+ const lane = w.colors.get(node.id);
28
+ return lane === void 0 ? LIFT_BACKDROP_CSS : abgrToCssRgba(lane);
29
+ }) : [abgrToCssRgba(getNodeColor(node, index, colorScheme, range))];
30
+ out.push({
31
+ node: node.id,
32
+ bp0: node.stable.start,
33
+ bp1: node.stable.start + node.length,
34
+ colors,
35
+ faded: colors.includes(LIFT_BACKDROP_CSS)
36
+ });
37
+ }
38
+ });
39
+ return out.sort((a, b) => a.bp0 - b.bp0);
40
+ }
41
+ function span(frame, bp0, bp1) {
42
+ const a = bp0 * frame.scale + frame.translateX;
43
+ const b = bp1 * frame.scale + frame.translateX;
44
+ return a < b ? [a, b] : [b, a];
45
+ }
46
+ var HIT_SLOP_PX = 2;
47
+ function stripOverhang(blocks, frame, width) {
48
+ const a = -frame.translateX / frame.scale;
49
+ const b = (width - frame.translateX) / frame.scale;
50
+ const lo = Math.min(a, b);
51
+ const hi = Math.max(a, b);
52
+ let below = 0;
53
+ let above = 0;
54
+ for (const block of blocks) {
55
+ below += Math.max(0, Math.min(block.bp1, lo) - block.bp0);
56
+ above += Math.max(0, block.bp1 - Math.max(block.bp0, hi));
57
+ }
58
+ return frame.scale >= 0 ? { left: Math.round(below), right: Math.round(above) } : { left: Math.round(above), right: Math.round(below) };
59
+ }
60
+ function stripBlockAt(blocks, frame, sx, sy) {
61
+ if (sy < 0 || sy > REFERENCE_STRIP_PX + HIT_SLOP_PX) {
62
+ return void 0;
63
+ }
64
+ let hit;
65
+ let best = HIT_SLOP_PX;
66
+ for (const b of blocks) {
67
+ const [x0, x1] = span(frame, b.bp0, b.bp1);
68
+ const dist = Math.max(0, x0 - sx, sx - x1);
69
+ if (dist <= best) {
70
+ best = dist;
71
+ hit = b.node;
72
+ }
73
+ }
74
+ return hit;
75
+ }
76
+ function nodeAnchor(segments, toScreen) {
77
+ const p = segments?.[Math.floor(segments.length / 2)];
78
+ return p ? toScreen(p) : void 0;
79
+ }
80
+ function stripPixels(blocks, frame, width, dpr) {
81
+ const placed = blocks.map((b) => {
82
+ const [x0, x1] = span(frame, b.bp0, b.bp1);
83
+ const px0 = Math.round(x0 * dpr);
84
+ return {
85
+ colors: b.colors,
86
+ x0,
87
+ px0,
88
+ px1: Math.max(Math.round(x1 * dpr), px0 + 1)
89
+ };
90
+ }).filter((b) => b.px1 > 0 && b.px0 < width * dpr).sort((a, b) => a.x0 - b.x0);
91
+ const out = [];
92
+ let claimed = -Infinity;
93
+ const tierEnds = [-Infinity, -Infinity];
94
+ for (const { colors, px0, px1 } of placed) {
95
+ const start = Math.max(px0, claimed);
96
+ if (px1 > start) {
97
+ const tier = tierEnds[0] < start ? 0 : 1;
98
+ tierEnds[tier] = px1;
99
+ out.push({ colors, x0: start / dpr, x1: px1 / dpr, tier });
100
+ claimed = px1;
101
+ }
102
+ }
103
+ const covered = out.length > 0 ? out.at(-1).x1 - out[0].x0 : 0;
104
+ return covered >= out.length * MIN_STAGGER_PX ? out : out.map(({ tier: _, ...b }) => b);
105
+ }
106
+ function drawOverhang(ctx, edgeX, inward, ink) {
107
+ const capX = inward > 0 ? edgeX : edgeX - OVERHANG_CAP_PX;
108
+ ctx.clearRect(capX, 0, OVERHANG_CAP_PX, REFERENCE_STRIP_PX);
109
+ const tipX = edgeX + inward;
110
+ const baseX = tipX + inward * OVERHANG_PX;
111
+ ctx.beginPath();
112
+ ctx.moveTo(tipX, REFERENCE_STRIP_PX / 2);
113
+ ctx.lineTo(baseX, 0);
114
+ ctx.lineTo(baseX, REFERENCE_STRIP_PX);
115
+ ctx.closePath();
116
+ ctx.fillStyle = ink;
117
+ ctx.fill();
118
+ }
119
+ function rowEdge(row, rows, dpr) {
120
+ return Math.round(row * REFERENCE_STRIP_PX * dpr / rows) / dpr;
121
+ }
122
+ function drawReferenceStrip(ctx, blocks, frame, {
123
+ width,
124
+ lit,
125
+ darkMode,
126
+ dpr = 1
127
+ }) {
128
+ const ink = darkMode ? "#ffffff" : "#18181c";
129
+ ctx.clearRect(0, 0, width, REFERENCE_STRIP_ZONE_PX);
130
+ for (const b of stripPixels(blocks, frame, width, dpr)) {
131
+ const { tier } = b;
132
+ const staggered = tier !== void 0 && b.colors.length === 1;
133
+ const rows = staggered ? 2 : b.colors.length;
134
+ b.colors.forEach((color, i) => {
135
+ const row = staggered ? tier : i;
136
+ const y0 = rowEdge(row, rows, dpr);
137
+ ctx.fillStyle = color;
138
+ ctx.fillRect(b.x0, y0, b.x1 - b.x0, rowEdge(row + 1, rows, dpr) - y0);
139
+ });
140
+ }
141
+ const overhang = stripOverhang(blocks, frame, width);
142
+ if (overhang.left > 0) {
143
+ drawOverhang(ctx, 0, 1, ink);
144
+ }
145
+ if (overhang.right > 0) {
146
+ drawOverhang(ctx, width, -1, ink);
147
+ }
148
+ if (lit) {
149
+ const [x0, x1] = span(frame, lit.start, lit.end);
150
+ const w = Math.max(x1 - x0, 3);
151
+ const left = (x0 + x1) / 2 - w / 2;
152
+ ctx.strokeStyle = ink;
153
+ ctx.lineWidth = 1.5;
154
+ ctx.strokeRect(left, 0.75, w, REFERENCE_STRIP_PX - 1.5);
155
+ if (lit.anchor) {
156
+ ctx.beginPath();
157
+ ctx.moveTo(left + w / 2, REFERENCE_STRIP_PX);
158
+ ctx.lineTo(lit.anchor.x, lit.anchor.y);
159
+ ctx.lineWidth = 1;
160
+ ctx.stroke();
161
+ }
162
+ }
163
+ }
164
+
165
+ export {
166
+ REFERENCE_STRIP_PX,
167
+ REFERENCE_STRIP_ZONE_PX,
168
+ referenceStripBlocks,
169
+ stripOverhang,
170
+ stripBlockAt,
171
+ nodeAnchor,
172
+ stripPixels,
173
+ drawReferenceStrip
174
+ };