@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,513 @@
1
+ import {
2
+ ROW_HEIGHT_PX
3
+ } from "./chunk-CPMHA4W5.js";
4
+
5
+ // src/util/geometry.ts
6
+ function svgPath(points) {
7
+ const round = (v) => Math.round(v * 100) / 100;
8
+ return points.map((p, i) => `${i ? "L" : "M"}${round(p.x)},${round(p.y)}`).join("");
9
+ }
10
+ function projectLine(x1, y1, x2, y2, distance) {
11
+ const d = Math.hypot(y2 - y1, x2 - x1);
12
+ if (d === 0) {
13
+ return [x2, y2];
14
+ }
15
+ const vx = (x2 - x1) / d;
16
+ const vy = (y2 - y1) / d;
17
+ return [x2 + distance * vx, y2 + distance * vy];
18
+ }
19
+ function curvePointAt(c, t) {
20
+ const u = 1 - t;
21
+ const a = u * u * u;
22
+ const b = 3 * u * u * t;
23
+ const d = 3 * u * t * t;
24
+ const e = t * t * t;
25
+ return {
26
+ x: a * c.x0 + b * c.cx0 + d * c.cx1 + e * c.x1,
27
+ y: a * c.y0 + b * c.cy0 + d * c.cy1 + e * c.y1
28
+ };
29
+ }
30
+ function curveMidpoint(curves) {
31
+ const mid = curves[Math.floor(curves.length / 2)];
32
+ return mid ? curvePointAt(mid, 0.5) : void 0;
33
+ }
34
+ function curveBounds(curves) {
35
+ let minX = Infinity;
36
+ let minY = Infinity;
37
+ let maxX = -Infinity;
38
+ let maxY = -Infinity;
39
+ for (const c of curves) {
40
+ minX = Math.min(minX, c.x0, c.cx0, c.cx1, c.x1);
41
+ maxX = Math.max(maxX, c.x0, c.cx0, c.cx1, c.x1);
42
+ minY = Math.min(minY, c.y0, c.cy0, c.cy1, c.y1);
43
+ maxY = Math.max(maxY, c.y0, c.cy0, c.cy1, c.y1);
44
+ }
45
+ return { minX, minY, maxX, maxY };
46
+ }
47
+ function subCurve(c, t0, t1) {
48
+ const p0 = curvePointAt(c, t0);
49
+ const p1 = curvePointAt(c, t1);
50
+ const dt = (t1 - t0) / 3;
51
+ const d0 = curveTangentAt(c, t0);
52
+ const d1 = curveTangentAt(c, t1);
53
+ return {
54
+ x0: p0.x,
55
+ y0: p0.y,
56
+ cx0: p0.x + d0.x * dt,
57
+ cy0: p0.y + d0.y * dt,
58
+ cx1: p1.x - d1.x * dt,
59
+ cy1: p1.y - d1.y * dt,
60
+ x1: p1.x,
61
+ y1: p1.y
62
+ };
63
+ }
64
+ function curveTangentAt(c, t) {
65
+ const u = 1 - t;
66
+ const a = 3 * u * u;
67
+ const b = 6 * u * t;
68
+ const d = 3 * t * t;
69
+ return {
70
+ x: a * (c.cx0 - c.x0) + b * (c.cx1 - c.cx0) + d * (c.x1 - c.cx1),
71
+ y: a * (c.cy0 - c.y0) + b * (c.cy1 - c.cy0) + d * (c.y1 - c.cy1)
72
+ };
73
+ }
74
+ function dashCurves(curves, dashWorld) {
75
+ const lengths = curves.map(
76
+ (c) => Math.hypot(c.cx0 - c.x0, c.cy0 - c.y0) + Math.hypot(c.cx1 - c.cx0, c.cy1 - c.cy0) + Math.hypot(c.x1 - c.cx1, c.y1 - c.cy1)
77
+ );
78
+ const total = lengths.reduce((a, b) => a + b, 0);
79
+ const spans = Math.max(3, Math.round(total / dashWorld) | 1);
80
+ const out = [];
81
+ for (let i = 0; i < spans; i += 2) {
82
+ const from = i / spans * total;
83
+ const to = (i + 1) / spans * total;
84
+ const dash = [];
85
+ let start = 0;
86
+ curves.forEach((c, k) => {
87
+ const length = lengths[k];
88
+ const end = start + length;
89
+ if (length > 0 && end > from && start < to) {
90
+ dash.push(
91
+ subCurve(
92
+ c,
93
+ Math.max(0, (from - start) / length),
94
+ Math.min(1, (to - start) / length)
95
+ )
96
+ );
97
+ }
98
+ start = end;
99
+ });
100
+ if (dash.length) {
101
+ out.push(dash);
102
+ }
103
+ }
104
+ return out;
105
+ }
106
+ function translateCurves(curves, dx, dy) {
107
+ const out = new Array(curves.length);
108
+ for (let i = 0; i < curves.length; i++) {
109
+ const c = curves[i];
110
+ out[i] = {
111
+ x0: c.x0 + dx,
112
+ y0: c.y0 + dy,
113
+ cx0: c.cx0 + dx,
114
+ cy0: c.cy0 + dy,
115
+ cx1: c.cx1 + dx,
116
+ cy1: c.cy1 + dy,
117
+ x1: c.x1 + dx,
118
+ y1: c.y1 + dy
119
+ };
120
+ }
121
+ return out;
122
+ }
123
+ function attachment(segments, side) {
124
+ const last = segments.length - 1;
125
+ return side === "end" ? { at: segments[last], inward: segments[last - 1] } : { at: segments[0], inward: segments[1] };
126
+ }
127
+ var TANGENT_SPAN_FACTOR = 0.5;
128
+ var MAX_TANGENT = 80;
129
+ function ownStrand(id) {
130
+ return id.endsWith("-") ? "-" : "+";
131
+ }
132
+ function strandSides(edge) {
133
+ const own = (id, strand) => (strand ?? ownStrand(id)) === ownStrand(id);
134
+ return {
135
+ from: own(edge.from, edge.fromStrand) ? "end" : "start",
136
+ to: own(edge.to, edge.toStrand) ? "start" : "end"
137
+ };
138
+ }
139
+ function spanOf(segments) {
140
+ let min = Infinity;
141
+ let max = -Infinity;
142
+ for (const s of segments) {
143
+ min = Math.min(min, s.x);
144
+ max = Math.max(max, s.x);
145
+ }
146
+ return { min, max };
147
+ }
148
+ function distanceToSpan(x, span) {
149
+ return Math.max(0, span.min - x, x - span.max);
150
+ }
151
+ function facingSide(segments, other, tieBreak) {
152
+ const startGap = distanceToSpan(segments[0].x, other);
153
+ const endGap = distanceToSpan(segments[segments.length - 1].x, other);
154
+ return startGap === endGap ? tieBreak : startGap < endGap ? "start" : "end";
155
+ }
156
+ function facingSides(fromSegments, toSegments) {
157
+ return {
158
+ from: facingSide(fromSegments, spanOf(toSegments), "end"),
159
+ to: facingSide(toSegments, spanOf(fromSegments), "start")
160
+ };
161
+ }
162
+ var APEX_FRACTION = 0.75;
163
+ var BOW_CLEARANCE = 1.15;
164
+ var ALONG_FRACTION = 0.35;
165
+ var MAX_ROW_BOW_PX = 3 * ROW_HEIGHT_PX;
166
+ function bowAround(p1x, p1y, p2x, p2y, toward, maxBow) {
167
+ const chord = Math.hypot(p2x - p1x, p2y - p1y);
168
+ const ux = chord === 0 ? 1 : (p2x - p1x) / chord;
169
+ const uy = chord === 0 ? 0 : (p2y - p1y) / chord;
170
+ let reach = 0;
171
+ let minAlong = Infinity;
172
+ let maxAlong = -Infinity;
173
+ for (const p of toward) {
174
+ const dx = p.x - p1x;
175
+ const dy = p.y - p1y;
176
+ const perp = dx * -uy + dy * ux;
177
+ if (Math.abs(perp) > Math.abs(reach)) {
178
+ reach = perp;
179
+ }
180
+ const along = dx * ux + dy * uy;
181
+ minAlong = Math.min(minAlong, along);
182
+ maxAlong = Math.max(maxAlong, along);
183
+ }
184
+ const span = toward.length ? maxAlong - minAlong : 0;
185
+ const uncapped = Math.max(
186
+ Math.abs(reach) * BOW_CLEARANCE / APEX_FRACTION,
187
+ span * ALONG_FRACTION
188
+ );
189
+ const size = maxBow === void 0 ? uncapped : Math.min(uncapped, maxBow);
190
+ return reach < 0 ? -size : size;
191
+ }
192
+ function yToXOf({ scaleX, scaleY }) {
193
+ return scaleY / scaleX;
194
+ }
195
+ var PATH_RIBBON_SPACING = 3;
196
+ function drawnDirection(a, b, yToX) {
197
+ const dx = b.x - a.x;
198
+ const dy = (b.y - a.y) * yToX;
199
+ const len = Math.hypot(dx, dy);
200
+ return len === 0 ? void 0 : { x: dx / len, y: dy / len };
201
+ }
202
+ function pathRibbonOffsets(fromSegments, toSegments, count, axis, spacing = PATH_RIBBON_SPACING) {
203
+ const yToX = yToXOf(axis);
204
+ const dir = drawnDirection(
205
+ fromSegments[fromSegments.length - 1],
206
+ toSegments[0],
207
+ yToX
208
+ ) ?? drawnDirection(fromSegments[0], fromSegments.at(-1), yToX) ?? drawnDirection(toSegments[0], toSegments.at(-1), yToX) ?? { x: 1, y: 0 };
209
+ const offsets = [];
210
+ for (let i = 0; i < count; i++) {
211
+ const distance = (i - (count - 1) / 2) * spacing;
212
+ offsets.push({ x: -dir.y * distance, y: dir.x * distance / yToX });
213
+ }
214
+ return offsets;
215
+ }
216
+ function scaleYOf(points, yToX) {
217
+ return points.map((p) => ({ x: p.x, y: p.y * yToX }));
218
+ }
219
+ function unscaleYOf(curves, yToX) {
220
+ for (const c of curves) {
221
+ c.y0 /= yToX;
222
+ c.cy0 /= yToX;
223
+ c.cy1 /= yToX;
224
+ c.y1 /= yToX;
225
+ }
226
+ return curves;
227
+ }
228
+ var SELF_LOOP_MIN_PX = 12;
229
+ var SELF_LOOP_MAX_PX = 40;
230
+ function selfLoopReach(segments, scale) {
231
+ let length = 0;
232
+ for (let i = 1; i < segments.length; i++) {
233
+ length += Math.hypot(
234
+ segments[i].x - segments[i - 1].x,
235
+ segments[i].y - segments[i - 1].y
236
+ );
237
+ }
238
+ const nodePx = length * scale;
239
+ const reachPx = Math.min(
240
+ SELF_LOOP_MAX_PX,
241
+ Math.max(SELF_LOOP_MIN_PX, nodePx / 2)
242
+ );
243
+ return reachPx / Math.max(scale, Number.EPSILON);
244
+ }
245
+ function selfLinkOf(edge) {
246
+ if (edge.from !== edge.to) {
247
+ return false;
248
+ }
249
+ return edge.fromStrand && edge.toStrand && edge.fromStrand !== edge.toStrand ? edge.fromStrand === "+" ? "end" : "start" : true;
250
+ }
251
+ function edgeJoinOf(edge, stranded = false) {
252
+ const self = selfLinkOf(edge);
253
+ if (!stranded) {
254
+ return self;
255
+ }
256
+ const sides = strandSides(edge);
257
+ return self === false ? sides : sides.from === sides.to ? sides.from : true;
258
+ }
259
+ function hairpinCurves(attach, reach, offsetX, offsetY) {
260
+ const { at, inward } = attach;
261
+ const dx = inward ? at.x - inward.x : 1;
262
+ const dy = inward ? at.y - inward.y : 0;
263
+ const len = Math.hypot(dx, dy) || 1;
264
+ const ux = dx / len;
265
+ const uy = dy / len;
266
+ const px = at.x + offsetX;
267
+ const py = at.y + offsetY;
268
+ const nearX = px + ux * reach / 3;
269
+ const nearY = py + uy * reach / 3;
270
+ const apexX = px + ux * reach;
271
+ const apexY = py + uy * reach;
272
+ const sideX = -uy * reach / 2;
273
+ const sideY = ux * reach / 2;
274
+ return [
275
+ {
276
+ x0: px,
277
+ y0: py,
278
+ cx0: nearX,
279
+ cy0: nearY,
280
+ cx1: apexX + sideX,
281
+ cy1: apexY + sideY,
282
+ x1: apexX,
283
+ y1: apexY
284
+ },
285
+ {
286
+ x0: apexX,
287
+ y0: apexY,
288
+ cx0: apexX - sideX,
289
+ cy0: apexY - sideY,
290
+ cx1: nearX,
291
+ cy1: nearY,
292
+ x1: px,
293
+ y1: py
294
+ }
295
+ ];
296
+ }
297
+ function computeEdgeCurves(fromSegments, toSegments, join, offsetX, offsetY, axis, bypassed = [], maxBowIn) {
298
+ const scale = axis.scaleX;
299
+ const yToX = yToXOf(axis);
300
+ const maxBow = maxBowIn ?? (axis.pixelRows ? MAX_ROW_BOW_PX : void 0);
301
+ if (yToX !== 1) {
302
+ return unscaleYOf(
303
+ computeEdgeCurves(
304
+ scaleYOf(fromSegments, yToX),
305
+ scaleYOf(toSegments, yToX),
306
+ join,
307
+ offsetX,
308
+ offsetY * yToX,
309
+ { scaleX: scale, scaleY: scale },
310
+ scaleYOf(bypassed, yToX),
311
+ // the cap is a y measure and the body works in x units, so it converts
312
+ // with everything else that crosses this boundary
313
+ maxBow === void 0 ? void 0 : maxBow * yToX
314
+ ),
315
+ yToX
316
+ );
317
+ }
318
+ if (typeof join === "string") {
319
+ return hairpinCurves(
320
+ attachment(fromSegments, join),
321
+ selfLoopReach(fromSegments, scale),
322
+ offsetX,
323
+ offsetY
324
+ );
325
+ }
326
+ const loop = join === true;
327
+ const sides = loop ? { from: "end", to: "start" } : join || facingSides(fromSegments, toSegments);
328
+ const fromAttach = attachment(fromSegments, sides.from);
329
+ const toAttach = attachment(toSegments, sides.to);
330
+ const fromEnd = fromAttach.at;
331
+ const toStart = toAttach.at;
332
+ const p1x = fromEnd.x + offsetX;
333
+ const p1y = fromEnd.y + offsetY;
334
+ const p2x = toStart.x + offsetX;
335
+ const p2y = toStart.y + offsetY;
336
+ if (loop) {
337
+ let segDirX = 1;
338
+ let segDirY = 0;
339
+ if (fromAttach.inward) {
340
+ const dx = fromEnd.x - fromAttach.inward.x;
341
+ const dy = fromEnd.y - fromAttach.inward.y;
342
+ const len = Math.hypot(dx, dy);
343
+ if (len > 0) {
344
+ segDirX = dx / len;
345
+ segDirY = dy / len;
346
+ }
347
+ }
348
+ const ext = selfLoopReach(fromSegments, scale);
349
+ const perpX = -segDirY;
350
+ const perpY = segDirX;
351
+ const midX = (fromEnd.x + toStart.x) / 2 + offsetX + perpX * ext;
352
+ const midY = (fromEnd.y + toStart.y) / 2 + offsetY + perpY * ext;
353
+ const cp1x = p1x + segDirX * ext;
354
+ const cp1y = p1y + segDirY * ext;
355
+ const cp2x = p2x - segDirX * ext;
356
+ const cp2y = p2y - segDirY * ext;
357
+ return [
358
+ {
359
+ x0: p1x,
360
+ y0: p1y,
361
+ cx0: cp1x,
362
+ cy0: cp1y,
363
+ cx1: cp1x + perpX * ext,
364
+ cy1: cp1y + perpY * ext,
365
+ x1: midX,
366
+ y1: midY
367
+ },
368
+ {
369
+ x0: midX,
370
+ y0: midY,
371
+ cx0: cp2x + perpX * ext,
372
+ cy0: cp2y + perpY * ext,
373
+ cx1: cp2x,
374
+ cy1: cp2y,
375
+ x1: p2x,
376
+ y1: p2y
377
+ }
378
+ ];
379
+ } else {
380
+ const fromPrev = fromAttach.inward ?? {
381
+ x: fromEnd.x - (toStart.x - fromEnd.x),
382
+ y: fromEnd.y - (toStart.y - fromEnd.y)
383
+ };
384
+ const toNext = toAttach.inward ?? {
385
+ x: toStart.x - (fromEnd.x - toStart.x),
386
+ y: toStart.y - (fromEnd.y - toStart.y)
387
+ };
388
+ const dist = Math.hypot(p2x - p1x, p2y - p1y);
389
+ const tangentProj = (prev, at, towardX, towardY) => {
390
+ const dx = at.x - prev.x;
391
+ const dy = at.y - prev.y;
392
+ const len = Math.hypot(dx, dy);
393
+ const along = len === 0 ? 0 : (towardX * dx + towardY * dy) / len;
394
+ return Math.max(
395
+ 0,
396
+ Math.min(dist * TANGENT_SPAN_FACTOR, MAX_TANGENT, along * 0.5)
397
+ );
398
+ };
399
+ const towardToX = toStart.x - fromEnd.x;
400
+ const towardToY = toStart.y - fromEnd.y;
401
+ const [cx1, cy1] = projectLine(
402
+ fromPrev.x,
403
+ fromPrev.y,
404
+ fromEnd.x,
405
+ fromEnd.y,
406
+ tangentProj(fromPrev, fromEnd, towardToX, towardToY)
407
+ );
408
+ const [cx2, cy2] = projectLine(
409
+ toNext.x,
410
+ toNext.y,
411
+ toStart.x,
412
+ toStart.y,
413
+ tangentProj(toNext, toStart, -towardToX, -towardToY)
414
+ );
415
+ const bulge = bypassed.length ? bowAround(p1x, p1y, p2x, p2y, bypassed, maxBow) : 0;
416
+ const chordLen = dist === 0 ? 1 : dist;
417
+ const ux = (p2x - p1x) / chordLen;
418
+ const uy = (p2y - p1y) / chordLen;
419
+ const bulgeX = -uy * bulge;
420
+ const bulgeY = ux * bulge;
421
+ const spreadX = ux * Math.abs(bulge);
422
+ const spreadY = uy * Math.abs(bulge);
423
+ return [
424
+ {
425
+ x0: p1x,
426
+ y0: p1y,
427
+ cx0: cx1 + offsetX + bulgeX - spreadX,
428
+ cy0: cy1 + offsetY + bulgeY - spreadY,
429
+ cx1: cx2 + offsetX + bulgeX + spreadX,
430
+ cy1: cy2 + offsetY + bulgeY + spreadY,
431
+ x1: p2x,
432
+ y1: p2y
433
+ }
434
+ ];
435
+ }
436
+ }
437
+ function nearerEnd(segments, to) {
438
+ const start = segments[0];
439
+ const end = segments.at(-1);
440
+ return Math.hypot(start.x - to.x, start.y - to.y) < Math.hypot(end.x - to.x, end.y - to.y) ? "start" : "end";
441
+ }
442
+ function routedEdgeCurves(fromSegments, toSegments, route, sides) {
443
+ const fromAttach = attachment(
444
+ fromSegments,
445
+ sides?.from ?? nearerEnd(fromSegments, route[0])
446
+ );
447
+ const toAttach = attachment(
448
+ toSegments,
449
+ sides?.to ?? nearerEnd(toSegments, route.at(-1))
450
+ );
451
+ const points = [fromAttach.at, ...route, toAttach.at];
452
+ const tangent = (i) => {
453
+ const p = points[i];
454
+ const span = (q) => Math.hypot(q.x - p.x, q.y - p.y);
455
+ const along = (inward, next) => {
456
+ const len = inward ? span(inward) : 0;
457
+ const reach = span(next);
458
+ return len === 0 ? { x: next.x - p.x, y: next.y - p.y } : {
459
+ x: (p.x - inward.x) / len * reach,
460
+ y: (p.y - inward.y) / len * reach
461
+ };
462
+ };
463
+ if (i === 0) {
464
+ return along(fromAttach.inward, points[1]);
465
+ }
466
+ if (i === points.length - 1) {
467
+ const out = along(toAttach.inward, points[i - 1]);
468
+ return { x: -out.x, y: -out.y };
469
+ }
470
+ return {
471
+ x: (points[i + 1].x - points[i - 1].x) / 2,
472
+ y: (points[i + 1].y - points[i - 1].y) / 2
473
+ };
474
+ };
475
+ const curves = [];
476
+ for (let i = 0; i < points.length - 1; i++) {
477
+ const a = points[i];
478
+ const b = points[i + 1];
479
+ const ta = tangent(i);
480
+ const tb = tangent(i + 1);
481
+ curves.push({
482
+ x0: a.x,
483
+ y0: a.y,
484
+ cx0: a.x + ta.x / 3,
485
+ cy0: a.y + ta.y / 3,
486
+ cx1: b.x - tb.x / 3,
487
+ cy1: b.y - tb.y / 3,
488
+ x1: b.x,
489
+ y1: b.y
490
+ });
491
+ }
492
+ return curves;
493
+ }
494
+
495
+ export {
496
+ svgPath,
497
+ projectLine,
498
+ curvePointAt,
499
+ curveMidpoint,
500
+ curveBounds,
501
+ dashCurves,
502
+ translateCurves,
503
+ ownStrand,
504
+ strandSides,
505
+ bowAround,
506
+ yToXOf,
507
+ PATH_RIBBON_SPACING,
508
+ pathRibbonOffsets,
509
+ selfLinkOf,
510
+ edgeJoinOf,
511
+ computeEdgeCurves,
512
+ routedEdgeCurves
513
+ };
@@ -0,0 +1,69 @@
1
+ import {
2
+ el
3
+ } from "./chunk-NNODIW4U.js";
4
+
5
+ // src/genes/exonOutline.ts
6
+ var EXON_COLOR = "#daa520";
7
+ var EXON_GAP_PX = 1;
8
+ var EXON_LINE_PX = 2;
9
+ var MIN_LANE_PX = 4;
10
+ function exonStretches(pins, halfWidthPx, lift, pathOf = (d) => d) {
11
+ const inkPx = (nodeId) => {
12
+ const own = halfWidthPx(nodeId) * 2;
13
+ return lift?.nodeIds.has(nodeId) ? Math.max(own, lift.walks.length * MIN_LANE_PX) : own;
14
+ };
15
+ return pins.flatMap(
16
+ (pin) => pin.exonsByNode.map(({ nodeId, d }) => ({
17
+ key: `${pin.gene.name}-${pin.gene.start}-${nodeId}`,
18
+ d: pathOf(d),
19
+ inner: inkPx(nodeId) + 2 * EXON_GAP_PX
20
+ }))
21
+ );
22
+ }
23
+ function exonOutlineTree(stretches, o) {
24
+ if (stretches.length === 0) {
25
+ return void 0;
26
+ }
27
+ const stroke = (s, color, width) => el("path", {
28
+ d: s.d,
29
+ fill: "none",
30
+ stroke: color,
31
+ "stroke-width": width,
32
+ "stroke-linecap": "round",
33
+ "stroke-linejoin": "round",
34
+ "vector-effect": o.transform ? "non-scaling-stroke" : void 0
35
+ });
36
+ return el(
37
+ "g",
38
+ {},
39
+ el(
40
+ "mask",
41
+ {
42
+ id: o.id,
43
+ maskUnits: "userSpaceOnUse",
44
+ x: 0,
45
+ y: 0,
46
+ width: o.width,
47
+ height: o.height
48
+ },
49
+ el(
50
+ "g",
51
+ { transform: o.transform },
52
+ ...stretches.map((s) => stroke(s, "#fff", s.inner + 2 * EXON_LINE_PX)),
53
+ ...stretches.map((s) => stroke(s, "#000", s.inner))
54
+ )
55
+ ),
56
+ el("rect", {
57
+ width: o.width,
58
+ height: o.height,
59
+ fill: EXON_COLOR,
60
+ mask: `url(#${o.id})`
61
+ })
62
+ );
63
+ }
64
+
65
+ export {
66
+ EXON_COLOR,
67
+ exonStretches,
68
+ exonOutlineTree
69
+ };