@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,29 @@
1
+ // src/renderer/colorBits.ts
2
+ import {
3
+ abgrAlpha,
4
+ abgrBlue,
5
+ abgrGreen,
6
+ abgrRed,
7
+ packAbgr
8
+ } from "@jbrowse/core/util/colorBits";
9
+ function fadeAbgr(c, alpha) {
10
+ return packAbgr(
11
+ abgrRed(c),
12
+ abgrGreen(c),
13
+ abgrBlue(c),
14
+ Math.round(abgrAlpha(c) * alpha)
15
+ );
16
+ }
17
+ function brightenAbgr(c, factor) {
18
+ return packAbgr(
19
+ Math.min(255, Math.round(abgrRed(c) * factor)),
20
+ Math.min(255, Math.round(abgrGreen(c) * factor)),
21
+ Math.min(255, Math.round(abgrBlue(c) * factor)),
22
+ abgrAlpha(c)
23
+ );
24
+ }
25
+
26
+ export {
27
+ fadeAbgr,
28
+ brightenAbgr
29
+ };
@@ -0,0 +1,26 @@
1
+ // src/el.ts
2
+ function el(tag, attrs = {}, ...children) {
3
+ return {
4
+ tag,
5
+ attrs,
6
+ children: children.filter((c) => !!c || c === "")
7
+ };
8
+ }
9
+ function escapeText(s) {
10
+ return s.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
11
+ }
12
+ function escapeAttr(s) {
13
+ return escapeText(s).replaceAll('"', "&quot;");
14
+ }
15
+ function serializeEl(node) {
16
+ if (typeof node === "string") {
17
+ return escapeText(node);
18
+ }
19
+ const attrs = Object.entries(node.attrs).filter(([, v]) => v !== void 0).map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`).join("");
20
+ return `<${node.tag}${attrs}>${node.children.map(serializeEl).join("")}</${node.tag}>`;
21
+ }
22
+
23
+ export {
24
+ el,
25
+ serializeEl
26
+ };
@@ -0,0 +1,232 @@
1
+ import {
2
+ canonicalStrand,
3
+ readColor,
4
+ stepName,
5
+ tubeMapReads
6
+ } from "./chunk-TEDQJPMI.js";
7
+ import {
8
+ pathCssColor,
9
+ pathGreyCssColor
10
+ } from "./chunk-7CHJKABZ.js";
11
+ import {
12
+ pathOrigin
13
+ } from "./chunk-ST2IBUAN.js";
14
+ import {
15
+ isBackbone
16
+ } from "./chunk-FYQWPEEY.js";
17
+
18
+ // src/layout/tubeMapLayout.ts
19
+ import { layoutTubeMap } from "@gmod/tubemap-core";
20
+ function visitStrands(graph) {
21
+ const strands = /* @__PURE__ */ new Map();
22
+ for (const [segment, visits] of graph.pathVisits ?? []) {
23
+ for (const visit of visits) {
24
+ const key = `${visit.path} ${segment}`;
25
+ const list = strands.get(key);
26
+ if (list) {
27
+ list.push(visit.strand);
28
+ } else {
29
+ strands.set(key, [visit.strand]);
30
+ }
31
+ }
32
+ }
33
+ return strands;
34
+ }
35
+ function tubeMapTracks(graph) {
36
+ const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
37
+ const strands = visitStrands(graph);
38
+ const paths = (graph.paths ?? []).map((path, index) => ({ path, index }));
39
+ const reference = paths.findIndex(
40
+ (p) => pathOrigin(p.path.name).name === graph.referencePath
41
+ );
42
+ if (reference > 0) {
43
+ paths.unshift(...paths.splice(reference, 1));
44
+ }
45
+ return paths.flatMap(({ path, index }) => {
46
+ const visitName = pathOrigin(path.name).name;
47
+ const seen = /* @__PURE__ */ new Map();
48
+ const sequence = path.nodeIds.flatMap((id) => {
49
+ const node = nodeById.get(id);
50
+ if (!node) {
51
+ return [];
52
+ }
53
+ const k = seen.get(node.name) ?? 0;
54
+ seen.set(node.name, k + 1);
55
+ const strand = strands.get(`${visitName} ${node.name}`)?.[k] ?? canonicalStrand(node);
56
+ return [stepName(node, strand)];
57
+ });
58
+ return sequence.length > 0 ? [
59
+ {
60
+ id: index,
61
+ name: path.name,
62
+ sequence,
63
+ sourceTrackID: 0,
64
+ type: "haplotype"
65
+ }
66
+ ] : [];
67
+ });
68
+ }
69
+ function tubeMapNodes(graph) {
70
+ return graph.nodes.map((n) => ({
71
+ name: n.id,
72
+ seq: "",
73
+ sequenceLength: Math.max(1, n.length)
74
+ }));
75
+ }
76
+ function hasTubeMapPaths(graph) {
77
+ return (graph.paths?.length ?? 0) > 0;
78
+ }
79
+ var STACK_PX = 600;
80
+ var MIN_TUBE_PX = 3;
81
+ var MAX_TUBE_PX = 15;
82
+ function tubeWidth(trackCount) {
83
+ return Math.max(
84
+ MIN_TUBE_PX,
85
+ Math.min(MAX_TUBE_PX, Math.floor(STACK_PX / Math.max(1, trackCount)))
86
+ );
87
+ }
88
+ function runTubeMap(graph) {
89
+ const paths = graph.paths ?? [];
90
+ const tracks = tubeMapTracks(graph);
91
+ const reads = graph.reads ? tubeMapReads(graph, graph.reads, paths.length) : [];
92
+ const tubeColor = reads.length > 0 ? pathGreyCssColor : pathCssColor;
93
+ const pathColors = paths.map((_, i) => tubeColor(i, paths.length));
94
+ const layout = layoutTubeMap(tubeMapNodes(graph), tracks, reads, {
95
+ nodeWidthOption: "compressed",
96
+ trackWidth: tubeWidth(tracks.length),
97
+ trackColor: (track) => track.type === "read" ? readColor(track) : pathColors[track.id]
98
+ });
99
+ return layout ? { layout, pathColors } : void 0;
100
+ }
101
+ function drawnNodes(layout) {
102
+ const nodes = [];
103
+ layout.nodes.forEach((node) => {
104
+ if (node.order >= 0) {
105
+ nodes.push(node);
106
+ }
107
+ });
108
+ return nodes;
109
+ }
110
+ function centreline(node, yOffset, x0, x1) {
111
+ const y = node.y + node.contentHeight / 2 - yOffset;
112
+ return [
113
+ { x: x0, y },
114
+ { x: x1, y }
115
+ ];
116
+ }
117
+ function extentOf(layout, yOffset) {
118
+ return {
119
+ minY: layout.bounds.minY - yOffset,
120
+ maxY: layout.bounds.maxY - yOffset
121
+ };
122
+ }
123
+ function tubeMapLayout(graph) {
124
+ const run = hasTubeMapPaths(graph) ? runTubeMap(graph) : void 0;
125
+ if (!run) {
126
+ return void 0;
127
+ }
128
+ const { layout, pathColors } = run;
129
+ const yOffset = layout.bounds.minY;
130
+ const nodePositions = {};
131
+ for (const node of drawnNodes(layout)) {
132
+ nodePositions[node.name] = centreline(
133
+ node,
134
+ yOffset,
135
+ node.x,
136
+ node.x + node.pixelWidth
137
+ );
138
+ }
139
+ const { minY, maxY } = extentOf(layout, yOffset);
140
+ return {
141
+ nodePositions,
142
+ tubeMap: { layout, yOffset, pathColors },
143
+ extent: {
144
+ minX: layout.bounds.minX,
145
+ maxX: layout.bounds.maxX,
146
+ minY,
147
+ maxY
148
+ }
149
+ };
150
+ }
151
+ function tubeMapColumns(graph, layout) {
152
+ const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
153
+ const byOrder = /* @__PURE__ */ new Map();
154
+ for (const node of drawnNodes(layout)) {
155
+ const list = byOrder.get(node.order);
156
+ if (list) {
157
+ list.push(node);
158
+ } else {
159
+ byOrder.set(node.order, [node]);
160
+ }
161
+ }
162
+ const columns = [];
163
+ let end = -Infinity;
164
+ for (const order of [...byOrder.keys()].sort((a, b) => a - b)) {
165
+ const nodes = byOrder.get(order);
166
+ let bp0 = Infinity;
167
+ let bp1 = -Infinity;
168
+ for (const node of nodes) {
169
+ const graphNode = nodeById.get(node.name);
170
+ if (graphNode && isBackbone(graphNode)) {
171
+ bp0 = Math.min(bp0, graphNode.stable.start);
172
+ bp1 = Math.max(bp1, graphNode.stable.start + node.sequenceLength);
173
+ }
174
+ }
175
+ if (bp0 === Infinity) {
176
+ bp0 = bp1 = end;
177
+ }
178
+ if (bp0 < end) {
179
+ bp0 = end;
180
+ bp1 = Math.max(end, bp1);
181
+ }
182
+ end = Math.max(end, bp1);
183
+ columns.push({
184
+ order,
185
+ x0: Math.min(...nodes.map((n) => n.x)),
186
+ x1: Math.max(...nodes.map((n) => n.x + n.pixelWidth)),
187
+ bp0,
188
+ bp1
189
+ });
190
+ }
191
+ const first = columns.find((c) => c.bp0 !== -Infinity);
192
+ for (const column of columns) {
193
+ if (column.bp0 === -Infinity) {
194
+ column.bp0 = column.bp1 = first?.bp0 ?? 0;
195
+ }
196
+ }
197
+ return columns;
198
+ }
199
+ function pathsReachBackbone(graph) {
200
+ return graph.nodes.some(isBackbone) && hasTubeMapPaths(graph);
201
+ }
202
+ function tubeMapReferenceLayout(graph) {
203
+ const run = pathsReachBackbone(graph) ? runTubeMap(graph) : void 0;
204
+ if (!run) {
205
+ return void 0;
206
+ }
207
+ const { layout, pathColors } = run;
208
+ const columns = tubeMapColumns(graph, layout);
209
+ const byOrder = new Map(columns.map((c) => [c.order, c]));
210
+ const yOffset = layout.bounds.minY;
211
+ const nodePositions = {};
212
+ for (const node of drawnNodes(layout)) {
213
+ const column = byOrder.get(node.order);
214
+ nodePositions[node.name] = centreline(node, yOffset, column.bp0, column.bp1);
215
+ }
216
+ return {
217
+ nodePositions,
218
+ tubeMap: { layout, yOffset, columns, pathColors },
219
+ referenceAxis: true,
220
+ pixelRows: true,
221
+ extent: extentOf(layout, yOffset)
222
+ };
223
+ }
224
+
225
+ export {
226
+ visitStrands,
227
+ tubeMapTracks,
228
+ hasTubeMapPaths,
229
+ tubeWidth,
230
+ tubeMapLayout,
231
+ tubeMapReferenceLayout
232
+ };
@@ -0,0 +1,60 @@
1
+ // src/nodeWidths.ts
2
+ var NODE_WIDTHS = [
3
+ {
4
+ value: "depth",
5
+ label: "By depth",
6
+ description: "Thicker the more paths carry a node, on the square root of its depth against the mean. A graph with one depth everywhere draws uniform."
7
+ },
8
+ {
9
+ value: "uniform",
10
+ label: "Uniform",
11
+ description: "Every node the same thickness."
12
+ }
13
+ ];
14
+ var NODE_WIDTH_VALUES = NODE_WIDTHS.map((w) => w.value);
15
+ var MIN_FACTOR = 0.3;
16
+ var MAX_FACTOR = 3;
17
+ function meanDepth(graph) {
18
+ let weighted = 0;
19
+ let length = 0;
20
+ for (const node of graph.nodes) {
21
+ weighted += node.depth * node.length;
22
+ length += node.length;
23
+ }
24
+ return length > 0 ? weighted / length : 0;
25
+ }
26
+ function depthWidthFactor(node, mean) {
27
+ if (!(mean > 0) || !(node.depth > 0)) {
28
+ return 1;
29
+ }
30
+ return Math.min(
31
+ MAX_FACTOR,
32
+ Math.max(MIN_FACTOR, Math.sqrt(node.depth / mean))
33
+ );
34
+ }
35
+ function nodeWidthPx(node, contigThickness, nodeWidth, mean) {
36
+ return contigThickness * (nodeWidth === "depth" ? depthWidthFactor(node, mean) : 1);
37
+ }
38
+ function maxNodeWidthPx(contigThickness, nodeWidth) {
39
+ return contigThickness * (nodeWidth === "depth" ? MAX_FACTOR : 1);
40
+ }
41
+ function nodeInk(graph, nodeById, contigThickness, nodeWidth) {
42
+ const mean = graph ? meanDepth(graph) : 0;
43
+ return {
44
+ maxHalfWidthPx: maxNodeWidthPx(contigThickness, nodeWidth) / 2,
45
+ halfWidthPx: (id) => {
46
+ const node = nodeById?.get(id);
47
+ return node ? nodeWidthPx(node, contigThickness, nodeWidth, mean) / 2 : contigThickness / 2;
48
+ }
49
+ };
50
+ }
51
+
52
+ export {
53
+ NODE_WIDTHS,
54
+ NODE_WIDTH_VALUES,
55
+ meanDepth,
56
+ depthWidthFactor,
57
+ nodeWidthPx,
58
+ maxNodeWidthPx,
59
+ nodeInk
60
+ };
@@ -0,0 +1,70 @@
1
+ import {
2
+ isBackbone
3
+ } from "./chunk-FYQWPEEY.js";
4
+
5
+ // src/layout/orientToReference.ts
6
+ function orientToReference(graph, positions) {
7
+ const backbone = graph.nodes.filter(
8
+ (node) => isBackbone(node) && (positions[node.id]?.length ?? 0) > 0
9
+ );
10
+ if (backbone.length < 2) {
11
+ return positions;
12
+ }
13
+ const points = backbone.map((node) => {
14
+ const segments = positions[node.id];
15
+ const mid = segments[Math.floor(segments.length / 2)];
16
+ return {
17
+ x: mid.x,
18
+ y: mid.y,
19
+ r: node.stable.start + node.length / 2,
20
+ w: node.length
21
+ };
22
+ });
23
+ const weight = points.reduce((sum, p) => sum + p.w, 0);
24
+ const cx = points.reduce((sum, p) => sum + p.x * p.w, 0) / weight;
25
+ const cy = points.reduce((sum, p) => sum + p.y * p.w, 0) / weight;
26
+ const cr = points.reduce((sum, p) => sum + p.r * p.w, 0) / weight;
27
+ let sxr = 0;
28
+ let syr = 0;
29
+ for (const p of points) {
30
+ sxr += p.w * (p.x - cx) * (p.r - cr);
31
+ syr += p.w * (p.y - cy) * (p.r - cr);
32
+ }
33
+ const angle = -Math.atan2(syr, sxr);
34
+ const c = Math.cos(angle);
35
+ const s = Math.sin(angle);
36
+ const out = {};
37
+ for (const [id, segments] of Object.entries(positions)) {
38
+ out[id] = segments.map((p) => {
39
+ const x = p.x - cx;
40
+ const y = p.y - cy;
41
+ return { x: cx + c * x - s * y, y: cy + s * x + c * y };
42
+ });
43
+ }
44
+ let below = 0;
45
+ let above = 0;
46
+ for (const node of graph.nodes) {
47
+ if (isBackbone(node)) {
48
+ continue;
49
+ }
50
+ for (const p of out[node.id] ?? []) {
51
+ if (p.y > cy) {
52
+ below++;
53
+ } else {
54
+ above++;
55
+ }
56
+ }
57
+ }
58
+ if (above > below) {
59
+ for (const segments of Object.values(out)) {
60
+ for (const p of segments) {
61
+ p.y = 2 * cy - p.y;
62
+ }
63
+ }
64
+ }
65
+ return out;
66
+ }
67
+
68
+ export {
69
+ orientToReference
70
+ };
@@ -0,0 +1,202 @@
1
+ import {
2
+ formatBp
3
+ } from "./chunk-HCANBJDD.js";
4
+ import {
5
+ isBackbone
6
+ } from "./chunk-FYQWPEEY.js";
7
+
8
+ // src/tubeMap/axis.ts
9
+ function referenceBoxes(graph, layout) {
10
+ const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
11
+ const byRefName = /* @__PURE__ */ new Map();
12
+ layout.nodes.forEach((node) => {
13
+ const graphNode = nodeById.get(node.name);
14
+ if (node.order >= 0 && graphNode && isBackbone(graphNode)) {
15
+ const { refName, start } = graphNode.stable;
16
+ const boxes = byRefName.get(refName) ?? byRefName.set(refName, []).get(refName);
17
+ boxes.push({
18
+ bp0: start,
19
+ bp1: start + node.sequenceLength,
20
+ x0: node.x,
21
+ x1: node.x + node.pixelWidth
22
+ });
23
+ }
24
+ });
25
+ for (const boxes of byRefName.values()) {
26
+ boxes.sort((a, b) => a.bp0 - b.bp0);
27
+ }
28
+ return byRefName;
29
+ }
30
+ function tubeX(boxes, bp, isEnd = false) {
31
+ let lo = 0;
32
+ let hi = boxes.length - 1;
33
+ while (lo < hi) {
34
+ const mid = lo + hi >> 1;
35
+ const bp1 = boxes[mid].bp1;
36
+ if (isEnd ? bp1 < bp : bp1 <= bp) {
37
+ lo = mid + 1;
38
+ } else {
39
+ hi = mid;
40
+ }
41
+ }
42
+ const box = boxes[lo];
43
+ if (bp <= box.bp0) {
44
+ return box.x0;
45
+ }
46
+ const span = box.bp1 - box.bp0;
47
+ return span > 0 ? box.x0 + (Math.min(bp, box.bp1) - box.bp0) / span * (box.x1 - box.x0) : box.x0;
48
+ }
49
+ function tubeSpan(boxes, start, end) {
50
+ const a = tubeX(boxes, start);
51
+ const b = tubeX(boxes, end, true);
52
+ return { x0: Math.min(a, b), x1: Math.max(a, b) };
53
+ }
54
+ function rulerBoxes(byRefName) {
55
+ let best;
56
+ for (const boxes of byRefName.values()) {
57
+ if (!best || boxes.length > best.length) {
58
+ best = boxes;
59
+ }
60
+ }
61
+ return best;
62
+ }
63
+ var TICK_PX = 5;
64
+ var LABEL_GAP_PX = 10;
65
+ var TARGET_TICK_PX = 110;
66
+ var MIN_BRACKET_PX = 3;
67
+ function rulerInk(darkMode) {
68
+ return darkMode ? "#b0b0b8" : "#55555c";
69
+ }
70
+ function niceStep(raw) {
71
+ const pow = 10 ** Math.floor(Math.log10(raw));
72
+ const m = raw / pow;
73
+ return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow;
74
+ }
75
+ function placeLabels(candidates) {
76
+ const placed = [];
77
+ for (const a of candidates) {
78
+ if (placed.every(
79
+ (b) => Math.abs(a.at - b.at) >= (a.width + b.width) / 2 + LABEL_GAP_PX
80
+ )) {
81
+ placed.push(a);
82
+ }
83
+ }
84
+ return placed.map(({ text, at }) => ({ text, at }));
85
+ }
86
+ function scaleMarks(boxes, x, width, measure) {
87
+ const first = boxes[0];
88
+ const last = boxes.at(-1);
89
+ if (!first || !last) {
90
+ return void 0;
91
+ }
92
+ const from = x(first.x0);
93
+ const to = x(last.x1);
94
+ const bpSpan = last.bp1 - first.bp0;
95
+ if (to <= from || bpSpan <= 0) {
96
+ return void 0;
97
+ }
98
+ const step = niceStep(bpSpan / (to - from) * TARGET_TICK_PX);
99
+ const ticks = [];
100
+ const positions = [];
101
+ for (let bp = Math.ceil(first.bp0 / step) * step; bp <= last.bp1; bp += step) {
102
+ const sx = Math.round(x(tubeX(boxes, bp))) + 0.5;
103
+ if (sx >= 0 && sx <= width) {
104
+ ticks.push(sx);
105
+ const text = bp.toLocaleString("en-US");
106
+ positions.push({ text, at: sx, width: measure(text) });
107
+ }
108
+ }
109
+ return {
110
+ spans: [{ x0: from, x1: to }],
111
+ ticks,
112
+ labels: placeLabels(positions)
113
+ };
114
+ }
115
+ function boxMarks(boxes, x, width, measure) {
116
+ const shown = boxes.map((box) => ({ box, s0: x(box.x0), s1: x(box.x1) })).filter(({ s0, s1 }) => s1 >= 0 && s0 <= width);
117
+ const first = shown[0];
118
+ const last = shown.at(-1);
119
+ if (!first || !last) {
120
+ return void 0;
121
+ }
122
+ const label = (text, at) => ({
123
+ text,
124
+ at,
125
+ width: measure(text)
126
+ });
127
+ const position = (bp, at) => label(bp.toLocaleString("en-US"), at);
128
+ const spans = [];
129
+ const ticks = [];
130
+ const lengths = [];
131
+ const boundaries = [];
132
+ for (const { box, s0, s1 } of shown) {
133
+ spans.push({ x0: s0, x1: s1 });
134
+ ticks.push(Math.round(s0) + 0.5);
135
+ if (s1 - s0 >= MIN_BRACKET_PX) {
136
+ ticks.push(Math.round(s1) + 0.5);
137
+ }
138
+ const length = label(formatBp(box.bp1 - box.bp0), (s0 + s1) / 2);
139
+ if (length.width <= s1 - s0) {
140
+ lengths.push(length);
141
+ }
142
+ if (box !== first.box) {
143
+ boundaries.push(position(box.bp0, s0));
144
+ }
145
+ }
146
+ const ends = [
147
+ position(first.box.bp0, first.s0),
148
+ position(last.box.bp1, last.s1)
149
+ ];
150
+ return {
151
+ spans,
152
+ ticks,
153
+ labels: placeLabels([...ends, ...lengths, ...boundaries])
154
+ };
155
+ }
156
+ function rulerMarks(boxes, x, width, measure, referenceAxis = false) {
157
+ return (referenceAxis ? scaleMarks : boxMarks)(boxes, x, width, measure);
158
+ }
159
+ function drawTubeMapRuler(ctx, boxes, frame, top, referenceAxis = false) {
160
+ const { x, width, darkMode } = frame;
161
+ ctx.font = "10px sans-serif";
162
+ const marks = rulerMarks(
163
+ boxes,
164
+ x,
165
+ width,
166
+ (text) => ctx.measureText(text).width,
167
+ referenceAxis
168
+ );
169
+ if (!marks) {
170
+ return;
171
+ }
172
+ const ink = rulerInk(darkMode);
173
+ const y = top + 0.5;
174
+ ctx.strokeStyle = ink;
175
+ ctx.fillStyle = ink;
176
+ ctx.lineWidth = 1;
177
+ ctx.textAlign = "center";
178
+ ctx.textBaseline = "top";
179
+ ctx.beginPath();
180
+ for (const { x0, x1 } of marks.spans) {
181
+ ctx.moveTo(Math.max(0, x0), y);
182
+ ctx.lineTo(Math.min(width, x1), y);
183
+ }
184
+ for (const sx of marks.ticks) {
185
+ ctx.moveTo(sx, top);
186
+ ctx.lineTo(sx, top + TICK_PX);
187
+ }
188
+ ctx.stroke();
189
+ for (const { text, at } of marks.labels) {
190
+ ctx.fillText(text, at, top + TICK_PX + 2);
191
+ }
192
+ }
193
+
194
+ export {
195
+ referenceBoxes,
196
+ tubeX,
197
+ tubeSpan,
198
+ rulerBoxes,
199
+ rulerInk,
200
+ rulerMarks,
201
+ drawTubeMapRuler
202
+ };