@jbrowse/bandage-core 6.0.1 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +23 -7
  2. package/dist/alleleProjection/projectAlleles.js +10 -0
  3. package/dist/anchoredNodes.js +24 -0
  4. package/dist/bubbleSpreads.js +11 -0
  5. package/dist/bubbles/bubbleHalos.js +11 -0
  6. package/dist/bubbles/bubbleLine.js +14 -0
  7. package/dist/bubbles/bubblesFromGraph.js +10 -0
  8. package/dist/bubbles/classifyBubble.js +15 -0
  9. package/dist/bubbles/popBubble.js +9 -0
  10. package/dist/chunks/chunk-3C2LGJKY.js +108 -0
  11. package/dist/chunks/chunk-3PLYZLYV.js +513 -0
  12. package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
  13. package/dist/chunks/chunk-3WHKR5TY.js +726 -0
  14. package/dist/chunks/chunk-47DYCZOK.js +115 -0
  15. package/dist/chunks/chunk-4S47XRYA.js +149 -0
  16. package/dist/chunks/chunk-527AI7CP.js +122 -0
  17. package/dist/chunks/chunk-57ZIXHZA.js +90 -0
  18. package/dist/chunks/chunk-5QT6NPSO.js +40 -0
  19. package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
  20. package/dist/chunks/chunk-6Q6HFBJT.js +170 -0
  21. package/dist/chunks/chunk-6SBUFFTB.js +97 -0
  22. package/dist/chunks/chunk-6YRRNX2P.js +61 -0
  23. package/dist/chunks/chunk-72N5DJHZ.js +155 -0
  24. package/dist/chunks/chunk-73RSLGZT.js +73 -0
  25. package/dist/chunks/chunk-7CHJKABZ.js +82 -0
  26. package/dist/chunks/chunk-7XHHVYG7.js +13 -0
  27. package/dist/chunks/chunk-AB35TJST.js +67 -0
  28. package/dist/chunks/chunk-AE3UQZY3.js +43 -0
  29. package/dist/chunks/chunk-BJV2W4KZ.js +257 -0
  30. package/dist/chunks/chunk-BPLIXMH7.js +52 -0
  31. package/dist/chunks/chunk-BXEPA4KS.js +179 -0
  32. package/dist/chunks/chunk-C44YRP5F.js +85 -0
  33. package/dist/chunks/chunk-CDLPYR5O.js +135 -0
  34. package/dist/chunks/chunk-CPMHA4W5.js +6 -0
  35. package/dist/chunks/chunk-CR7EGAFE.js +241 -0
  36. package/dist/chunks/chunk-CZ54VG2H.js +431 -0
  37. package/dist/chunks/chunk-D3M7SBKL.js +210 -0
  38. package/dist/chunks/chunk-EJ5OZTCK.js +52 -0
  39. package/dist/chunks/chunk-ES2N6V56.js +83 -0
  40. package/dist/chunks/chunk-EZPBJPK6.js +54 -0
  41. package/dist/chunks/chunk-FYQWPEEY.js +65 -0
  42. package/dist/chunks/chunk-GQ2AOHU4.js +112 -0
  43. package/dist/chunks/chunk-HB6PDCOU.js +177 -0
  44. package/dist/chunks/chunk-HCANBJDD.js +326 -0
  45. package/dist/chunks/chunk-HIJVRRYE.js +174 -0
  46. package/dist/chunks/chunk-HNQPEMYE.js +297 -0
  47. package/dist/chunks/chunk-IGVV37VI.js +60 -0
  48. package/dist/chunks/chunk-IMW3AK27.js +102 -0
  49. package/dist/chunks/chunk-JKJWSY4V.js +32 -0
  50. package/dist/chunks/chunk-KCW7L3NV.js +6 -0
  51. package/dist/chunks/chunk-KQFOCDFY.js +73 -0
  52. package/dist/chunks/chunk-L3O7AWLI.js +52 -0
  53. package/dist/chunks/chunk-L5RYUPZI.js +53 -0
  54. package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
  55. package/dist/chunks/chunk-LTUWJKHD.js +112 -0
  56. package/dist/chunks/chunk-LVZZH2NI.js +181 -0
  57. package/dist/chunks/chunk-MFKORG3F.js +44 -0
  58. package/dist/chunks/chunk-MHGHYHTL.js +205 -0
  59. package/dist/chunks/chunk-MI35EUIE.js +76 -0
  60. package/dist/chunks/chunk-MNUSQCLG.js +29 -0
  61. package/dist/chunks/chunk-NNODIW4U.js +26 -0
  62. package/dist/chunks/chunk-P7B3GDAY.js +232 -0
  63. package/dist/chunks/chunk-PBENCR5L.js +60 -0
  64. package/dist/chunks/chunk-PUG7LXDF.js +70 -0
  65. package/dist/chunks/chunk-Q3KCTA4N.js +202 -0
  66. package/dist/chunks/chunk-QCYM44UL.js +476 -0
  67. package/dist/chunks/chunk-QYBZZI5H.js +0 -0
  68. package/dist/chunks/chunk-RAKOB6IV.js +169 -0
  69. package/dist/chunks/chunk-RVS2QCOX.js +135 -0
  70. package/dist/chunks/chunk-SDGM243E.js +190 -0
  71. package/dist/chunks/chunk-ST2IBUAN.js +119 -0
  72. package/dist/chunks/chunk-SV27AJCC.js +101 -0
  73. package/dist/chunks/chunk-SZNZ2T6D.js +198 -0
  74. package/dist/chunks/chunk-TDQLINVI.js +16 -0
  75. package/dist/chunks/chunk-TEDQJPMI.js +131 -0
  76. package/dist/chunks/chunk-TZFWUT2C.js +66 -0
  77. package/dist/chunks/chunk-UVBGRJQX.js +50 -0
  78. package/dist/chunks/chunk-VHW7FDHU.js +42 -0
  79. package/dist/chunks/chunk-VKB57ZYE.js +218 -0
  80. package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
  81. package/dist/chunks/chunk-WOO7QYKB.js +121 -0
  82. package/dist/chunks/chunk-WSIFAZU7.js +132 -0
  83. package/dist/chunks/chunk-X32PABB7.js +117 -0
  84. package/dist/chunks/chunk-YJQ7CT7F.js +23 -0
  85. package/dist/cli/figure.js +84 -17
  86. package/dist/colorSchemes.js +10 -0
  87. package/dist/deletionEdges.js +13 -0
  88. package/dist/el.js +8 -0
  89. package/dist/facetGrid.js +16 -0
  90. package/dist/figure.js +51 -0
  91. package/dist/gaf/parseGaf.js +12 -0
  92. package/dist/gbzCut.js +19 -0
  93. package/dist/gbzJoin.js +6 -0
  94. package/dist/gbzWindow.js +28 -0
  95. package/dist/genes/exonOutline.js +11 -0
  96. package/dist/genes/geneFiles.js +14 -0
  97. package/dist/genes/genePins.js +10 -0
  98. package/dist/gfa/gfaConverter.js +11 -0
  99. package/dist/gfa-core/gfaParser.js +8 -0
  100. package/dist/gfa-core/index.js +9 -0
  101. package/dist/graphLabels.js +24 -0
  102. package/dist/index.js +262 -1407
  103. package/dist/labelLayout.js +24 -0
  104. package/dist/layout/anchoredLayout.js +11 -0
  105. package/dist/layout/deletionRoutes.js +10 -0
  106. package/dist/layout/drawnScale.js +22 -0
  107. package/dist/layout/mergeRuns.js +15 -0
  108. package/dist/layout/orderedLayout.js +12 -0
  109. package/dist/layout/orientToReference.js +7 -0
  110. package/dist/layout/placeOffReference.js +9 -0
  111. package/dist/layout/referenceSeeds.js +11 -0
  112. package/dist/layout/rowSpacing.js +6 -0
  113. package/dist/layout/sampleRowLayout.js +11 -0
  114. package/dist/layout/trimToWindow.js +99 -0
  115. package/dist/layout/tubeMapLayout.js +28 -0
  116. package/dist/layout/walkRowDraw.js +65 -0
  117. package/dist/layout/walkRowLayout.js +13 -0
  118. package/dist/layout/walkRows.js +11 -0
  119. package/dist/layout/walkStrip.js +40 -0
  120. package/dist/layoutModes.js +35 -0
  121. package/dist/loadBandage.js +6 -0
  122. package/dist/nodeWidths.js +18 -0
  123. package/dist/overlayLabels.js +18 -0
  124. package/dist/pansn.js +18 -0
  125. package/dist/pathAnchoring.js +18 -0
  126. package/dist/pathColors.js +31 -0
  127. package/dist/pipeline.js +40 -0
  128. package/dist/reference.js +24 -0
  129. package/dist/referenceRampCss.js +29 -0
  130. package/dist/referenceSpan.js +11 -0
  131. package/dist/referenceStrip.js +35 -0
  132. package/dist/renderer/Canvas2DRenderer.js +9 -0
  133. package/dist/renderer/GeometryBuilder.js +42 -0
  134. package/dist/renderer/colorBits.js +8 -0
  135. package/dist/renderer/recordingCanvas.js +44 -0
  136. package/dist/renderer/svgCanvas.js +6 -0
  137. package/dist/renderer/types.js +0 -0
  138. package/dist/tubeMap/axis.js +25 -0
  139. package/dist/tubeMap/coarsen.js +361 -0
  140. package/dist/tubeMap/connectors.js +13 -0
  141. package/dist/tubeMap/deviations.js +85 -0
  142. package/dist/tubeMap/draw.js +15 -0
  143. package/dist/tubeMap/frame.js +9 -0
  144. package/dist/tubeMap/genes.js +20 -0
  145. package/dist/tubeMap/mismatches.js +6 -0
  146. package/dist/tubeMap/nodeColors.js +21 -0
  147. package/dist/tubeMap/reads.js +17 -0
  148. package/dist/tubeMap/warp.js +8 -0
  149. package/dist/types/version.d.ts +1 -1
  150. package/dist/types.js +0 -0
  151. package/dist/util/SpatialIndex.js +13 -0
  152. package/dist/util/edgeCurves.js +10 -0
  153. package/dist/util/geometry.js +39 -0
  154. package/dist/util/hitDetection.js +20 -0
  155. package/dist/util/wheelZoom.js +6 -0
  156. package/dist/version.js +6 -0
  157. package/dist/viewport.js +33 -0
  158. package/dist/walkEncoding.js +39 -0
  159. package/dist/walkHighlight.js +26 -0
  160. package/dist/walkKey.js +17 -0
  161. package/package.json +38 -5
  162. package/dist/chunks/chunk-EXSFRX2F.js +0 -7400
@@ -0,0 +1,83 @@
1
+ import {
2
+ isBackbone
3
+ } from "./chunk-FYQWPEEY.js";
4
+
5
+ // src/referenceSpan.ts
6
+ var MAX_BACKBONE_HOPS = 4;
7
+ function buildNeighbors(graph) {
8
+ const neighbors = /* @__PURE__ */ new Map();
9
+ function add(from, to) {
10
+ const list = neighbors.get(from);
11
+ if (list) {
12
+ list.push(to);
13
+ } else {
14
+ neighbors.set(from, [to]);
15
+ }
16
+ }
17
+ for (const edge of graph.edges) {
18
+ add(edge.from, edge.to);
19
+ add(edge.to, edge.from);
20
+ }
21
+ return neighbors;
22
+ }
23
+ function flankingSpan({
24
+ nodeId,
25
+ nodeById,
26
+ neighbors
27
+ }) {
28
+ const seen = /* @__PURE__ */ new Set([nodeId]);
29
+ let frontier = [nodeId];
30
+ let minEnd = Infinity;
31
+ let maxStart = -Infinity;
32
+ for (let hop = 0; hop < MAX_BACKBONE_HOPS && frontier.length > 0; hop++) {
33
+ const next = [];
34
+ for (const id of frontier) {
35
+ const adjacent = neighbors.get(id);
36
+ if (adjacent) {
37
+ for (const neighborId of adjacent) {
38
+ if (!seen.has(neighborId)) {
39
+ seen.add(neighborId);
40
+ const neighbor = nodeById.get(neighborId);
41
+ if (neighbor && isBackbone(neighbor)) {
42
+ minEnd = Math.min(minEnd, neighbor.stable.start + neighbor.length);
43
+ maxStart = Math.max(maxStart, neighbor.stable.start);
44
+ } else {
45
+ next.push(neighborId);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ frontier = next;
52
+ }
53
+ return minEnd === Infinity ? void 0 : {
54
+ start: Math.min(minEnd, maxStart),
55
+ end: Math.max(minEnd, maxStart)
56
+ };
57
+ }
58
+ function nodeReferenceSpan({
59
+ nodeId,
60
+ nodeById,
61
+ neighbors
62
+ }) {
63
+ const node = nodeById.get(nodeId);
64
+ return node === void 0 ? void 0 : isBackbone(node) ? { start: node.stable.start, end: node.stable.start + node.length } : flankingSpan({ nodeId, nodeById, neighbors });
65
+ }
66
+ function referenceMidpoints(graph) {
67
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
68
+ const neighbors = buildNeighbors(graph);
69
+ const midpoints = /* @__PURE__ */ new Map();
70
+ for (const node of graph.nodes) {
71
+ const span = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors });
72
+ if (span) {
73
+ midpoints.set(node.id, (span.start + span.end) / 2);
74
+ }
75
+ }
76
+ return midpoints;
77
+ }
78
+
79
+ export {
80
+ buildNeighbors,
81
+ nodeReferenceSpan,
82
+ referenceMidpoints
83
+ };
@@ -0,0 +1,54 @@
1
+ import {
2
+ walkRows
3
+ } from "./chunk-SZNZ2T6D.js";
4
+ import {
5
+ ROW_HEIGHT_PX
6
+ } from "./chunk-CPMHA4W5.js";
7
+ import {
8
+ backboneNodes,
9
+ backbonePositions,
10
+ overlapsWindow
11
+ } from "./chunk-FYQWPEEY.js";
12
+
13
+ // src/layout/walkRowLayout.ts
14
+ var READOUT_ROOM = 1.3;
15
+ function walkRowsExtent(walks) {
16
+ let longest = walks.reference.bp;
17
+ for (const row of walks.rows) {
18
+ longest = Math.max(longest, row.bp);
19
+ }
20
+ return {
21
+ maxX: walks.origin + longest * READOUT_ROOM,
22
+ maxY: walks.rows.length * ROW_HEIGHT_PX
23
+ };
24
+ }
25
+ function walkRowLayout(graph, region) {
26
+ const backbone = backboneNodes(graph);
27
+ const walks = walkRows(graph, region);
28
+ if (backbone.length === 0 || !walks) {
29
+ return void 0;
30
+ }
31
+ const overWindow = region ? backbone.filter((n) => overlapsWindow(n, region)) : backbone;
32
+ const nodePositions = backbonePositions(
33
+ overWindow.length > 0 ? overWindow : backbone
34
+ );
35
+ const rowLabels = [
36
+ { label: walks.reference.label, y: 0 },
37
+ ...walks.rows.map((row, i) => ({
38
+ label: row.label,
39
+ y: (i + 1) * ROW_HEIGHT_PX
40
+ }))
41
+ ];
42
+ return {
43
+ nodePositions,
44
+ rowLabels,
45
+ referenceAxis: true,
46
+ pixelRows: true,
47
+ extent: walkRowsExtent(walks)
48
+ };
49
+ }
50
+
51
+ export {
52
+ walkRowsExtent,
53
+ walkRowLayout
54
+ };
@@ -0,0 +1,65 @@
1
+ // src/anchoredNodes.ts
2
+ var REFERENCE_RANK = 0;
3
+ function isAnchored(node) {
4
+ return node.stable !== void 0;
5
+ }
6
+ function isBackbone(node) {
7
+ return node.stable?.rank === REFERENCE_RANK;
8
+ }
9
+ function isOffReference(node) {
10
+ return node.stable !== void 0 && node.stable.rank > REFERENCE_RANK;
11
+ }
12
+ function backboneNodes(graph) {
13
+ return graph.nodes.filter(isBackbone);
14
+ }
15
+ function overlapsWindow(node, window) {
16
+ return node.stable.start < window.end && node.stable.start + node.length > window.start;
17
+ }
18
+ function backbonePositions(backbone) {
19
+ const positions = {};
20
+ for (const node of backbone) {
21
+ positions[node.id] = [
22
+ { x: node.stable.start, y: 0 },
23
+ { x: node.stable.start + node.length, y: 0 }
24
+ ];
25
+ }
26
+ return positions;
27
+ }
28
+ function firstNodeAtOrAfter(nodes, bp) {
29
+ let lo = 0;
30
+ let hi = nodes.length;
31
+ while (lo < hi) {
32
+ const mid = lo + hi >> 1;
33
+ if (nodes[mid].stable.start < bp) {
34
+ lo = mid + 1;
35
+ } else {
36
+ hi = mid;
37
+ }
38
+ }
39
+ return lo;
40
+ }
41
+ function backboneSpan(backbone) {
42
+ let min = Infinity;
43
+ let max = -Infinity;
44
+ for (const node of backbone) {
45
+ min = Math.min(min, node.stable.start);
46
+ max = Math.max(max, node.stable.start + node.length);
47
+ }
48
+ return max - min;
49
+ }
50
+ function referenceSpan(backbone, region) {
51
+ return region && region.end > region.start ? region.end - region.start : backboneSpan(backbone);
52
+ }
53
+
54
+ export {
55
+ REFERENCE_RANK,
56
+ isAnchored,
57
+ isBackbone,
58
+ isOffReference,
59
+ backboneNodes,
60
+ overlapsWindow,
61
+ backbonePositions,
62
+ firstNodeAtOrAfter,
63
+ backboneSpan,
64
+ referenceSpan
65
+ };
@@ -0,0 +1,112 @@
1
+ import {
2
+ isBackbone
3
+ } from "./chunk-FYQWPEEY.js";
4
+
5
+ // src/tubeMap/connectors.ts
6
+ function referenceNodes(graph, layout) {
7
+ const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
8
+ const out = [];
9
+ layout.nodes.forEach((node) => {
10
+ const graphNode = nodeById.get(node.name);
11
+ if (node.order >= 0 && graphNode && isBackbone(graphNode)) {
12
+ out.push({
13
+ node: node.name,
14
+ bp0: graphNode.stable.start,
15
+ bp1: graphNode.stable.start + node.sequenceLength,
16
+ x0: node.x,
17
+ x1: node.x + node.pixelWidth
18
+ });
19
+ }
20
+ });
21
+ return out;
22
+ }
23
+ function tubeMapConnectors(nodes, bpToScreen, tubeToScreen, width) {
24
+ const out = [];
25
+ for (const n of nodes) {
26
+ const bottom0 = tubeToScreen(n.x0);
27
+ const bottom1 = tubeToScreen(n.x1);
28
+ if (bottom1 >= 0 && bottom0 <= width) {
29
+ out.push({
30
+ node: n.node,
31
+ top0: bpToScreen(n.bp0),
32
+ top1: bpToScreen(n.bp1),
33
+ bottom0,
34
+ bottom1
35
+ });
36
+ }
37
+ }
38
+ return out;
39
+ }
40
+ var HIT_SLOP_PX = 3;
41
+ function connectorAt(connectors, zoneBottom, sx, sy) {
42
+ if (zoneBottom <= 0 || sy < 0 || sy > zoneBottom) {
43
+ return void 0;
44
+ }
45
+ const t = sy / zoneBottom;
46
+ let hit;
47
+ let best = HIT_SLOP_PX;
48
+ for (const c of connectors) {
49
+ const a = c.top0 + (c.bottom0 - c.top0) * t;
50
+ const b = c.top1 + (c.bottom1 - c.top1) * t;
51
+ const dist = Math.max(0, Math.min(a, b) - sx, sx - Math.max(a, b));
52
+ if (dist < best) {
53
+ best = dist;
54
+ hit = c.node;
55
+ }
56
+ }
57
+ return hit;
58
+ }
59
+ function band(ctx, c, bottom) {
60
+ ctx.moveTo(c.top0, 0);
61
+ ctx.lineTo(c.top1, 0);
62
+ ctx.lineTo(c.bottom1, bottom);
63
+ ctx.lineTo(c.bottom0, bottom);
64
+ ctx.closePath();
65
+ }
66
+ function sides(ctx, c, bottom) {
67
+ ctx.moveTo(c.top0, 0);
68
+ ctx.lineTo(c.bottom0, bottom);
69
+ ctx.moveTo(c.top1, 0);
70
+ ctx.lineTo(c.bottom1, bottom);
71
+ }
72
+ function drawTubeMapConnectors(ctx, connectors, bottom, {
73
+ highlightNode,
74
+ darkMode
75
+ }) {
76
+ if (bottom <= 0 || connectors.length === 0) {
77
+ return;
78
+ }
79
+ const ink = darkMode ? "255,255,255" : "0,0,0";
80
+ ctx.beginPath();
81
+ for (const c of connectors) {
82
+ band(ctx, c, bottom);
83
+ }
84
+ ctx.fillStyle = `rgba(${ink},0.07)`;
85
+ ctx.fill();
86
+ ctx.beginPath();
87
+ for (const c of connectors) {
88
+ sides(ctx, c, bottom);
89
+ }
90
+ ctx.strokeStyle = `rgba(${ink},0.35)`;
91
+ ctx.lineWidth = 0.5;
92
+ ctx.stroke();
93
+ const lit = connectors.find((c) => c.node === highlightNode);
94
+ if (lit) {
95
+ ctx.beginPath();
96
+ band(ctx, lit, bottom);
97
+ ctx.fillStyle = "rgba(255,192,203,0.5)";
98
+ ctx.fill();
99
+ ctx.beginPath();
100
+ sides(ctx, lit, bottom);
101
+ ctx.strokeStyle = "#ff0000";
102
+ ctx.lineWidth = 1;
103
+ ctx.stroke();
104
+ }
105
+ }
106
+
107
+ export {
108
+ referenceNodes,
109
+ tubeMapConnectors,
110
+ connectorAt,
111
+ drawTubeMapConnectors
112
+ };
@@ -0,0 +1,177 @@
1
+ import {
2
+ nodesUnderBox
3
+ } from "./chunk-BJV2W4KZ.js";
4
+ import {
5
+ nodeLabelBudget,
6
+ placeSizeLabels,
7
+ rowLabelBox,
8
+ sizeLabelCandidates
9
+ } from "./chunk-HCANBJDD.js";
10
+ import {
11
+ LABEL_CHAR_PX,
12
+ LABEL_PAD,
13
+ LABEL_PX,
14
+ occupancy,
15
+ placeLabels
16
+ } from "./chunk-AB35TJST.js";
17
+
18
+ // src/labelLayout.ts
19
+ var HALO_FACTOR = 3.4;
20
+ var LEGEND_INSET_PX = 6;
21
+ var ROUTE_STACK = 8;
22
+ var GENE_PIN_DROP_PX = 18;
23
+ var GENE_STACK = 2;
24
+ var TOPMOST_BASELINE = LABEL_PX + LABEL_PAD;
25
+ function geneCoverageNote(pin) {
26
+ const length = pin.gene.end - pin.gene.start;
27
+ if (pin.covered >= 0.98) {
28
+ return void 0;
29
+ }
30
+ const [per, unit] = length >= 1e6 ? [1e6, "Mb"] : length >= 1e3 ? [1e3, "kb"] : [1, "bp"];
31
+ const amount = (bp) => +(bp / per).toFixed(per === 1 ? 0 : 1);
32
+ return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`;
33
+ }
34
+ function geneLabelCandidates(pins, screen, contigThickness, nodesUnder) {
35
+ const ink = contigThickness / 2;
36
+ return byExtent(pins, (pin) => pin.gene.end - pin.gene.start).map((pin) => {
37
+ const { x, y } = screen(pin.at);
38
+ const note = geneCoverageNote(pin);
39
+ return {
40
+ item: pin,
41
+ x,
42
+ y: y + contigThickness + GENE_PIN_DROP_PX,
43
+ text: note ? `${pin.gene.name} \xB7 ${note}` : pin.gene.name,
44
+ fallback: note ? `${pin.gene.name} \u2026` : void 0,
45
+ stack: GENE_STACK,
46
+ fits: (box) => [
47
+ ...nodesUnder({
48
+ x0: box.x0 - ink,
49
+ x1: box.x1 + ink,
50
+ y0: box.y0 - ink,
51
+ y1: box.y1 + ink
52
+ })
53
+ ].every((id) => pin.nodeIds.has(id))
54
+ };
55
+ });
56
+ }
57
+ function byExtent(items, extent) {
58
+ return [...items].sort((a, b) => extent(b) - extent(a));
59
+ }
60
+ function layoutLabels(m) {
61
+ const { paneWidth: width, canvasHeight: height, translateX, translateY } = m;
62
+ const { scaleX, scaleY } = m.axisScale;
63
+ const frame = { width, height };
64
+ const screen = (p) => ({
65
+ x: p.x * scaleX + translateX,
66
+ y: p.y * scaleY + translateY
67
+ });
68
+ const reserved = m.drawnRowLabels.map(
69
+ ({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
70
+ );
71
+ const stripPx = m.referenceStripZonePx ?? 0;
72
+ if (stripPx > 0) {
73
+ reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
74
+ }
75
+ if (m.legendSize.width > 0) {
76
+ reserved.push({
77
+ x0: width - LEGEND_INSET_PX - m.legendSize.width,
78
+ x1: width,
79
+ y0: 0,
80
+ y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
81
+ });
82
+ }
83
+ if (m.poppedFrom) {
84
+ reserved.push({
85
+ x0: 0,
86
+ x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,
87
+ y0: 0,
88
+ y1: 44
89
+ });
90
+ }
91
+ const take = occupancy(frame, reserved);
92
+ const onRows = m.drawnRowLabels.length > 0;
93
+ const rowsTop = onRows ? Math.min(...m.drawnRowLabels.map((r) => r.y)) : Infinity;
94
+ const halo = m.contigThickness * HALO_FACTOR;
95
+ const byBubble = byExtent(m.bubbleHalos, (h) => h.members);
96
+ const bubbles = placeLabels(
97
+ byBubble.map((h) => {
98
+ const { x, y } = screen({
99
+ x: h.labelAt.x,
100
+ y: Math.min(h.labelAt.y, rowsTop)
101
+ });
102
+ const baseline = y - halo / 2 - 6;
103
+ return {
104
+ item: h,
105
+ x,
106
+ y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,
107
+ text: h.label
108
+ };
109
+ }),
110
+ frame,
111
+ take
112
+ );
113
+ const genes = placeLabels(
114
+ geneLabelCandidates(
115
+ m.genePins,
116
+ screen,
117
+ m.contigThickness,
118
+ nodesUnderBox(
119
+ m.nodePositions ?? {},
120
+ { scaleX, scaleY, translateX, translateY },
121
+ m.positionsVersion
122
+ )
123
+ ),
124
+ frame,
125
+ take
126
+ );
127
+ const sizeCandidates = m.nodePositions && m.labelsNodeSizes ? sizeLabelCandidates({
128
+ nodePositions: m.nodePositions,
129
+ nodeLengths: m.nodeLengths,
130
+ deletions: m.showDeletionEdges ? m.deletions : [],
131
+ deletionRoutes: m.deletionRoutes,
132
+ stranded: m.stranded,
133
+ alleleDeletions: m.alleleDeletions,
134
+ axis: m.axisScale,
135
+ translateX,
136
+ translateY,
137
+ width,
138
+ height,
139
+ version: m.positionsVersion
140
+ }) : { deletions: [], nodes: [] };
141
+ const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
142
+ const routes = placeLabels(
143
+ byBubble.flatMap(
144
+ (h) => h.routes.map((route) => {
145
+ const { x, y } = screen(route.at);
146
+ return {
147
+ item: { halo: h, route },
148
+ x,
149
+ y: y + 4,
150
+ text: route.text,
151
+ stack: ROUTE_STACK
152
+ };
153
+ })
154
+ ),
155
+ frame,
156
+ take
157
+ );
158
+ const nodeLabels = placeSizeLabels(
159
+ sizeCandidates.nodes,
160
+ take,
161
+ nodeLabelBudget(width, height)
162
+ );
163
+ return {
164
+ bubbles,
165
+ genes,
166
+ routes,
167
+ sizes: [...deletionLabels, ...nodeLabels]
168
+ };
169
+ }
170
+
171
+ export {
172
+ HALO_FACTOR,
173
+ LEGEND_INSET_PX,
174
+ geneCoverageNote,
175
+ geneLabelCandidates,
176
+ layoutLabels
177
+ };