@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
package/README.md CHANGED
@@ -23,7 +23,7 @@ npm install @jbrowse/bandage-core
23
23
  - Figures: `figureSvg` draws a graph and its lifted walks to a standalone SVG,
24
24
  and the `bandage-figure` CLI makes one from a JSON spec with no browser:
25
25
  `npx -p @jbrowse/bandage-core bandage-figure spec.json -o figure.svg`. See
26
- [docs/figures.md](https://github.com/GMOD/jbrowse-plugin-graphgenomeviewer/blob/main/docs/figures.md)
26
+ [docs/figures.md](docs/figures.md)
27
27
 
28
28
  Nothing here imports React, MobX or a JBrowse host. BandageJS's
29
29
  [`src/main.ts`](https://github.com/cmdcolin/BandageJS/blob/main/src/main.ts) is
@@ -31,10 +31,26 @@ the worked example.
31
31
 
32
32
  ## Developing
33
33
 
34
- Lives in the plugin repo as a pnpm workspace package. The plugin imports its
35
- source directly, so a change here needs no publish to reach the plugin.
34
+ ```
35
+ pnpm install
36
+ pnpm test # unit tests
37
+ pnpm build # dist/ with declarations
38
+ pnpm test:wasm # the committed layout engine still loads
39
+ ```
40
+
41
+ The Bandage layout engine is OGDF's FMMM compiled to WASM and committed at
42
+ `src/bandage/bandage-layout.js`; [src/bandage/README.md](src/bandage/README.md)
43
+ says how to rebuild it from `src/bandage/native` and the vendored `vendor/ogdf`.
44
+ A weekly workflow proves the committed artifact still reproduces.
45
+
46
+ CI also packs the core and runs
47
+ [BandageJS](https://github.com/cmdcolin/BandageJS) and
48
+ [jbrowse-plugin-graphgenomeviewer](https://github.com/GMOD/jbrowse-plugin-graphgenomeviewer)
49
+ against it. An API change lands with the consumer's side on a `core-next` branch
50
+ in that repo, which CI tests in place of `main` until the release.
51
+
52
+ Release with `pnpm version patch`: it lints, tests, stamps `src/version.ts`,
53
+ writes the changelog with git-cliff and pushes the `v*` tag, and `publish.yml`
54
+ publishes to npm with trusted publishing.
36
55
 
37
- The core releases with the plugin, at the plugin's version: `pnpm version patch`
38
- at the repo root sets both, and `publish.yml` and `publish-core.yml` each
39
- publish one from the `v*` tag. `publish-core.yml` also runs by hand to publish a
40
- version a failed run left behind.
56
+ Both Bandage and OGDF are GPL, which is why this package is GPL-3.0-or-later.
@@ -0,0 +1,10 @@
1
+ import {
2
+ parsePanSN,
3
+ projectAlleles
4
+ } from "../chunks/chunk-RVS2QCOX.js";
5
+ import "../chunks/chunk-ES2N6V56.js";
6
+ import "../chunks/chunk-FYQWPEEY.js";
7
+ export {
8
+ parsePanSN,
9
+ projectAlleles
10
+ };
@@ -0,0 +1,24 @@
1
+ import {
2
+ REFERENCE_RANK,
3
+ backboneNodes,
4
+ backbonePositions,
5
+ backboneSpan,
6
+ firstNodeAtOrAfter,
7
+ isAnchored,
8
+ isBackbone,
9
+ isOffReference,
10
+ overlapsWindow,
11
+ referenceSpan
12
+ } from "./chunks/chunk-FYQWPEEY.js";
13
+ export {
14
+ REFERENCE_RANK,
15
+ backboneNodes,
16
+ backbonePositions,
17
+ backboneSpan,
18
+ firstNodeAtOrAfter,
19
+ isAnchored,
20
+ isBackbone,
21
+ isOffReference,
22
+ overlapsWindow,
23
+ referenceSpan
24
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ BUBBLE_SPREADS,
3
+ BUBBLE_SPREAD_VALUES,
4
+ spreadFor
5
+ } from "./chunks/chunk-UVBGRJQX.js";
6
+ import "./chunks/chunk-6JEWNXCQ.js";
7
+ export {
8
+ BUBBLE_SPREADS,
9
+ BUBBLE_SPREAD_VALUES,
10
+ spreadFor
11
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ bubbleHalos
3
+ } from "../chunks/chunk-ISEI2FH3.js";
4
+ import "../chunks/chunk-WOO7QYKB.js";
5
+ import "../chunks/chunk-MFKORG3F.js";
6
+ import "../chunks/chunk-2CVHXFK7.js";
7
+ import "../chunks/chunk-CPMHA4W5.js";
8
+ import "../chunks/chunk-FYQWPEEY.js";
9
+ export {
10
+ bubbleHalos
11
+ };
@@ -0,0 +1,14 @@
1
+ import {
2
+ SATURATED_PATH_COUNT,
3
+ bubbleDescription,
4
+ bubbleLabel,
5
+ parseBubbleLine,
6
+ sameBubble
7
+ } from "../chunks/chunk-MFKORG3F.js";
8
+ export {
9
+ SATURATED_PATH_COUNT,
10
+ bubbleDescription,
11
+ bubbleLabel,
12
+ parseBubbleLine,
13
+ sameBubble
14
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ bubblesFromGraph
3
+ } from "../chunks/chunk-4S47XRYA.js";
4
+ import "../chunks/chunk-MFKORG3F.js";
5
+ import "../chunks/chunk-HNQPEMYE.js";
6
+ import "../chunks/chunk-CPMHA4W5.js";
7
+ import "../chunks/chunk-FYQWPEEY.js";
8
+ export {
9
+ bubblesFromGraph
10
+ };
@@ -0,0 +1,15 @@
1
+ import {
2
+ BUBBLE_KIND_COLORS,
3
+ BUBBLE_KIND_NAMES,
4
+ bubbleSegmentIds,
5
+ classifyBubble,
6
+ formatBp
7
+ } from "../chunks/chunk-WOO7QYKB.js";
8
+ import "../chunks/chunk-MFKORG3F.js";
9
+ export {
10
+ BUBBLE_KIND_COLORS,
11
+ BUBBLE_KIND_NAMES,
12
+ bubbleSegmentIds,
13
+ classifyBubble,
14
+ formatBp
15
+ };
@@ -0,0 +1,9 @@
1
+ import {
2
+ bubbleSubgraph
3
+ } from "../chunks/chunk-VHW7FDHU.js";
4
+ import "../chunks/chunk-ST2IBUAN.js";
5
+ import "../chunks/chunk-BPLIXMH7.js";
6
+ import "../chunks/chunk-FYQWPEEY.js";
7
+ export {
8
+ bubbleSubgraph
9
+ };
@@ -0,0 +1,530 @@
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 laidSides = /* @__PURE__ */ new WeakMap();
163
+ function heldFacingSides(fromSegments, toSegments) {
164
+ let byTo = laidSides.get(fromSegments);
165
+ if (!byTo) {
166
+ byTo = /* @__PURE__ */ new WeakMap();
167
+ laidSides.set(fromSegments, byTo);
168
+ }
169
+ let sides = byTo.get(toSegments);
170
+ if (!sides) {
171
+ sides = facingSides(fromSegments, toSegments);
172
+ byTo.set(toSegments, sides);
173
+ }
174
+ return sides;
175
+ }
176
+ var APEX_FRACTION = 0.75;
177
+ var BOW_CLEARANCE = 1.15;
178
+ var ALONG_FRACTION = 0.35;
179
+ var MAX_ROW_BOW_PX = 3 * ROW_HEIGHT_PX;
180
+ function bowAround(p1x, p1y, p2x, p2y, toward, maxBow) {
181
+ const chord = Math.hypot(p2x - p1x, p2y - p1y);
182
+ const ux = chord === 0 ? 1 : (p2x - p1x) / chord;
183
+ const uy = chord === 0 ? 0 : (p2y - p1y) / chord;
184
+ let reach = 0;
185
+ let minAlong = Infinity;
186
+ let maxAlong = -Infinity;
187
+ for (const p of toward) {
188
+ const dx = p.x - p1x;
189
+ const dy = p.y - p1y;
190
+ const perp = dx * -uy + dy * ux;
191
+ if (Math.abs(perp) > Math.abs(reach)) {
192
+ reach = perp;
193
+ }
194
+ const along = dx * ux + dy * uy;
195
+ minAlong = Math.min(minAlong, along);
196
+ maxAlong = Math.max(maxAlong, along);
197
+ }
198
+ const span = toward.length ? maxAlong - minAlong : 0;
199
+ const uncapped = Math.max(
200
+ Math.abs(reach) * BOW_CLEARANCE / APEX_FRACTION,
201
+ span * ALONG_FRACTION
202
+ );
203
+ const size = maxBow === void 0 ? uncapped : Math.min(uncapped, maxBow);
204
+ return reach < 0 ? -size : size;
205
+ }
206
+ function yToXOf({ scaleX, scaleY }) {
207
+ return scaleY / scaleX;
208
+ }
209
+ var PATH_RIBBON_SPACING = 3;
210
+ function drawnDirection(a, b, yToX) {
211
+ const dx = b.x - a.x;
212
+ const dy = (b.y - a.y) * yToX;
213
+ const len = Math.hypot(dx, dy);
214
+ return len === 0 ? void 0 : { x: dx / len, y: dy / len };
215
+ }
216
+ function pathRibbonOffsets(fromSegments, toSegments, count, axis, spacing = PATH_RIBBON_SPACING) {
217
+ const yToX = yToXOf(axis);
218
+ const dir = drawnDirection(
219
+ fromSegments[fromSegments.length - 1],
220
+ toSegments[0],
221
+ yToX
222
+ ) ?? drawnDirection(fromSegments[0], fromSegments.at(-1), yToX) ?? drawnDirection(toSegments[0], toSegments.at(-1), yToX) ?? { x: 1, y: 0 };
223
+ const offsets = [];
224
+ for (let i = 0; i < count; i++) {
225
+ const distance = (i - (count - 1) / 2) * spacing;
226
+ offsets.push({ x: -dir.y * distance, y: dir.x * distance / yToX });
227
+ }
228
+ return offsets;
229
+ }
230
+ function scaleYOf(points, yToX) {
231
+ return points.map((p) => ({ x: p.x, y: p.y * yToX }));
232
+ }
233
+ function unscaleYOf(curves, yToX) {
234
+ for (const c of curves) {
235
+ c.y0 /= yToX;
236
+ c.cy0 /= yToX;
237
+ c.cy1 /= yToX;
238
+ c.y1 /= yToX;
239
+ }
240
+ return curves;
241
+ }
242
+ var SELF_LOOP_MIN_PX = 12;
243
+ var SELF_LOOP_MAX_PX = 40;
244
+ function selfLoopReach(segments, scale) {
245
+ let length = 0;
246
+ for (let i = 1; i < segments.length; i++) {
247
+ length += Math.hypot(
248
+ segments[i].x - segments[i - 1].x,
249
+ segments[i].y - segments[i - 1].y
250
+ );
251
+ }
252
+ const nodePx = length * scale;
253
+ const reachPx = Math.min(
254
+ SELF_LOOP_MAX_PX,
255
+ Math.max(SELF_LOOP_MIN_PX, nodePx / 2)
256
+ );
257
+ return reachPx / Math.max(scale, Number.EPSILON);
258
+ }
259
+ function selfLinkOf(edge) {
260
+ if (edge.from !== edge.to) {
261
+ return false;
262
+ }
263
+ return edge.fromStrand && edge.toStrand && edge.fromStrand !== edge.toStrand ? edge.fromStrand === "+" ? "end" : "start" : true;
264
+ }
265
+ function edgeJoinOf(edge, stranded = false) {
266
+ const self = selfLinkOf(edge);
267
+ if (!stranded) {
268
+ return self;
269
+ }
270
+ const sides = strandSides(edge);
271
+ return self === false ? sides : sides.from === sides.to ? sides.from : true;
272
+ }
273
+ function hairpinCurves(attach, reach, offsetX, offsetY) {
274
+ const { at, inward } = attach;
275
+ const dx = inward ? at.x - inward.x : 1;
276
+ const dy = inward ? at.y - inward.y : 0;
277
+ const len = Math.hypot(dx, dy) || 1;
278
+ const ux = dx / len;
279
+ const uy = dy / len;
280
+ const px = at.x + offsetX;
281
+ const py = at.y + offsetY;
282
+ const nearX = px + ux * reach / 3;
283
+ const nearY = py + uy * reach / 3;
284
+ const apexX = px + ux * reach;
285
+ const apexY = py + uy * reach;
286
+ const sideX = -uy * reach / 2;
287
+ const sideY = ux * reach / 2;
288
+ return [
289
+ {
290
+ x0: px,
291
+ y0: py,
292
+ cx0: nearX,
293
+ cy0: nearY,
294
+ cx1: apexX + sideX,
295
+ cy1: apexY + sideY,
296
+ x1: apexX,
297
+ y1: apexY
298
+ },
299
+ {
300
+ x0: apexX,
301
+ y0: apexY,
302
+ cx0: apexX - sideX,
303
+ cy0: apexY - sideY,
304
+ cx1: nearX,
305
+ cy1: nearY,
306
+ x1: px,
307
+ y1: py
308
+ }
309
+ ];
310
+ }
311
+ function computeEdgeCurves(fromSegments, toSegments, join, offsetX, offsetY, axis, bypassed = [], maxBowIn) {
312
+ const scale = axis.scaleX;
313
+ const yToX = yToXOf(axis);
314
+ const maxBow = maxBowIn ?? (axis.pixelRows ? MAX_ROW_BOW_PX : void 0);
315
+ if (join === false) {
316
+ join = heldFacingSides(fromSegments, toSegments);
317
+ }
318
+ if (yToX !== 1) {
319
+ return unscaleYOf(
320
+ computeEdgeCurves(
321
+ scaleYOf(fromSegments, yToX),
322
+ scaleYOf(toSegments, yToX),
323
+ join,
324
+ offsetX,
325
+ offsetY * yToX,
326
+ { scaleX: scale, scaleY: scale },
327
+ scaleYOf(bypassed, yToX),
328
+ // the cap is a y measure and the body works in x units, so it converts
329
+ // with everything else that crosses this boundary
330
+ maxBow === void 0 ? void 0 : maxBow * yToX
331
+ ),
332
+ yToX
333
+ );
334
+ }
335
+ if (typeof join === "string") {
336
+ return hairpinCurves(
337
+ attachment(fromSegments, join),
338
+ selfLoopReach(fromSegments, scale),
339
+ offsetX,
340
+ offsetY
341
+ );
342
+ }
343
+ const loop = join === true;
344
+ const sides = join === true ? { from: "end", to: "start" } : join;
345
+ const fromAttach = attachment(fromSegments, sides.from);
346
+ const toAttach = attachment(toSegments, sides.to);
347
+ const fromEnd = fromAttach.at;
348
+ const toStart = toAttach.at;
349
+ const p1x = fromEnd.x + offsetX;
350
+ const p1y = fromEnd.y + offsetY;
351
+ const p2x = toStart.x + offsetX;
352
+ const p2y = toStart.y + offsetY;
353
+ if (loop) {
354
+ let segDirX = 1;
355
+ let segDirY = 0;
356
+ if (fromAttach.inward) {
357
+ const dx = fromEnd.x - fromAttach.inward.x;
358
+ const dy = fromEnd.y - fromAttach.inward.y;
359
+ const len = Math.hypot(dx, dy);
360
+ if (len > 0) {
361
+ segDirX = dx / len;
362
+ segDirY = dy / len;
363
+ }
364
+ }
365
+ const ext = selfLoopReach(fromSegments, scale);
366
+ const perpX = -segDirY;
367
+ const perpY = segDirX;
368
+ const midX = (fromEnd.x + toStart.x) / 2 + offsetX + perpX * ext;
369
+ const midY = (fromEnd.y + toStart.y) / 2 + offsetY + perpY * ext;
370
+ const cp1x = p1x + segDirX * ext;
371
+ const cp1y = p1y + segDirY * ext;
372
+ const cp2x = p2x - segDirX * ext;
373
+ const cp2y = p2y - segDirY * ext;
374
+ return [
375
+ {
376
+ x0: p1x,
377
+ y0: p1y,
378
+ cx0: cp1x,
379
+ cy0: cp1y,
380
+ cx1: cp1x + perpX * ext,
381
+ cy1: cp1y + perpY * ext,
382
+ x1: midX,
383
+ y1: midY
384
+ },
385
+ {
386
+ x0: midX,
387
+ y0: midY,
388
+ cx0: cp2x + perpX * ext,
389
+ cy0: cp2y + perpY * ext,
390
+ cx1: cp2x,
391
+ cy1: cp2y,
392
+ x1: p2x,
393
+ y1: p2y
394
+ }
395
+ ];
396
+ } else {
397
+ const fromPrev = fromAttach.inward ?? {
398
+ x: fromEnd.x - (toStart.x - fromEnd.x),
399
+ y: fromEnd.y - (toStart.y - fromEnd.y)
400
+ };
401
+ const toNext = toAttach.inward ?? {
402
+ x: toStart.x - (fromEnd.x - toStart.x),
403
+ y: toStart.y - (fromEnd.y - toStart.y)
404
+ };
405
+ const dist = Math.hypot(p2x - p1x, p2y - p1y);
406
+ const tangentProj = (prev, at, towardX, towardY) => {
407
+ const dx = at.x - prev.x;
408
+ const dy = at.y - prev.y;
409
+ const len = Math.hypot(dx, dy);
410
+ const along = len === 0 ? 0 : (towardX * dx + towardY * dy) / len;
411
+ return Math.max(
412
+ 0,
413
+ Math.min(dist * TANGENT_SPAN_FACTOR, MAX_TANGENT, along * 0.5)
414
+ );
415
+ };
416
+ const towardToX = toStart.x - fromEnd.x;
417
+ const towardToY = toStart.y - fromEnd.y;
418
+ const [cx1, cy1] = projectLine(
419
+ fromPrev.x,
420
+ fromPrev.y,
421
+ fromEnd.x,
422
+ fromEnd.y,
423
+ tangentProj(fromPrev, fromEnd, towardToX, towardToY)
424
+ );
425
+ const [cx2, cy2] = projectLine(
426
+ toNext.x,
427
+ toNext.y,
428
+ toStart.x,
429
+ toStart.y,
430
+ tangentProj(toNext, toStart, -towardToX, -towardToY)
431
+ );
432
+ const bulge = bypassed.length ? bowAround(p1x, p1y, p2x, p2y, bypassed, maxBow) : 0;
433
+ const chordLen = dist === 0 ? 1 : dist;
434
+ const ux = (p2x - p1x) / chordLen;
435
+ const uy = (p2y - p1y) / chordLen;
436
+ const bulgeX = -uy * bulge;
437
+ const bulgeY = ux * bulge;
438
+ const spreadX = ux * Math.abs(bulge);
439
+ const spreadY = uy * Math.abs(bulge);
440
+ return [
441
+ {
442
+ x0: p1x,
443
+ y0: p1y,
444
+ cx0: cx1 + offsetX + bulgeX - spreadX,
445
+ cy0: cy1 + offsetY + bulgeY - spreadY,
446
+ cx1: cx2 + offsetX + bulgeX + spreadX,
447
+ cy1: cy2 + offsetY + bulgeY + spreadY,
448
+ x1: p2x,
449
+ y1: p2y
450
+ }
451
+ ];
452
+ }
453
+ }
454
+ function nearerEnd(segments, to) {
455
+ const start = segments[0];
456
+ const end = segments.at(-1);
457
+ return Math.hypot(start.x - to.x, start.y - to.y) < Math.hypot(end.x - to.x, end.y - to.y) ? "start" : "end";
458
+ }
459
+ function routedEdgeCurves(fromSegments, toSegments, route, sides) {
460
+ const fromAttach = attachment(
461
+ fromSegments,
462
+ sides?.from ?? nearerEnd(fromSegments, route[0])
463
+ );
464
+ const toAttach = attachment(
465
+ toSegments,
466
+ sides?.to ?? nearerEnd(toSegments, route.at(-1))
467
+ );
468
+ const points = [fromAttach.at, ...route, toAttach.at];
469
+ const tangent = (i) => {
470
+ const p = points[i];
471
+ const span = (q) => Math.hypot(q.x - p.x, q.y - p.y);
472
+ const along = (inward, next) => {
473
+ const len = inward ? span(inward) : 0;
474
+ const reach = span(next);
475
+ return len === 0 ? { x: next.x - p.x, y: next.y - p.y } : {
476
+ x: (p.x - inward.x) / len * reach,
477
+ y: (p.y - inward.y) / len * reach
478
+ };
479
+ };
480
+ if (i === 0) {
481
+ return along(fromAttach.inward, points[1]);
482
+ }
483
+ if (i === points.length - 1) {
484
+ const out = along(toAttach.inward, points[i - 1]);
485
+ return { x: -out.x, y: -out.y };
486
+ }
487
+ return {
488
+ x: (points[i + 1].x - points[i - 1].x) / 2,
489
+ y: (points[i + 1].y - points[i - 1].y) / 2
490
+ };
491
+ };
492
+ const curves = [];
493
+ for (let i = 0; i < points.length - 1; i++) {
494
+ const a = points[i];
495
+ const b = points[i + 1];
496
+ const ta = tangent(i);
497
+ const tb = tangent(i + 1);
498
+ curves.push({
499
+ x0: a.x,
500
+ y0: a.y,
501
+ cx0: a.x + ta.x / 3,
502
+ cy0: a.y + ta.y / 3,
503
+ cx1: b.x - tb.x / 3,
504
+ cy1: b.y - tb.y / 3,
505
+ x1: b.x,
506
+ y1: b.y
507
+ });
508
+ }
509
+ return curves;
510
+ }
511
+
512
+ export {
513
+ svgPath,
514
+ projectLine,
515
+ curvePointAt,
516
+ curveMidpoint,
517
+ curveBounds,
518
+ dashCurves,
519
+ translateCurves,
520
+ ownStrand,
521
+ strandSides,
522
+ bowAround,
523
+ yToXOf,
524
+ PATH_RIBBON_SPACING,
525
+ pathRibbonOffsets,
526
+ selfLinkOf,
527
+ edgeJoinOf,
528
+ computeEdgeCurves,
529
+ routedEdgeCurves
530
+ };