@jbrowse/bandage-core 6.0.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +23 -7
  2. package/dist/alleleProjection/projectAlleles.js +10 -0
  3. package/dist/anchoredNodes.js +24 -0
  4. package/dist/bubbleSpreads.js +11 -0
  5. package/dist/bubbles/bubbleHalos.js +11 -0
  6. package/dist/bubbles/bubbleLine.js +14 -0
  7. package/dist/bubbles/bubblesFromGraph.js +10 -0
  8. package/dist/bubbles/classifyBubble.js +15 -0
  9. package/dist/bubbles/popBubble.js +9 -0
  10. package/dist/chunks/chunk-2CVHXFK7.js +530 -0
  11. package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
  12. package/dist/chunks/chunk-47DYCZOK.js +115 -0
  13. package/dist/chunks/chunk-4EMPBO7S.js +117 -0
  14. package/dist/chunks/chunk-4S47XRYA.js +149 -0
  15. package/dist/chunks/chunk-5XQDMQXU.js +431 -0
  16. package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
  17. package/dist/chunks/chunk-6SBUFFTB.js +97 -0
  18. package/dist/chunks/chunk-6SXMCK6I.js +476 -0
  19. package/dist/chunks/chunk-72N5DJHZ.js +155 -0
  20. package/dist/chunks/chunk-73RSLGZT.js +73 -0
  21. package/dist/chunks/chunk-7XHHVYG7.js +13 -0
  22. package/dist/chunks/chunk-AB35TJST.js +67 -0
  23. package/dist/chunks/chunk-BPLIXMH7.js +52 -0
  24. package/dist/chunks/chunk-BUBZ32IC.js +101 -0
  25. package/dist/chunks/chunk-BXEPA4KS.js +179 -0
  26. package/dist/chunks/chunk-C44YRP5F.js +85 -0
  27. package/dist/chunks/chunk-CDLPYR5O.js +135 -0
  28. package/dist/chunks/chunk-CJ2EE4AF.js +40 -0
  29. package/dist/chunks/chunk-CPMHA4W5.js +6 -0
  30. package/dist/chunks/chunk-CR7EGAFE.js +241 -0
  31. package/dist/chunks/chunk-CS3E4ETM.js +202 -0
  32. package/dist/chunks/chunk-CTSBC2O6.js +90 -0
  33. package/dist/chunks/chunk-D3M7SBKL.js +210 -0
  34. package/dist/chunks/chunk-DKOB2OPC.js +177 -0
  35. package/dist/chunks/chunk-EJ5OZTCK.js +52 -0
  36. package/dist/chunks/chunk-EK4HGYO7.js +174 -0
  37. package/dist/chunks/chunk-EPZORFTI.js +23 -0
  38. package/dist/chunks/chunk-ES2N6V56.js +83 -0
  39. package/dist/chunks/chunk-EZPBJPK6.js +54 -0
  40. package/dist/chunks/chunk-FYQWPEEY.js +65 -0
  41. package/dist/chunks/chunk-GGQRADFS.js +170 -0
  42. package/dist/chunks/chunk-GOY6REMT.js +43 -0
  43. package/dist/chunks/chunk-GQ2AOHU4.js +112 -0
  44. package/dist/chunks/chunk-HNQPEMYE.js +297 -0
  45. package/dist/chunks/chunk-IGVV37VI.js +60 -0
  46. package/dist/chunks/chunk-IO2PCPZI.js +218 -0
  47. package/dist/chunks/chunk-ISEI2FH3.js +108 -0
  48. package/dist/chunks/chunk-JKJWSY4V.js +32 -0
  49. package/dist/chunks/chunk-KQFOCDFY.js +73 -0
  50. package/dist/chunks/chunk-L3O7AWLI.js +52 -0
  51. package/dist/chunks/chunk-LLJUG2H7.js +6 -0
  52. package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
  53. package/dist/chunks/chunk-LTUWJKHD.js +112 -0
  54. package/dist/chunks/chunk-MFKORG3F.js +44 -0
  55. package/dist/chunks/chunk-MI35EUIE.js +76 -0
  56. package/dist/chunks/chunk-MNUSQCLG.js +29 -0
  57. package/dist/chunks/chunk-MQJUG2D2.js +53 -0
  58. package/dist/chunks/chunk-MREQ7GIA.js +181 -0
  59. package/dist/chunks/chunk-N62KBLFC.js +326 -0
  60. package/dist/chunks/chunk-NNODIW4U.js +26 -0
  61. package/dist/chunks/chunk-PBENCR5L.js +60 -0
  62. package/dist/chunks/chunk-PCZ4PTYC.js +726 -0
  63. package/dist/chunks/chunk-PUG7LXDF.js +70 -0
  64. package/dist/chunks/chunk-QYBZZI5H.js +0 -0
  65. package/dist/chunks/chunk-R6ZUK4Y3.js +232 -0
  66. package/dist/chunks/chunk-RAKOB6IV.js +169 -0
  67. package/dist/chunks/chunk-ROF2R3RF.js +205 -0
  68. package/dist/chunks/chunk-RVS2QCOX.js +135 -0
  69. package/dist/chunks/chunk-ST2IBUAN.js +119 -0
  70. package/dist/chunks/chunk-SZNZ2T6D.js +198 -0
  71. package/dist/chunks/chunk-T43W7NKK.js +122 -0
  72. package/dist/chunks/chunk-TAMCZHLW.js +190 -0
  73. package/dist/chunks/chunk-TDQLINVI.js +16 -0
  74. package/dist/chunks/chunk-TEDQJPMI.js +131 -0
  75. package/dist/chunks/chunk-TZFWUT2C.js +66 -0
  76. package/dist/chunks/chunk-UVBGRJQX.js +50 -0
  77. package/dist/chunks/chunk-VHW7FDHU.js +42 -0
  78. package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
  79. package/dist/chunks/chunk-WFX3MAKA.js +102 -0
  80. package/dist/chunks/chunk-WOO7QYKB.js +121 -0
  81. package/dist/chunks/chunk-WSIFAZU7.js +132 -0
  82. package/dist/chunks/chunk-X5L37W4J.js +82 -0
  83. package/dist/chunks/chunk-Y3IOTZJA.js +61 -0
  84. package/dist/chunks/chunk-YMSSF2RK.js +257 -0
  85. package/dist/cli/figure.js +84 -17
  86. package/dist/colorSchemes.js +10 -0
  87. package/dist/deletionEdges.js +13 -0
  88. package/dist/el.js +8 -0
  89. package/dist/facetGrid.js +16 -0
  90. package/dist/figure.js +51 -0
  91. package/dist/gaf/parseGaf.js +12 -0
  92. package/dist/gbzCut.js +19 -0
  93. package/dist/gbzJoin.js +6 -0
  94. package/dist/gbzWindow.js +28 -0
  95. package/dist/genes/exonOutline.js +11 -0
  96. package/dist/genes/geneFiles.js +14 -0
  97. package/dist/genes/genePins.js +10 -0
  98. package/dist/gfa/gfaConverter.js +11 -0
  99. package/dist/gfa-core/gfaParser.js +8 -0
  100. package/dist/gfa-core/index.js +9 -0
  101. package/dist/graphLabels.js +24 -0
  102. package/dist/index.js +262 -1407
  103. package/dist/labelLayout.js +24 -0
  104. package/dist/layout/anchoredLayout.js +11 -0
  105. package/dist/layout/deletionRoutes.js +10 -0
  106. package/dist/layout/drawnScale.js +22 -0
  107. package/dist/layout/mergeRuns.js +15 -0
  108. package/dist/layout/orderedLayout.js +12 -0
  109. package/dist/layout/orientToReference.js +7 -0
  110. package/dist/layout/placeOffReference.js +9 -0
  111. package/dist/layout/referenceSeeds.js +11 -0
  112. package/dist/layout/rowSpacing.js +6 -0
  113. package/dist/layout/sampleRowLayout.js +11 -0
  114. package/dist/layout/trimToWindow.js +99 -0
  115. package/dist/layout/tubeMapLayout.js +28 -0
  116. package/dist/layout/walkRowDraw.js +65 -0
  117. package/dist/layout/walkRowLayout.js +13 -0
  118. package/dist/layout/walkRows.js +11 -0
  119. package/dist/layout/walkStrip.js +40 -0
  120. package/dist/layoutModes.js +35 -0
  121. package/dist/loadBandage.js +6 -0
  122. package/dist/nodeWidths.js +18 -0
  123. package/dist/overlayLabels.js +18 -0
  124. package/dist/pansn.js +18 -0
  125. package/dist/pathAnchoring.js +18 -0
  126. package/dist/pathColors.js +31 -0
  127. package/dist/pipeline.js +40 -0
  128. package/dist/reference.js +24 -0
  129. package/dist/referenceRampCss.js +29 -0
  130. package/dist/referenceSpan.js +11 -0
  131. package/dist/referenceStrip.js +35 -0
  132. package/dist/renderer/Canvas2DRenderer.js +9 -0
  133. package/dist/renderer/GeometryBuilder.js +42 -0
  134. package/dist/renderer/colorBits.js +8 -0
  135. package/dist/renderer/recordingCanvas.js +44 -0
  136. package/dist/renderer/svgCanvas.js +6 -0
  137. package/dist/renderer/types.js +0 -0
  138. package/dist/tubeMap/axis.js +25 -0
  139. package/dist/tubeMap/coarsen.js +361 -0
  140. package/dist/tubeMap/connectors.js +13 -0
  141. package/dist/tubeMap/deviations.js +85 -0
  142. package/dist/tubeMap/draw.js +15 -0
  143. package/dist/tubeMap/frame.js +9 -0
  144. package/dist/tubeMap/genes.js +20 -0
  145. package/dist/tubeMap/mismatches.js +6 -0
  146. package/dist/tubeMap/nodeColors.js +21 -0
  147. package/dist/tubeMap/reads.js +17 -0
  148. package/dist/tubeMap/warp.js +8 -0
  149. package/dist/types/version.d.ts +1 -1
  150. package/dist/types.js +0 -0
  151. package/dist/util/SpatialIndex.js +13 -0
  152. package/dist/util/edgeCurves.js +10 -0
  153. package/dist/util/geometry.js +39 -0
  154. package/dist/util/hitDetection.js +20 -0
  155. package/dist/util/wheelZoom.js +6 -0
  156. package/dist/version.js +6 -0
  157. package/dist/viewport.js +33 -0
  158. package/dist/walkEncoding.js +39 -0
  159. package/dist/walkHighlight.js +26 -0
  160. package/dist/walkKey.js +17 -0
  161. package/package.json +38 -5
  162. package/dist/chunks/chunk-EXSFRX2F.js +0 -7400
@@ -0,0 +1,726 @@
1
+ import {
2
+ fadeAbgr
3
+ } from "./chunk-MNUSQCLG.js";
4
+ import {
5
+ baseEdgeCurves
6
+ } from "./chunk-Y3IOTZJA.js";
7
+ import {
8
+ PATH_LIGHTNESS,
9
+ PATH_SATURATION,
10
+ nameHue,
11
+ pathHueAt
12
+ } from "./chunk-X5L37W4J.js";
13
+ import {
14
+ referenceMidpoints
15
+ } from "./chunk-ES2N6V56.js";
16
+ import {
17
+ meanDepth,
18
+ nodeWidthPx
19
+ } from "./chunk-PBENCR5L.js";
20
+ import {
21
+ dashCurves,
22
+ pathRibbonOffsets,
23
+ strandSides,
24
+ translateCurves,
25
+ yToXOf
26
+ } from "./chunk-2CVHXFK7.js";
27
+
28
+ // src/renderer/GeometryBuilder.ts
29
+ import { abgrToCssRgba, packAbgr } from "@jbrowse/core/util/colorBits";
30
+ var EDGE_DEFAULT_COLOR = packAbgr(119, 119, 119, 217);
31
+ var EDGE_PATH_FALLBACK_COLOR = packAbgr(136, 136, 136, 217);
32
+ var EDGE_DELETION_COLOR = packAbgr(24, 24, 28, 240);
33
+ var DELETION_THICKNESS_FACTOR = 2.2;
34
+ var LIFT_BACKDROP_COLOR = packAbgr(160, 160, 160, 255);
35
+ var FADED_ALPHA = 0.18;
36
+ var LIFT_BACKDROP_CSS = abgrToCssRgba(
37
+ fadeAbgr(LIFT_BACKDROP_COLOR, FADED_ALPHA)
38
+ );
39
+ var DELETION_DASH_PX = 11;
40
+ var MIN_PATH_STRIPE_PX = 1.2;
41
+ var MIN_WALK_LANE_PX = 4;
42
+ var MIN_SCALE_FOR_OFFSET = 1e-6;
43
+ var ARROW_LENGTH_BASE_PX = 7;
44
+ var ARROW_LENGTH_PER_EDGE_PX = 3;
45
+ var ARROW_HALF_WIDTH_BASE_PX = 3;
46
+ var ARROW_HALF_WIDTH_PER_EDGE_PX = 1.5;
47
+ var MIN_LINK_HEADS = 3;
48
+ var MIN_ARROW_FIT = 0.5;
49
+ var MIN_ARROW_SCALE = 0.45;
50
+ var ARROW_CURVE_STEPS = 12;
51
+ var ABUTTING_PX = 1e-6;
52
+ var walkX = new Float64Array(64);
53
+ var walkY = new Float64Array(64);
54
+ var walkPx = new Float64Array(64);
55
+ function backFromEnd(curves, scale, yToX, untilPx) {
56
+ const most = curves.length * ARROW_CURVE_STEPS + 1;
57
+ if (walkX.length < most) {
58
+ walkX = new Float64Array(most);
59
+ walkY = new Float64Array(most);
60
+ walkPx = new Float64Array(most);
61
+ }
62
+ const last = curves[curves.length - 1];
63
+ walkX[0] = last.x1;
64
+ walkY[0] = last.y1;
65
+ walkPx[0] = 0;
66
+ let n = 1;
67
+ let px = 0;
68
+ for (let ci = curves.length - 1; ci >= 0 && px < untilPx; ci--) {
69
+ const c = curves[ci];
70
+ for (let k = 1; k <= ARROW_CURVE_STEPS && px < untilPx; k++) {
71
+ const t = 1 - (k / ARROW_CURVE_STEPS) ** 2;
72
+ const u = 1 - t;
73
+ const a = u * u * u;
74
+ const b = 3 * u * u * t;
75
+ const d = 3 * u * t * t;
76
+ const e = t * t * t;
77
+ const x = a * c.x0 + b * c.cx0 + d * c.cx1 + e * c.x1;
78
+ const y = a * c.y0 + b * c.cy0 + d * c.cy1 + e * c.y1;
79
+ px += Math.hypot(x - walkX[n - 1], (y - walkY[n - 1]) * yToX) * scale;
80
+ walkX[n] = x;
81
+ walkY[n] = y;
82
+ walkPx[n] = px;
83
+ n++;
84
+ }
85
+ }
86
+ return n;
87
+ }
88
+ function walkPointAt(n, px) {
89
+ let i = 1;
90
+ while (i < n - 1 && walkPx[i] < px) {
91
+ i++;
92
+ }
93
+ const from = walkPx[i - 1];
94
+ const to = walkPx[i];
95
+ const f = to > from ? Math.min(1, Math.max(0, (px - from) / (to - from))) : 0;
96
+ return {
97
+ x: walkX[i - 1] + (walkX[i] - walkX[i - 1]) * f,
98
+ y: walkY[i - 1] + (walkY[i] - walkY[i - 1]) * f
99
+ };
100
+ }
101
+ function arrowheadFor({
102
+ curves,
103
+ edgeHalfWidth,
104
+ nodeHalfWidth,
105
+ scale,
106
+ yToX,
107
+ color,
108
+ fromSegments,
109
+ toSegments
110
+ }) {
111
+ const length = ARROW_LENGTH_BASE_PX + ARROW_LENGTH_PER_EDGE_PX * edgeHalfWidth;
112
+ const n = backFromEnd(
113
+ curves,
114
+ scale,
115
+ yToX,
116
+ nodeHalfWidth + MIN_LINK_HEADS * length
117
+ );
118
+ const lengthPx = walkPx[n - 1];
119
+ const abutting = lengthPx < ABUTTING_PX;
120
+ const inset = Math.min(nodeHalfWidth, lengthPx);
121
+ const fit = abutting ? Math.min(1, polylinePx(fromSegments, scale, yToX) / length) : 1;
122
+ if (abutting ? fit < MIN_ARROW_FIT : lengthPx < MIN_LINK_HEADS * length) {
123
+ return void 0;
124
+ }
125
+ const tip = walkPointAt(n, inset);
126
+ const base = walkPointAt(n, inset + length * fit);
127
+ const dx = tip.x - base.x;
128
+ const dy = (tip.y - base.y) * yToX;
129
+ const last = curves[curves.length - 1];
130
+ return {
131
+ x: tip.x,
132
+ y: tip.y,
133
+ angle: Math.hypot(dx, dy) > 0 ? Math.atan2(dy, dx) : endTangent(last, yToX, intoDirection(last, toSegments)),
134
+ length: length * fit,
135
+ halfWidth: (ARROW_HALF_WIDTH_BASE_PX + ARROW_HALF_WIDTH_PER_EDGE_PX * edgeHalfWidth) * fit,
136
+ color
137
+ };
138
+ }
139
+ function polylinePx(segments, scale, yToX) {
140
+ let length = 0;
141
+ for (let i = 1; i < segments.length; i++) {
142
+ length += Math.hypot(
143
+ segments[i].x - segments[i - 1].x,
144
+ (segments[i].y - segments[i - 1].y) * yToX
145
+ );
146
+ }
147
+ return length * scale;
148
+ }
149
+ function leavesStart(edge, segments, first, stranded, yToX) {
150
+ if (stranded) {
151
+ return strandSides(edge).from === "start";
152
+ }
153
+ const gap = (p) => Math.hypot(p.x - first.x0, (p.y - first.y0) * yToX);
154
+ return gap(segments[0]) < gap(segments.at(-1));
155
+ }
156
+ function intoDirection(last, toSegments) {
157
+ if (toSegments.length < 2) {
158
+ return void 0;
159
+ }
160
+ const first = toSegments[0];
161
+ const final = toSegments[toSegments.length - 1];
162
+ const atStart = Math.hypot(last.x1 - first.x, last.y1 - first.y) <= Math.hypot(last.x1 - final.x, last.y1 - final.y);
163
+ return atStart ? { from: first, to: toSegments[1] } : { from: final, to: toSegments[toSegments.length - 2] };
164
+ }
165
+ function pointNormalsOf(rawPoints, yToX = 1) {
166
+ const points = yToX === 1 ? rawPoints : rawPoints.map((p) => ({ x: p.x, y: p.y * yToX }));
167
+ const pointNormals = [];
168
+ for (let i = 0; i < points.length; i++) {
169
+ let nx = 0;
170
+ let ny = 0;
171
+ if (i === 0) {
172
+ const dx = points[1].x - points[0].x;
173
+ const dy = points[1].y - points[0].y;
174
+ const len = Math.hypot(dx, dy);
175
+ if (len > 0) {
176
+ nx = -dy / len;
177
+ ny = dx / len;
178
+ }
179
+ } else if (i === points.length - 1) {
180
+ const dx = points[i].x - points[i - 1].x;
181
+ const dy = points[i].y - points[i - 1].y;
182
+ const len = Math.hypot(dx, dy);
183
+ if (len > 0) {
184
+ nx = -dy / len;
185
+ ny = dx / len;
186
+ }
187
+ } else {
188
+ const dx1 = points[i].x - points[i - 1].x;
189
+ const dy1 = points[i].y - points[i - 1].y;
190
+ const len1 = Math.hypot(dx1, dy1);
191
+ const dx2 = points[i + 1].x - points[i].x;
192
+ const dy2 = points[i + 1].y - points[i].y;
193
+ const len2 = Math.hypot(dx2, dy2);
194
+ if (len1 > 0 && len2 > 0) {
195
+ const nx1 = -dy1 / len1;
196
+ const ny1 = dx1 / len1;
197
+ const nx2 = -dy2 / len2;
198
+ const ny2 = dx2 / len2;
199
+ nx = (nx1 + nx2) / 2;
200
+ ny = (ny1 + ny2) / 2;
201
+ const dot = nx1 * nx + ny1 * ny;
202
+ if (dot > 0.1) {
203
+ nx /= dot;
204
+ ny /= dot;
205
+ }
206
+ } else if (len1 > 0) {
207
+ nx = -dy1 / len1;
208
+ ny = dx1 / len1;
209
+ } else if (len2 > 0) {
210
+ nx = -dy2 / len2;
211
+ ny = dx2 / len2;
212
+ }
213
+ }
214
+ pointNormals.push({ nx, ny });
215
+ }
216
+ return pointNormals;
217
+ }
218
+ function offsetPolyline(points, normals, distance, yToX = 1) {
219
+ return points.map((p, i) => ({
220
+ x: p.x + normals[i].nx * distance,
221
+ y: p.y + normals[i].ny * distance / yToX
222
+ }));
223
+ }
224
+ function packNorm(r, g, b, a) {
225
+ return packAbgr(
226
+ Math.round(r * 255),
227
+ Math.round(g * 255),
228
+ Math.round(b * 255),
229
+ Math.round(a * 255)
230
+ );
231
+ }
232
+ function hslToRgb(h, s, l) {
233
+ const c = (1 - Math.abs(2 * l - 1)) * s;
234
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
235
+ const m = l - c / 2;
236
+ let r = 0;
237
+ let g = 0;
238
+ let b = 0;
239
+ if (h < 60) {
240
+ r = c;
241
+ g = x;
242
+ } else if (h < 120) {
243
+ r = x;
244
+ g = c;
245
+ } else if (h < 180) {
246
+ g = c;
247
+ b = x;
248
+ } else if (h < 240) {
249
+ g = x;
250
+ b = c;
251
+ } else if (h < 300) {
252
+ r = x;
253
+ b = c;
254
+ } else {
255
+ r = c;
256
+ b = x;
257
+ }
258
+ return [r + m, g + m, b + m];
259
+ }
260
+ var DEPTH_GRADIENT = [
261
+ [68, 1, 84],
262
+ [59, 82, 139],
263
+ [33, 145, 140],
264
+ [94, 201, 98],
265
+ [253, 231, 37]
266
+ ];
267
+ var STABLE_RANK_GRADIENT = [
268
+ [237, 137, 44],
269
+ [158, 42, 122]
270
+ ];
271
+ var NODE_LENGTH_GRADIENT = [
272
+ [220, 50, 50],
273
+ [50, 120, 220]
274
+ ];
275
+ function sampleGradient(stops, t) {
276
+ const pos = Math.max(0, Math.min(1, t)) * (stops.length - 1);
277
+ const i = Math.min(stops.length - 2, Math.floor(pos));
278
+ const f = pos - i;
279
+ const a = stops[i];
280
+ const b = stops[i + 1];
281
+ return packAbgr(
282
+ Math.round(a[0] + (b[0] - a[0]) * f),
283
+ Math.round(a[1] + (b[1] - a[1]) * f),
284
+ Math.round(a[2] + (b[2] - a[2]) * f),
285
+ 255
286
+ );
287
+ }
288
+ function hashColor(str, alpha) {
289
+ const [r, g, b] = hslToRgb(nameHue(str), PATH_SATURATION, PATH_LIGHTNESS);
290
+ return packNorm(r, g, b, alpha);
291
+ }
292
+ var REFERENCE_RAMP_MAX_HUE = 300;
293
+ var REFERENCE_RAMP_SATURATION = 0.7;
294
+ var REFERENCE_RAMP_LIGHTNESS = 0.5;
295
+ var REFERENCE_RAMP_ALT_RGB = [60, 65, 72];
296
+ var REFERENCE_RAMP_ALT_COLOR = packAbgr(...REFERENCE_RAMP_ALT_RGB, 255);
297
+ var REFERENCE_RAMP_ALT_CSS = `rgb(${REFERENCE_RAMP_ALT_RGB.join(", ")})`;
298
+ function computeReferenceRamp(graph, domain) {
299
+ const midpoints = referenceMidpoints(graph);
300
+ if (domain && domain.end > domain.start) {
301
+ return {
302
+ start: domain.start,
303
+ span: domain.end - domain.start,
304
+ midpoints
305
+ };
306
+ }
307
+ let min = Infinity;
308
+ let max = -Infinity;
309
+ for (const mid of midpoints.values()) {
310
+ min = Math.min(min, mid);
311
+ max = Math.max(max, mid);
312
+ }
313
+ return {
314
+ start: min === Infinity ? 0 : min,
315
+ span: max > min ? max - min : 1,
316
+ midpoints
317
+ };
318
+ }
319
+ function computeColorSchemeRange(graph) {
320
+ let minDepth = Infinity;
321
+ let maxDepth = -Infinity;
322
+ let minLength = Infinity;
323
+ let maxLength = -Infinity;
324
+ let maxRank = 0;
325
+ for (const n of graph.nodes) {
326
+ if (n.stable !== void 0 && n.stable.rank > maxRank) {
327
+ maxRank = n.stable.rank;
328
+ }
329
+ if (n.depth < minDepth) {
330
+ minDepth = n.depth;
331
+ }
332
+ if (n.depth > maxDepth) {
333
+ maxDepth = n.depth;
334
+ }
335
+ if (n.length < minLength) {
336
+ minLength = n.length;
337
+ }
338
+ if (n.length > maxLength) {
339
+ maxLength = n.length;
340
+ }
341
+ }
342
+ return {
343
+ minDepth,
344
+ maxDepth,
345
+ minLength,
346
+ maxLength,
347
+ maxRank,
348
+ nodeCount: graph.nodes.length
349
+ };
350
+ }
351
+ function getNodeColor(node, nodeIndex, colorScheme, range) {
352
+ switch (colorScheme) {
353
+ case "random":
354
+ return hashColor(node.id, 1);
355
+ case "depth":
356
+ return sampleGradient(
357
+ DEPTH_GRADIENT,
358
+ range.maxDepth > range.minDepth ? (node.depth - range.minDepth) / (range.maxDepth - range.minDepth) : 0.5
359
+ );
360
+ case "node-length":
361
+ return sampleGradient(
362
+ NODE_LENGTH_GRADIENT,
363
+ range.maxLength > range.minLength ? (node.length - range.minLength) / (range.maxLength - range.minLength) : 0.5
364
+ );
365
+ // rGFA states the backbone rather than leaving it to be inferred: rank 0 is
366
+ // the reference, higher ranks are the sequence that diverges from it. A
367
+ // graph with no SR tags draws every node as unranked.
368
+ case "stable-rank":
369
+ return node.stable === void 0 ? packAbgr(160, 160, 160, 255) : node.stable.rank === 0 ? packAbgr(52, 152, 219, 255) : sampleGradient(
370
+ STABLE_RANK_GRADIENT,
371
+ range.maxRank > 1 ? (node.stable.rank - 1) / (range.maxRank - 1) : 0
372
+ );
373
+ // Position on the reference, the one quantity both panels of a
374
+ // graph-over-linear figure can state. A node with no reference position at
375
+ // all — nothing anchored, or an allele whose flanks fell outside the cut —
376
+ // is grey rather than a hue it has not earned.
377
+ case "reference-position": {
378
+ const ramp = range.referenceRamp;
379
+ const mid = ramp?.midpoints.get(node.id);
380
+ if (ramp === void 0 || mid === void 0) {
381
+ return packAbgr(160, 160, 160, 255);
382
+ }
383
+ if (node.stable !== void 0 && node.stable.rank > 0) {
384
+ return REFERENCE_RAMP_ALT_COLOR;
385
+ }
386
+ const frac = Math.max(0, Math.min(1, (mid - ramp.start) / ramp.span));
387
+ const [r, g, b] = hslToRgb(
388
+ frac * REFERENCE_RAMP_MAX_HUE,
389
+ REFERENCE_RAMP_SATURATION,
390
+ REFERENCE_RAMP_LIGHTNESS
391
+ );
392
+ return packNorm(r, g, b, 1);
393
+ }
394
+ case "grey":
395
+ return packAbgr(160, 160, 160, 255);
396
+ case "rainbow": {
397
+ const hue = range.nodeCount > 1 ? nodeIndex / range.nodeCount * 360 : 0;
398
+ const [r, g, b] = hslToRgb(hue, 0.75, 0.5);
399
+ return packNorm(r, g, b, 1);
400
+ }
401
+ default:
402
+ return packAbgr(52, 152, 219, 255);
403
+ }
404
+ }
405
+ function endTangent(c, yToX = 1, into) {
406
+ const candidates = [
407
+ [c.cx1, c.cy1],
408
+ [c.cx0, c.cy0],
409
+ [c.x0, c.y0]
410
+ ];
411
+ for (const [x, y] of candidates) {
412
+ const dx = c.x1 - x;
413
+ const dy = (c.y1 - y) * yToX;
414
+ if (Math.hypot(dx, dy) > 0) {
415
+ return Math.atan2(dy, dx);
416
+ }
417
+ }
418
+ return into ? Math.atan2((into.to.y - into.from.y) * yToX, into.to.x - into.from.x) : 0;
419
+ }
420
+ function isPolylineInBounds(segments, bounds) {
421
+ let hit = false;
422
+ for (let i = 0; i < segments.length - 1 && !hit; i++) {
423
+ const a = segments[i];
424
+ const b = segments[i + 1];
425
+ hit = Math.max(a.x, b.x) >= bounds.minX && Math.min(a.x, b.x) <= bounds.maxX && Math.max(a.y, b.y) >= bounds.minY && Math.min(a.y, b.y) <= bounds.maxY;
426
+ }
427
+ return hit;
428
+ }
429
+ function isBezierInBounds(curves, bounds) {
430
+ for (const c of curves) {
431
+ const cMinX = Math.min(c.x0, c.cx0, c.cx1, c.x1);
432
+ const cMaxX = Math.max(c.x0, c.cx0, c.cx1, c.x1);
433
+ const cMinY = Math.min(c.y0, c.cy0, c.cy1, c.y1);
434
+ const cMaxY = Math.max(c.y0, c.cy0, c.cy1, c.y1);
435
+ if (cMaxX >= bounds.minX && cMinX <= bounds.maxX && cMaxY >= bounds.minY && cMinY <= bounds.maxY) {
436
+ return true;
437
+ }
438
+ }
439
+ return false;
440
+ }
441
+ var paletteCache = /* @__PURE__ */ new WeakMap();
442
+ function pathPaletteOf(graph) {
443
+ let palette = paletteCache.get(graph);
444
+ if (!palette) {
445
+ const pathColorByIndex = [];
446
+ const pathIndexByName = /* @__PURE__ */ new Map();
447
+ const paths = graph.paths ?? [];
448
+ for (let i = 0; i < paths.length; i++) {
449
+ const [r, g, b] = hslToRgb(
450
+ pathHueAt(i, paths.length),
451
+ PATH_SATURATION,
452
+ PATH_LIGHTNESS
453
+ );
454
+ pathColorByIndex.push(packNorm(r, g, b, 0.85));
455
+ if (!pathIndexByName.has(paths[i].name)) {
456
+ pathIndexByName.set(paths[i].name, i);
457
+ }
458
+ }
459
+ palette = { pathColorByIndex, pathIndexByName };
460
+ paletteCache.set(graph, palette);
461
+ }
462
+ return palette;
463
+ }
464
+ var slotsCache = /* @__PURE__ */ new WeakMap();
465
+ var NO_SLOTS = /* @__PURE__ */ new Map();
466
+ function pathSlotsOf(graph) {
467
+ let slots = slotsCache.get(graph);
468
+ if (!slots) {
469
+ slots = /* @__PURE__ */ new Map();
470
+ for (const [pathIdx, path] of (graph.paths ?? []).entries()) {
471
+ for (const nodeId of new Set(path.nodeIds)) {
472
+ const visits = slots.get(nodeId);
473
+ if (visits) {
474
+ visits.push(pathIdx);
475
+ } else {
476
+ slots.set(nodeId, [pathIdx]);
477
+ }
478
+ }
479
+ }
480
+ slotsCache.set(graph, slots);
481
+ }
482
+ return slots;
483
+ }
484
+ function buildGeometry(options) {
485
+ const {
486
+ nodePositions,
487
+ graph,
488
+ nodeById,
489
+ colorScheme,
490
+ contigThickness,
491
+ connectorThickness,
492
+ drawPaths,
493
+ nodeWidth = "uniform",
494
+ highlight,
495
+ axis,
496
+ linearLayout,
497
+ viewportBounds,
498
+ referenceRamp,
499
+ deletions,
500
+ deletionRoutes,
501
+ stranded,
502
+ hiddenEdges,
503
+ version = 0
504
+ } = options;
505
+ const depthNorm = nodeWidth === "depth" ? meanDepth(graph) : 0;
506
+ const scale = axis.scaleX;
507
+ const yToX = yToXOf(axis);
508
+ const sharedCurves = baseEdgeCurves(
509
+ nodePositions,
510
+ graph,
511
+ axis,
512
+ deletions,
513
+ version,
514
+ deletionRoutes,
515
+ stranded
516
+ );
517
+ const nodeStrokes = [];
518
+ const nodeStrokeRuns = /* @__PURE__ */ new Map();
519
+ const arrows = [];
520
+ const arrowRuns = /* @__PURE__ */ new Map();
521
+ const edgeCurves = [];
522
+ const edgeCurveRuns = /* @__PURE__ */ new Map();
523
+ const colorRange = { ...computeColorSchemeRange(graph), referenceRamp };
524
+ const nodeIndexMap = /* @__PURE__ */ new Map();
525
+ if (colorScheme === "rainbow") {
526
+ for (let i = 0; i < graph.nodes.length; i++) {
527
+ nodeIndexMap.set(graph.nodes[i].id, i);
528
+ }
529
+ }
530
+ const pathCount = graph.paths?.length ?? 0;
531
+ const { pathColorByIndex, pathIndexByName } = pathPaletteOf(graph);
532
+ const pathColorByName = (name) => pathColorByIndex[pathIndexByName.get(name) ?? -1] ?? EDGE_PATH_FALLBACK_COLOR;
533
+ const nodePathSlots = drawPaths && pathCount > 1 ? pathSlotsOf(graph) : NO_SLOTS;
534
+ const showArrows = scale > (linearLayout ? 1 : MIN_ARROW_SCALE);
535
+ for (let ei = 0; ei < graph.edges.length; ei++) {
536
+ const edge = graph.edges[ei];
537
+ const fromSegments = nodePositions[edge.from];
538
+ const toSegments = nodePositions[edge.to];
539
+ const baseCurves = sharedCurves.get(ei);
540
+ if (!fromSegments?.length || !toSegments?.length || !baseCurves || hiddenEdges?.has(ei)) {
541
+ continue;
542
+ }
543
+ const numPaths = edge.pathIds?.length ?? 0;
544
+ const bypassed = deletions?.get(ei);
545
+ const isDeletion = bypassed !== void 0;
546
+ const onWalk = highlight?.edgeIndexes.has(ei) ?? false;
547
+ const edgeThickness = connectorThickness / 2 * (isDeletion ? DELETION_THICKNESS_FACTOR : 1);
548
+ const ribbons = drawPaths && numPaths > 0;
549
+ const plainEdgeColor = isDeletion ? EDGE_DELETION_COLOR : EDGE_DEFAULT_COLOR;
550
+ const edgeColor = !highlight || onWalk ? plainEdgeColor : fadeAbgr(plainEdgeColor, FADED_ALPHA);
551
+ const toNode = nodeById.get(edge.to);
552
+ const intoHalfWidth = (toNode ? nodeWidthPx(toNode, contigThickness, nodeWidth, depthNorm) : contigThickness) / 2;
553
+ const buildSingleEdge = (offsetX, offsetY, color, arrowColor) => {
554
+ const curves = offsetX === 0 && offsetY === 0 ? baseCurves : translateCurves(baseCurves, offsetX, offsetY);
555
+ if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {
556
+ return;
557
+ }
558
+ if (isDeletion) {
559
+ for (const dash of dashCurves(curves, DELETION_DASH_PX / scale)) {
560
+ edgeCurves.push({ curves: dash, thickness: edgeThickness, color });
561
+ }
562
+ } else {
563
+ edgeCurves.push({ curves, thickness: edgeThickness, color });
564
+ }
565
+ const head = showArrows && arrowColor !== void 0 ? arrowheadFor({
566
+ curves,
567
+ edgeHalfWidth: connectorThickness / 2,
568
+ nodeHalfWidth: intoHalfWidth,
569
+ scale,
570
+ yToX,
571
+ color: arrowColor,
572
+ fromSegments,
573
+ toSegments
574
+ }) : void 0;
575
+ if (head) {
576
+ arrows.push(head);
577
+ }
578
+ };
579
+ const edgeCurveStart = edgeCurves.length;
580
+ const arrowStart = arrows.length;
581
+ const laneWalks = highlight && onWalk && !ribbons ? highlight.walks : void 0;
582
+ if (laneWalks) {
583
+ const fromNode = nodeById.get(edge.from);
584
+ const width = fromNode ? nodeWidthPx(fromNode, contigThickness, nodeWidth, depthNorm) : contigThickness;
585
+ const laneWidth = Math.max(width / laneWalks.length, MIN_WALK_LANE_PX);
586
+ const normals = pointNormalsOf(fromSegments, yToX);
587
+ const { nx, ny } = (leavesStart(
588
+ edge,
589
+ fromSegments,
590
+ baseCurves[0],
591
+ stranded,
592
+ yToX
593
+ ) ? normals[0] : normals.at(-1)) ?? {
594
+ nx: 0,
595
+ ny: 0
596
+ };
597
+ const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET);
598
+ laneWalks.forEach((walk, lane) => {
599
+ if (!walk.edgeIndexes.has(ei)) {
600
+ return;
601
+ }
602
+ const offset = (lane - (laneWalks.length - 1) / 2) * laneWidth * worldPerScreenPx;
603
+ const curves = offset === 0 ? baseCurves : translateCurves(baseCurves, nx * offset, ny * offset / yToX);
604
+ if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {
605
+ return;
606
+ }
607
+ edgeCurves.push({
608
+ curves,
609
+ thickness: laneWidth / 2,
610
+ color: walk.colors.get(edge.from) ?? walk.colors.get(edge.to) ?? plainEdgeColor
611
+ });
612
+ });
613
+ } else if (!ribbons) {
614
+ buildSingleEdge(0, 0, edgeColor, edgeColor);
615
+ } else {
616
+ const offsets = pathRibbonOffsets(
617
+ fromSegments,
618
+ toSegments,
619
+ numPaths,
620
+ axis
621
+ );
622
+ const arrowRibbon = Math.floor((numPaths - 1) / 2);
623
+ for (let pathIdx = 0; pathIdx < numPaths; pathIdx++) {
624
+ const offset = offsets[pathIdx];
625
+ const ribbonColor = pathColorByName(edge.pathIds[pathIdx]);
626
+ buildSingleEdge(
627
+ offset.x,
628
+ offset.y,
629
+ highlight && !highlight.names.has(edge.pathIds[pathIdx]) ? fadeAbgr(ribbonColor, FADED_ALPHA) : ribbonColor,
630
+ pathIdx === arrowRibbon ? edgeColor : void 0
631
+ );
632
+ }
633
+ }
634
+ const edgeCurveCount = edgeCurves.length - edgeCurveStart;
635
+ if (edgeCurveCount > 0) {
636
+ edgeCurveRuns.set(ei, { start: edgeCurveStart, count: edgeCurveCount });
637
+ }
638
+ const arrowCount = arrows.length - arrowStart;
639
+ if (arrowCount > 0) {
640
+ arrowRuns.set(ei, { start: arrowStart, count: arrowCount });
641
+ }
642
+ }
643
+ for (const [nodeId, segments] of Object.entries(nodePositions)) {
644
+ const node = nodeById.get(nodeId);
645
+ if (!node) {
646
+ continue;
647
+ }
648
+ if (viewportBounds && !isPolylineInBounds(segments, viewportBounds)) {
649
+ continue;
650
+ }
651
+ const faded = highlight !== void 0 && !highlight.nodeIds.has(nodeId);
652
+ const own = faded ? LIFT_BACKDROP_COLOR : getNodeColor(
653
+ node,
654
+ nodeIndexMap.get(nodeId) ?? 0,
655
+ colorScheme,
656
+ colorRange
657
+ );
658
+ const color = faded ? fadeAbgr(own, FADED_ALPHA) : own;
659
+ const width = nodeWidthPx(node, contigThickness, nodeWidth, depthNorm);
660
+ const nodeThickness = width / 2;
661
+ const start = nodeStrokes.length;
662
+ const slots = nodePathSlots.get(nodeId);
663
+ const slotWidth = width / pathCount;
664
+ const drawable = segments.length >= 2;
665
+ const lanes = highlight && !faded && !slots?.length ? highlight.walks : void 0;
666
+ if (drawable && lanes) {
667
+ const laneWidth = Math.max(width / lanes.length, MIN_WALK_LANE_PX);
668
+ const normals = pointNormalsOf(segments, yToX);
669
+ const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET);
670
+ lanes.forEach((walk, lane) => {
671
+ const laneColor = walk.colors.get(nodeId);
672
+ if (laneColor === void 0) {
673
+ return;
674
+ }
675
+ const offset = (lane - (lanes.length - 1) / 2) * laneWidth * worldPerScreenPx;
676
+ nodeStrokes.push({
677
+ points: lanes.length === 1 ? segments : offsetPolyline(segments, normals, offset, yToX),
678
+ thickness: laneWidth / 2,
679
+ color: laneColor
680
+ });
681
+ });
682
+ } else if (drawable && slots?.length && slotWidth >= MIN_PATH_STRIPE_PX) {
683
+ const normals = pointNormalsOf(segments, yToX);
684
+ const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET);
685
+ for (const slot of slots) {
686
+ const offset = (slot - (pathCount - 1) / 2) * slotWidth * worldPerScreenPx;
687
+ const stripe = pathColorByIndex[slot] ?? EDGE_PATH_FALLBACK_COLOR;
688
+ nodeStrokes.push({
689
+ points: offsetPolyline(segments, normals, offset, yToX),
690
+ thickness: slotWidth / 2,
691
+ // a node off the lifted walk fades whichever way it is painted
692
+ color: faded ? fadeAbgr(stripe, FADED_ALPHA) : stripe
693
+ });
694
+ }
695
+ } else if (drawable) {
696
+ nodeStrokes.push({ points: segments, thickness: nodeThickness, color });
697
+ }
698
+ const count = nodeStrokes.length - start;
699
+ if (count > 0) {
700
+ nodeStrokeRuns.set(nodeId, { start, count });
701
+ }
702
+ }
703
+ return {
704
+ nodeStrokes,
705
+ nodeStrokeRuns,
706
+ arrows,
707
+ arrowRuns,
708
+ edgeCurves,
709
+ edgeCurveRuns
710
+ };
711
+ }
712
+
713
+ export {
714
+ LIFT_BACKDROP_COLOR,
715
+ FADED_ALPHA,
716
+ LIFT_BACKDROP_CSS,
717
+ hslToRgb,
718
+ REFERENCE_RAMP_MAX_HUE,
719
+ REFERENCE_RAMP_ALT_COLOR,
720
+ REFERENCE_RAMP_ALT_CSS,
721
+ computeReferenceRamp,
722
+ computeColorSchemeRange,
723
+ getNodeColor,
724
+ endTangent,
725
+ buildGeometry
726
+ };