@jbrowse/bandage-core 11.7.3 → 11.8.0
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.
- package/README.md +59 -19
- package/dist/bubbles/bubbleHalos.js +6 -6
- package/dist/bubbles/classifyBubble.js +5 -5
- package/dist/bubbles/superbubbles.js +7 -7
- package/dist/chunks/{chunk-ZZZ6NVLW.js → chunk-22ZMVREZ.js} +1 -1
- package/dist/chunks/{chunk-VXKL2ZLR.js → chunk-2H3ALI6L.js} +223 -45
- package/dist/chunks/{chunk-F4CDC72I.js → chunk-2RCKHCF7.js} +1 -1
- package/dist/chunks/{chunk-P276TWPM.js → chunk-3CXZUC2Y.js} +1 -1
- package/dist/chunks/{chunk-VNF236GG.js → chunk-3XYLEK4H.js} +2 -2
- package/dist/chunks/{chunk-PKGPSTRW.js → chunk-4OVIUCSB.js} +2 -2
- package/dist/chunks/{chunk-BLDUBB7X.js → chunk-4YPCUVMC.js} +2 -2
- package/dist/chunks/{chunk-N73RA4DR.js → chunk-6CXLLJ2C.js} +3 -3
- package/dist/chunks/{chunk-4SN7R4UF.js → chunk-6MD4VS2F.js} +1 -1
- package/dist/chunks/{chunk-VACK24HF.js → chunk-735MPLUL.js} +35 -9
- package/dist/chunks/chunk-7GDNDQD7.js +504 -0
- package/dist/chunks/{chunk-2US43KNG.js → chunk-7PEYA3NA.js} +2 -2
- package/dist/chunks/{chunk-XTCIKBWM.js → chunk-AXE3RR7J.js} +1 -1
- package/dist/chunks/{chunk-BXE3SBBN.js → chunk-C3KAPREU.js} +1 -1
- package/dist/chunks/{chunk-QJC63NFR.js → chunk-G3GLGDKV.js} +2 -2
- package/dist/chunks/{chunk-63XWYNID.js → chunk-G4WQBRDS.js} +4 -4
- package/dist/chunks/{chunk-SDI6BVG7.js → chunk-J4PDHWP2.js} +1 -1
- package/dist/chunks/{chunk-GD6RUZU5.js → chunk-JKIAHYAW.js} +14 -8
- package/dist/chunks/{chunk-AJQRKN3X.js → chunk-L2RQV23D.js} +1 -1
- package/dist/chunks/{chunk-EPOPGV5G.js → chunk-LA6JHEVV.js} +1 -1
- package/dist/chunks/{chunk-G7SG4XMK.js → chunk-LODKC7PJ.js} +7 -0
- package/dist/chunks/{chunk-QXBQDHKL.js → chunk-MO4DIRHD.js} +80 -38
- package/dist/chunks/{chunk-IHM43Y7B.js → chunk-N2GAA7RB.js} +1 -1
- package/dist/chunks/{chunk-6RB5ZQ34.js → chunk-N4AJV4GH.js} +25 -14
- package/dist/chunks/{chunk-LH436HS2.js → chunk-NHOXLUIL.js} +2 -2
- package/dist/chunks/{chunk-QLLKALSH.js → chunk-NQWEYJDD.js} +1 -1
- package/dist/chunks/{chunk-AWJJSA7D.js → chunk-OIOAK7BS.js} +1 -1
- package/dist/chunks/{chunk-UTDMFVC7.js → chunk-R3NPBUKT.js} +8 -8
- package/dist/chunks/{chunk-4KOYS5WR.js → chunk-R4XCTRIU.js} +1 -1
- package/dist/chunks/{chunk-XKPXOIWD.js → chunk-S3ICPPXC.js} +3 -3
- package/dist/chunks/{chunk-K4KVAMEF.js → chunk-S4CH434Z.js} +1 -1
- package/dist/chunks/{chunk-HMEAGAGC.js → chunk-S7BCZVRK.js} +2 -2
- package/dist/chunks/{chunk-SG7VQB4F.js → chunk-SDDMV2MM.js} +1 -1
- package/dist/chunks/{chunk-KMLATBNT.js → chunk-SIXFXXRI.js} +3 -3
- package/dist/chunks/{chunk-T3MEG5D3.js → chunk-U6JPSXJ3.js} +90 -21
- package/dist/chunks/{chunk-SENPGW4O.js → chunk-VCUOYVLC.js} +1 -1
- package/dist/chunks/{chunk-33KUO34R.js → chunk-VSRRIFRF.js} +1 -1
- package/dist/chunks/{chunk-BFUS3ENH.js → chunk-X5ULFAMM.js} +2 -2
- package/dist/chunks/{chunk-W5YRBEWQ.js → chunk-XOIYBEU7.js} +1 -1
- package/dist/chunks/{chunk-CL6ELLVF.js → chunk-YEDZ37PH.js} +1 -1
- package/dist/chunks/{chunk-Z4HXW6ZH.js → chunk-YF6T33PU.js} +10 -2
- package/dist/chunks/{chunk-2VMKQKVO.js → chunk-YIWN32ZX.js} +1 -1
- package/dist/chunks/{chunk-2T3V6Z3M.js → chunk-ZKIH2USC.js} +1 -1
- package/dist/cli/figure.js +31 -30
- package/dist/cli/fold.js +6 -6
- package/dist/deletionEdges.js +2 -2
- package/dist/figure.js +27 -26
- package/dist/figureData.js +25 -24
- package/dist/figureScene.js +18 -17
- package/dist/figureSettings.js +6 -6
- package/dist/foldVariants.js +2 -2
- package/dist/genes/genePins.js +3 -3
- package/dist/graphLabels.js +4 -4
- package/dist/index.js +45 -44
- package/dist/labelLayout.js +8 -8
- package/dist/layout/forceCache.js +6 -6
- package/dist/layout/mergeRuns.js +2 -2
- package/dist/layout/orientToReference.js +2 -2
- package/dist/layout/tubeMapLayout.js +9 -9
- package/dist/layout/walkRowDraw.js +9 -8
- package/dist/layout/walkRowLayout.js +3 -3
- package/dist/layout/walkRowRuns.js +1 -1
- package/dist/layout/walkRows.js +2 -2
- package/dist/layout/walkStrip.js +13 -12
- package/dist/layoutModes.js +11 -11
- package/dist/pathColors.js +5 -5
- package/dist/pipeline.js +5 -5
- package/dist/referenceStrip.js +9 -8
- package/dist/renderer/GeometryBuilder.js +8 -7
- package/dist/renderer/walkLanes.js +44 -0
- package/dist/tubeMap/axis.js +5 -5
- package/dist/tubeMap/bundle.js +10 -10
- package/dist/tubeMap/coarsen.js +10 -10
- package/dist/tubeMap/deviations.js +11 -11
- package/dist/tubeMap/draw.js +5 -5
- package/dist/tubeMap/genes.js +6 -6
- package/dist/tubeMap/nodeColors.js +9 -8
- package/dist/tubeMap/reads.js +2 -2
- package/dist/types/figureData.d.ts +22 -0
- package/dist/types/renderer/types.d.ts +20 -0
- package/dist/types/renderer/walkLanes.d.ts +60 -0
- package/dist/types/util/geometry.d.ts +13 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/walkEncoding.d.ts +7 -5
- package/dist/types/walkHighlight.d.ts +3 -0
- package/dist/util/SpatialIndex.js +4 -4
- package/dist/util/edgeCurves.js +3 -3
- package/dist/util/geometry.js +7 -1
- package/dist/util/hitDetection.js +5 -5
- package/dist/version.js +1 -1
- package/dist/viewport.js +6 -6
- package/dist/walkEncoding.js +9 -8
- package/dist/walkHighlight.js +10 -9
- package/dist/walkKey.js +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ npm install @jbrowse/bandage-core
|
|
|
43
43
|
backbone lies on. `featuresOnBackbone` renames that assembly's genes onto the
|
|
44
44
|
backbone's refNames, and `genePins` pins only those
|
|
45
45
|
|
|
46
|
-
## Figures
|
|
46
|
+
## Figures
|
|
47
47
|
|
|
48
48
|
- `figureSvg` draws a graph and its lifted walks to a standalone SVG. The
|
|
49
49
|
`bandage-figure` CLI makes one from a JSON spec with no browser:
|
|
@@ -52,9 +52,43 @@ npm install @jbrowse/bandage-core
|
|
|
52
52
|
- `figureData` returns what `figureSvg` would draw as columnar tables, for
|
|
53
53
|
another graphics system to draw;
|
|
54
54
|
[ggbandage](https://github.com/GMOD/ggbandage) draws them in ggplot2
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
|
|
56
|
+
## Coarse tier: `bandage-fold`
|
|
57
|
+
|
|
58
|
+
`bandage-fold` writes an rGFA with every variant under `--below` bp folded into
|
|
59
|
+
the reference. Indexed by gfa-to-tabix, the output is the coarse tier a graph
|
|
60
|
+
track draws once zoomed out past the fine index, so a whole chromosome fits.
|
|
61
|
+
[`gfa-to-tabix fold`](https://github.com/GMOD/gfa-to-tabix/blob/main/docs/subcommands.md#fold)
|
|
62
|
+
documents the fold and the handover from the fine index.
|
|
63
|
+
|
|
64
|
+
```console
|
|
65
|
+
npx -p @jbrowse/bandage-core bandage-fold graph.rgfa.gz --below 10000 \
|
|
66
|
+
| gfa-to-tabix - -o graph.fold10000
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- The input is a GFA or rGFA, plain or gzipped, or `-` for stdin. `-o out.gfa`
|
|
70
|
+
writes a file in place of stdout
|
|
71
|
+
- `--reference PATH` names a plain GFA's backbone path. In an rGFA, the segments
|
|
72
|
+
tagged `SR:i:0` form the backbone
|
|
73
|
+
- The fold keeps the backbone, every allele whose own length or the reference it
|
|
74
|
+
replaces reaches `--below`, and the shortest way from each one's ends back to
|
|
75
|
+
the backbone. It joins the reference between kept alleles into one segment
|
|
76
|
+
- `bandage-fold` drops paths and walks from the output. It writes forward a
|
|
77
|
+
segment that a plain GFA's path reads reversed, and flips its links to match,
|
|
78
|
+
so the output re-reads as the graph that was folded
|
|
79
|
+
- gfa-to-tabix's `--layout anchored|contig` should match the fine index's. An
|
|
80
|
+
anchored window inside a large bubble then returns the whole bubble in both
|
|
81
|
+
tiers, and a contig one only what links reach from it
|
|
82
|
+
- The graph track folds each cut it draws at ten pixels (`FOLD_PX` in
|
|
83
|
+
jbrowse-plugin-graphgenomeviewer), and folding a fold again at a larger size
|
|
84
|
+
closely matches folding the original at that size. A tier folded at N and
|
|
85
|
+
handed over at N / 10 bp per pixel, such as
|
|
86
|
+
`coarse: { uri: "graph.fold10000", aboveBpPerPx: 1000 }`, therefore draws
|
|
87
|
+
nearly what the fine cut drew
|
|
88
|
+
- Node caps a string at 512 MB, and `bandage-fold` reads the whole graph into
|
|
89
|
+
one, so it cannot fold a whole human pangenome. `gfa-to-tabix fold`, a port of
|
|
90
|
+
the same fold, writes the index in one pass:
|
|
91
|
+
`gfa-to-tabix fold graph.rgfa.gz --below 10000 -o graph.fold10000`
|
|
58
92
|
|
|
59
93
|
## Developing
|
|
60
94
|
|
|
@@ -64,21 +98,27 @@ pnpm test # unit tests
|
|
|
64
98
|
pnpm build # dist/ with declarations
|
|
65
99
|
```
|
|
66
100
|
|
|
67
|
-
The FMMM engine and its wasm build are in
|
|
68
|
-
[graph-fmmm-layout](https://github.com/GMOD/graph-fmmm-layout)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
[
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
101
|
+
- The FMMM engine and its wasm build are in
|
|
102
|
+
[graph-fmmm-layout](https://github.com/GMOD/graph-fmmm-layout)
|
|
103
|
+
- CI also packs the core and runs the typecheck and tests of
|
|
104
|
+
[BandageJS](https://github.com/cmdcolin/BandageJS) and
|
|
105
|
+
[jbrowse-plugin-graphgenomeviewer](https://github.com/GMOD/jbrowse-plugin-graphgenomeviewer)
|
|
106
|
+
against it. For an API change, push the consumer's matching change to a
|
|
107
|
+
`core-next` branch in that consumer's repo, which CI tests in place of `main`
|
|
108
|
+
until the release
|
|
109
|
+
- `src/foldParity.test.ts` folds a few hundred random rGFA graphs with
|
|
110
|
+
`foldVariants` and with the gfa-to-tabix release that
|
|
111
|
+
`.github/actions/gfa-to-tabix` pins, and fails when they fold differently.
|
|
112
|
+
Locally the test runs the `gfa-to-tabix` on PATH or named by `GFA_TO_TABIX`,
|
|
113
|
+
and skips without one. In the other direction, gfa-to-tabix's
|
|
114
|
+
`scripts/parity-fold.sh` checks that `gfa-to-tabix fold` writes the same bytes
|
|
115
|
+
as `bandage-fold | gfa-to-tabix -`, and its CI tests the port against a pinned
|
|
116
|
+
bandage-core
|
|
117
|
+
- Release with `pnpm version patch`: it lints, tests, stamps `src/version.ts`,
|
|
118
|
+
writes the changelog with git-cliff and pushes the `v*` tag. `publish.yml`
|
|
119
|
+
then runs CI, consumers included, and publishes to npm with trusted publishing
|
|
120
|
+
only if it passes. When a consumer fails, push its side to `core-next` and
|
|
121
|
+
rerun the failed jobs
|
|
82
122
|
|
|
83
123
|
## See also
|
|
84
124
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bubbleHalos,
|
|
3
3
|
coversGraph
|
|
4
|
-
} from "../chunks/chunk-
|
|
5
|
-
import "../chunks/chunk-
|
|
4
|
+
} from "../chunks/chunk-4YPCUVMC.js";
|
|
5
|
+
import "../chunks/chunk-OIOAK7BS.js";
|
|
6
6
|
import "../chunks/chunk-FX4RDMDN.js";
|
|
7
|
-
import "../chunks/chunk-
|
|
7
|
+
import "../chunks/chunk-6CXLLJ2C.js";
|
|
8
8
|
import "../chunks/chunk-HMXCN6PG.js";
|
|
9
9
|
import "../chunks/chunk-AB35TJST.js";
|
|
10
|
-
import "../chunks/chunk-
|
|
11
|
-
import "../chunks/chunk-
|
|
10
|
+
import "../chunks/chunk-YIWN32ZX.js";
|
|
11
|
+
import "../chunks/chunk-3CXZUC2Y.js";
|
|
12
12
|
import "../chunks/chunk-TLWXLNLY.js";
|
|
13
13
|
import "../chunks/chunk-3YBP46PB.js";
|
|
14
|
-
import "../chunks/chunk-
|
|
14
|
+
import "../chunks/chunk-735MPLUL.js";
|
|
15
15
|
import "../chunks/chunk-CPMHA4W5.js";
|
|
16
16
|
import "../chunks/chunk-ZPZ7GNBT.js";
|
|
17
17
|
import "../chunks/chunk-BSOZW3FC.js";
|
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
BUBBLE_KIND_NAMES,
|
|
4
4
|
bubbleSegmentIds,
|
|
5
5
|
classifyBubble
|
|
6
|
-
} from "../chunks/chunk-
|
|
6
|
+
} from "../chunks/chunk-OIOAK7BS.js";
|
|
7
7
|
import "../chunks/chunk-FX4RDMDN.js";
|
|
8
|
-
import "../chunks/chunk-
|
|
8
|
+
import "../chunks/chunk-6CXLLJ2C.js";
|
|
9
9
|
import "../chunks/chunk-HMXCN6PG.js";
|
|
10
10
|
import "../chunks/chunk-AB35TJST.js";
|
|
11
|
-
import "../chunks/chunk-
|
|
12
|
-
import "../chunks/chunk-
|
|
11
|
+
import "../chunks/chunk-YIWN32ZX.js";
|
|
12
|
+
import "../chunks/chunk-3CXZUC2Y.js";
|
|
13
13
|
import "../chunks/chunk-TLWXLNLY.js";
|
|
14
14
|
import "../chunks/chunk-3YBP46PB.js";
|
|
15
|
-
import "../chunks/chunk-
|
|
15
|
+
import "../chunks/chunk-735MPLUL.js";
|
|
16
16
|
import "../chunks/chunk-CPMHA4W5.js";
|
|
17
17
|
import "../chunks/chunk-ZPZ7GNBT.js";
|
|
18
18
|
import "../chunks/chunk-BSOZW3FC.js";
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
graphBubbles,
|
|
3
3
|
superbubblesFromGraph
|
|
4
|
-
} from "../chunks/chunk-
|
|
5
|
-
import "../chunks/chunk-
|
|
4
|
+
} from "../chunks/chunk-SIXFXXRI.js";
|
|
5
|
+
import "../chunks/chunk-4YPCUVMC.js";
|
|
6
6
|
import "../chunks/chunk-RFG42VIO.js";
|
|
7
|
-
import "../chunks/chunk-
|
|
7
|
+
import "../chunks/chunk-OIOAK7BS.js";
|
|
8
8
|
import "../chunks/chunk-FX4RDMDN.js";
|
|
9
9
|
import "../chunks/chunk-MHZTTLBU.js";
|
|
10
10
|
import "../chunks/chunk-UAFATXYA.js";
|
|
11
11
|
import "../chunks/chunk-VX656OAZ.js";
|
|
12
|
-
import "../chunks/chunk-
|
|
12
|
+
import "../chunks/chunk-6CXLLJ2C.js";
|
|
13
13
|
import "../chunks/chunk-HMXCN6PG.js";
|
|
14
14
|
import "../chunks/chunk-AB35TJST.js";
|
|
15
|
-
import "../chunks/chunk-
|
|
16
|
-
import "../chunks/chunk-
|
|
15
|
+
import "../chunks/chunk-YIWN32ZX.js";
|
|
16
|
+
import "../chunks/chunk-3CXZUC2Y.js";
|
|
17
17
|
import "../chunks/chunk-TLWXLNLY.js";
|
|
18
18
|
import "../chunks/chunk-3YBP46PB.js";
|
|
19
|
-
import "../chunks/chunk-
|
|
19
|
+
import "../chunks/chunk-735MPLUL.js";
|
|
20
20
|
import "../chunks/chunk-CPMHA4W5.js";
|
|
21
21
|
import "../chunks/chunk-ZPZ7GNBT.js";
|
|
22
22
|
import "../chunks/chunk-BSOZW3FC.js";
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LANE_SMOOTHING_PX,
|
|
3
|
+
THREAD_PITCH_PX,
|
|
4
|
+
THREAD_PX,
|
|
5
|
+
THREAD_REACH_PX,
|
|
6
|
+
THREAD_TAPER_PX,
|
|
7
|
+
bandScale,
|
|
8
|
+
bandSpans,
|
|
9
|
+
bandWidthPx,
|
|
10
|
+
laneLines,
|
|
11
|
+
laneRuns,
|
|
12
|
+
laneSlideAt,
|
|
13
|
+
slotCentres,
|
|
14
|
+
threadsPasses,
|
|
15
|
+
walkThreads
|
|
16
|
+
} from "./chunk-7GDNDQD7.js";
|
|
1
17
|
import {
|
|
2
18
|
abgrToCssRgba,
|
|
3
19
|
fadeAbgr,
|
|
@@ -10,13 +26,13 @@ import {
|
|
|
10
26
|
} from "./chunk-GQZVR5DW.js";
|
|
11
27
|
import {
|
|
12
28
|
baseEdgeCurves
|
|
13
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-VSRRIFRF.js";
|
|
14
30
|
import {
|
|
15
31
|
PATH_LIGHTNESS,
|
|
16
32
|
PATH_SATURATION,
|
|
17
33
|
nameHue,
|
|
18
34
|
pathHueAt
|
|
19
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-JKIAHYAW.js";
|
|
20
36
|
import {
|
|
21
37
|
meanDepth,
|
|
22
38
|
nodeWidthPx,
|
|
@@ -26,13 +42,14 @@ import {
|
|
|
26
42
|
referenceMidpoints
|
|
27
43
|
} from "./chunk-VX656OAZ.js";
|
|
28
44
|
import {
|
|
29
|
-
|
|
45
|
+
dashSpans,
|
|
30
46
|
pathRibbonOffsets,
|
|
31
47
|
polylinePx,
|
|
48
|
+
slideCurves,
|
|
32
49
|
strandSides,
|
|
33
50
|
translateCurves,
|
|
34
51
|
yToXOf
|
|
35
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-735MPLUL.js";
|
|
36
53
|
import {
|
|
37
54
|
pushTo
|
|
38
55
|
} from "./chunk-BSOZW3FC.js";
|
|
@@ -149,13 +166,16 @@ function arrowheadFor({
|
|
|
149
166
|
color
|
|
150
167
|
};
|
|
151
168
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const gap = (p) => Math.hypot(p.x -
|
|
169
|
+
var FIRST_THREADS = [[0, 0]];
|
|
170
|
+
var ONE_PASS = [0];
|
|
171
|
+
var NO_SHIFT = { dx: 0, dy: 0 };
|
|
172
|
+
function nearerStart(at, segments, yToX) {
|
|
173
|
+
const gap = (p) => Math.hypot(p.x - at.x, (p.y - at.y) * yToX);
|
|
157
174
|
return gap(segments[0]) < gap(segments.at(-1));
|
|
158
175
|
}
|
|
176
|
+
function leavesStart(edge, segments, first, stranded, yToX) {
|
|
177
|
+
return stranded ? strandSides(edge).from === "start" : nearerStart({ x: first.x0, y: first.y0 }, segments, yToX);
|
|
178
|
+
}
|
|
159
179
|
function intoDirection(last, toSegments) {
|
|
160
180
|
if (toSegments.length < 2) {
|
|
161
181
|
return void 0;
|
|
@@ -496,6 +516,74 @@ function buildGeometry(options) {
|
|
|
496
516
|
const { pathColorByIndex, pathIndexByName } = pathPaletteOf(graph);
|
|
497
517
|
const pathColorByName = (name) => pathColorByIndex[pathIndexByName.get(name) ?? -1] ?? EDGE_PATH_FALLBACK_COLOR;
|
|
498
518
|
const nodePathSlots = drawPaths && pathCount > 1 ? pathSlotsOf(graph) : NO_SLOTS;
|
|
519
|
+
const liftedWalks = highlight?.walks ?? [];
|
|
520
|
+
const threaded = threadsPasses(liftedWalks);
|
|
521
|
+
const lanePasses = liftedWalks.map((w) => w.mostPasses);
|
|
522
|
+
const thinned = threaded ? bandScale(lanePasses) : 1;
|
|
523
|
+
const runs = liftedWalks.length > 1 ? laneRuns(
|
|
524
|
+
liftedWalks.flatMap((w) => w.route),
|
|
525
|
+
nodePositions,
|
|
526
|
+
yToX,
|
|
527
|
+
LANE_SMOOTHING_PX * worldPerScreenPx
|
|
528
|
+
) : void 0;
|
|
529
|
+
const lines = runs ? laneLines(
|
|
530
|
+
runs,
|
|
531
|
+
nodePositions,
|
|
532
|
+
yToX,
|
|
533
|
+
LANE_SMOOTHING_PX * worldPerScreenPx,
|
|
534
|
+
bandWidthPx(lanePasses) * thinned * worldPerScreenPx,
|
|
535
|
+
threaded
|
|
536
|
+
) : void 0;
|
|
537
|
+
const threads = threaded ? liftedWalks.map(
|
|
538
|
+
(w) => walkThreads(
|
|
539
|
+
w.route,
|
|
540
|
+
w.passes,
|
|
541
|
+
nodePositions,
|
|
542
|
+
yToX,
|
|
543
|
+
THREAD_REACH_PX * worldPerScreenPx
|
|
544
|
+
)
|
|
545
|
+
) : void 0;
|
|
546
|
+
const spanField = runs && threads ? bandSpans(
|
|
547
|
+
runs,
|
|
548
|
+
threads.map((t) => t.span),
|
|
549
|
+
{
|
|
550
|
+
reach: THREAD_REACH_PX * worldPerScreenPx,
|
|
551
|
+
taper: THREAD_TAPER_PX * worldPerScreenPx
|
|
552
|
+
}
|
|
553
|
+
) : void 0;
|
|
554
|
+
const edgeKeys = new Set(
|
|
555
|
+
threads ? graph.edges.map((e) => `${e.from}>${e.to}`) : []
|
|
556
|
+
);
|
|
557
|
+
const centresByNode = /* @__PURE__ */ new Map();
|
|
558
|
+
const laneOffset = (nodeId, laneWidth, lane, pass) => {
|
|
559
|
+
if (!threaded) {
|
|
560
|
+
return (lane - (liftedWalks.length - 1) / 2) * laneWidth;
|
|
561
|
+
}
|
|
562
|
+
let centres = centresByNode.get(nodeId);
|
|
563
|
+
if (!centres) {
|
|
564
|
+
const spans = spanField?.get(nodeId);
|
|
565
|
+
centres = slotCentres(spans?.slot ?? lanePasses, lanePasses, spans?.held);
|
|
566
|
+
centresByNode.set(nodeId, centres);
|
|
567
|
+
}
|
|
568
|
+
return (centres[lane] + pass * THREAD_PITCH_PX) * thinned;
|
|
569
|
+
};
|
|
570
|
+
const laneHalfWidth = (laneWidth) => threaded ? THREAD_PX / 2 * thinned : laneWidth / 2;
|
|
571
|
+
const laneNormals = /* @__PURE__ */ new Map();
|
|
572
|
+
const laneNormalsOf = (nodeId, segments) => {
|
|
573
|
+
let normals = laneNormals.get(nodeId);
|
|
574
|
+
if (!normals) {
|
|
575
|
+
const smoothed = lines?.normals.get(nodeId);
|
|
576
|
+
normals = smoothed ? pointNormalsOf(segments, yToX).map((n, i) => smoothed[i] ?? n) : pointNormalsOf(segments, yToX);
|
|
577
|
+
laneNormals.set(nodeId, normals);
|
|
578
|
+
}
|
|
579
|
+
return normals;
|
|
580
|
+
};
|
|
581
|
+
const laneWidths = /* @__PURE__ */ new Map();
|
|
582
|
+
const laneShiftOf = (nodeId, index) => lines?.shifts.get(nodeId)?.[index] ?? NO_SHIFT;
|
|
583
|
+
const lanePointsOf = (nodeId, segments) => lines?.shifts.has(nodeId) ? segments.map((p, i) => {
|
|
584
|
+
const { dx, dy } = laneShiftOf(nodeId, i);
|
|
585
|
+
return { ...p, x: p.x + dx, y: p.y + dy };
|
|
586
|
+
}) : segments;
|
|
499
587
|
const showArrows = scale > (linearLayout ? 1 : MIN_ARROW_SCALE);
|
|
500
588
|
for (let ei = 0; ei < graph.edges.length; ei++) {
|
|
501
589
|
const edge = graph.edges[ei];
|
|
@@ -516,18 +604,37 @@ function buildGeometry(options) {
|
|
|
516
604
|
const toNode = nodeById.get(edge.to);
|
|
517
605
|
const intoHalfWidth = (toNode ? nodeWidthPx(toNode, contigThickness, nodeWidth, depthNorm) : contigThickness) / 2;
|
|
518
606
|
const inView = (curves) => !viewportBounds || isBezierInBounds(curves, viewportBounds);
|
|
519
|
-
const stroke = (curves, thickness, color) => {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
curves,
|
|
523
|
-
|
|
524
|
-
yToX,
|
|
525
|
-
viewportBounds
|
|
526
|
-
)) {
|
|
527
|
-
edgeCurves.push({ curves: dash, thickness, color });
|
|
607
|
+
const stroke = (curves, thickness, color, lane) => {
|
|
608
|
+
const push = (pieces, from = 0, to = 1) => {
|
|
609
|
+
if (!lane) {
|
|
610
|
+
edgeCurves.push({ curves: pieces, thickness, color });
|
|
611
|
+
return;
|
|
528
612
|
}
|
|
529
|
-
|
|
530
|
-
|
|
613
|
+
const start = from === 0 ? lane.start : laneSlideAt(lane.start, lane.end, from);
|
|
614
|
+
const end = to === 1 ? lane.end : laneSlideAt(lane.start, lane.end, to);
|
|
615
|
+
edgeCurves.push({
|
|
616
|
+
curves: pieces,
|
|
617
|
+
thickness,
|
|
618
|
+
color,
|
|
619
|
+
walk: lane.walk,
|
|
620
|
+
pass: lane.pass,
|
|
621
|
+
normal: start.normal,
|
|
622
|
+
offsetX: start.offsetX,
|
|
623
|
+
endNormal: end.normal,
|
|
624
|
+
endOffsetX: end.offsetX
|
|
625
|
+
});
|
|
626
|
+
};
|
|
627
|
+
if (!isDeletion) {
|
|
628
|
+
push(curves);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
for (const dash of dashSpans(
|
|
632
|
+
curves,
|
|
633
|
+
DELETION_DASH_PX / scale,
|
|
634
|
+
yToX,
|
|
635
|
+
viewportBounds
|
|
636
|
+
)) {
|
|
637
|
+
push(dash.curves, dash.startAlong, dash.endAlong);
|
|
531
638
|
}
|
|
532
639
|
};
|
|
533
640
|
const arrowOn = (curves) => {
|
|
@@ -549,32 +656,86 @@ function buildGeometry(options) {
|
|
|
549
656
|
const arrowStart = arrows.length;
|
|
550
657
|
const laneWalks = highlight?.walks.length && onWalk && !ribbons ? highlight.walks : void 0;
|
|
551
658
|
if (laneWalks) {
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
659
|
+
const laneWidthOf = (id) => {
|
|
660
|
+
let width = laneWidths.get(id);
|
|
661
|
+
if (width === void 0) {
|
|
662
|
+
const node = nodeById.get(id);
|
|
663
|
+
width = walkLaneWidthPx(
|
|
664
|
+
node ? nodeWidthPx(node, contigThickness, nodeWidth, depthNorm) : contigThickness,
|
|
665
|
+
laneWalks.length
|
|
666
|
+
);
|
|
667
|
+
laneWidths.set(id, width);
|
|
668
|
+
}
|
|
669
|
+
return width;
|
|
670
|
+
};
|
|
671
|
+
const endOf = (id, segments, atStart) => {
|
|
672
|
+
const at = atStart ? 0 : segments.length - 1;
|
|
673
|
+
return {
|
|
674
|
+
id,
|
|
675
|
+
laneWidth: laneWidthOf(id),
|
|
676
|
+
normal: laneNormalsOf(id, segments)[at] ?? { nx: 0, ny: 0 },
|
|
677
|
+
shift: laneShiftOf(id, at)
|
|
678
|
+
};
|
|
679
|
+
};
|
|
680
|
+
const from = endOf(
|
|
681
|
+
edge.from,
|
|
558
682
|
fromSegments,
|
|
559
|
-
baseCurves[0],
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
683
|
+
leavesStart(edge, fromSegments, baseCurves[0], stranded, yToX)
|
|
684
|
+
);
|
|
685
|
+
const last = baseCurves.at(-1);
|
|
686
|
+
const to = threaded ? endOf(
|
|
687
|
+
edge.to,
|
|
688
|
+
toSegments,
|
|
689
|
+
nearerStart({ x: last.x1, y: last.y1 }, toSegments, yToX)
|
|
690
|
+
) : from;
|
|
691
|
+
const slideOf = (end, offsetX) => ({
|
|
692
|
+
dx: end.shift.dx + end.normal.nx * offsetX,
|
|
693
|
+
dy: end.shift.dy + end.normal.ny * offsetX / yToX
|
|
694
|
+
});
|
|
695
|
+
const aheadKey = threads ? `${edge.from}>${edge.to}` : "";
|
|
696
|
+
const backKey = threads ? `${edge.to}>${edge.from}` : "";
|
|
697
|
+
const pairsOf = (lane) => {
|
|
698
|
+
const own = threads?.[lane]?.linkThreads;
|
|
699
|
+
if (!own?.size) {
|
|
700
|
+
return FIRST_THREADS;
|
|
701
|
+
}
|
|
702
|
+
const ahead = own.get(aheadKey);
|
|
703
|
+
const back = edgeKeys.has(backKey) ? void 0 : own.get(backKey);
|
|
704
|
+
if (!back) {
|
|
705
|
+
return ahead ?? FIRST_THREADS;
|
|
706
|
+
}
|
|
707
|
+
const turned = back.map(([a, b]) => [b, a]);
|
|
708
|
+
return ahead ? [...ahead, ...turned] : turned;
|
|
565
709
|
};
|
|
710
|
+
const single = laneWalks.length === 1;
|
|
566
711
|
laneWalks.forEach((walk, lane) => {
|
|
567
712
|
if (!walk.edgeIndexes.has(ei)) {
|
|
568
713
|
return;
|
|
569
714
|
}
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
laneWidth
|
|
576
|
-
|
|
715
|
+
for (const [pass, toPass] of pairsOf(lane)) {
|
|
716
|
+
const offsetX = single ? 0 : laneOffset(from.id, from.laneWidth, lane, pass) * worldPerScreenPx;
|
|
717
|
+
const start = { normal: from.normal, offsetX };
|
|
718
|
+
const end = threaded ? {
|
|
719
|
+
normal: to.normal,
|
|
720
|
+
offsetX: laneOffset(to.id, to.laneWidth, lane, toPass) * worldPerScreenPx
|
|
721
|
+
} : start;
|
|
722
|
+
const curves = threaded ? slideCurves(
|
|
723
|
+
baseCurves,
|
|
724
|
+
slideOf(from, start.offsetX),
|
|
725
|
+
slideOf(to, end.offsetX)
|
|
726
|
+
) : offsetX === 0 ? baseCurves : translateCurves(
|
|
727
|
+
baseCurves,
|
|
728
|
+
from.normal.nx * offsetX,
|
|
729
|
+
from.normal.ny * offsetX / yToX
|
|
577
730
|
);
|
|
731
|
+
if (inView(curves)) {
|
|
732
|
+
stroke(
|
|
733
|
+
curves,
|
|
734
|
+
laneHalfWidth(from.laneWidth),
|
|
735
|
+
walk.colors.get(edge.from) ?? walk.colors.get(edge.to) ?? plainEdgeColor,
|
|
736
|
+
{ walk: lane, pass, start, end }
|
|
737
|
+
);
|
|
738
|
+
}
|
|
578
739
|
}
|
|
579
740
|
});
|
|
580
741
|
if (edgeCurves.length > edgeCurveStart) {
|
|
@@ -644,17 +805,34 @@ function buildGeometry(options) {
|
|
|
644
805
|
const lanes = highlight?.walks.length && !faded && !slots?.length ? highlight.walks : void 0;
|
|
645
806
|
if (drawable && lanes) {
|
|
646
807
|
const laneWidth = walkLaneWidthPx(width, lanes.length);
|
|
647
|
-
const normals =
|
|
808
|
+
const normals = lanes.length > 1 ? laneNormalsOf(nodeId, segments) : [];
|
|
809
|
+
const points = lanes.length > 1 ? lanePointsOf(nodeId, segments) : segments;
|
|
648
810
|
lanes.forEach((walk, lane) => {
|
|
649
811
|
const laneColor = walk.colors.get(nodeId);
|
|
650
812
|
if (laneColor === void 0) {
|
|
651
813
|
return;
|
|
652
814
|
}
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
815
|
+
const onThreads = threads?.[lane]?.nodeThreads.get(nodeId) ?? ONE_PASS;
|
|
816
|
+
const thickness = laneHalfWidth(laneWidth);
|
|
817
|
+
onThreads.forEach((pass) => {
|
|
818
|
+
const offsetX = lanes.length === 1 ? 0 : laneOffset(nodeId, laneWidth, lane, pass) * worldPerScreenPx;
|
|
819
|
+
nodeStrokes.push(
|
|
820
|
+
lanes.length === 1 ? {
|
|
821
|
+
points: segments,
|
|
822
|
+
thickness,
|
|
823
|
+
color: laneColor,
|
|
824
|
+
walk: lane,
|
|
825
|
+
pass
|
|
826
|
+
} : {
|
|
827
|
+
points: offsetPolyline(points, normals, offsetX, yToX),
|
|
828
|
+
thickness,
|
|
829
|
+
color: laneColor,
|
|
830
|
+
walk: lane,
|
|
831
|
+
pass,
|
|
832
|
+
normals,
|
|
833
|
+
offsetX
|
|
834
|
+
}
|
|
835
|
+
);
|
|
658
836
|
});
|
|
659
837
|
});
|
|
660
838
|
} else if (drawable && slots?.length && slotWidth >= MIN_PATH_STRIPE_PX) {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
hasTubeMapPaths,
|
|
4
4
|
tubeMapLayout,
|
|
5
5
|
tubeMapReferenceLayout
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-G4WQBRDS.js";
|
|
7
7
|
import {
|
|
8
8
|
sampleRowLayout
|
|
9
9
|
} from "./chunk-XR2HMGAS.js";
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "./chunk-UAFATXYA.js";
|
|
16
16
|
import {
|
|
17
17
|
walkRowLayout
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-VCUOYVLC.js";
|
|
19
19
|
import {
|
|
20
20
|
isBackbone,
|
|
21
21
|
isOffReference
|
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
boxesGenes,
|
|
4
4
|
rowPitch,
|
|
5
5
|
rowSpan
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LA6JHEVV.js";
|
|
7
7
|
import {
|
|
8
8
|
el
|
|
9
9
|
} from "./chunk-H2H4Y3TV.js";
|
|
10
10
|
import {
|
|
11
11
|
READOUT_ROOM,
|
|
12
12
|
longestBar
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-VCUOYVLC.js";
|
|
14
14
|
import {
|
|
15
15
|
LABEL_CHAR_PX
|
|
16
16
|
} from "./chunk-AB35TJST.js";
|
|
@@ -7,15 +7,15 @@ import {
|
|
|
7
7
|
} from "./chunk-AB35TJST.js";
|
|
8
8
|
import {
|
|
9
9
|
polylineMidpoint
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YIWN32ZX.js";
|
|
11
11
|
import {
|
|
12
12
|
edgeCurvesOf
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-3CXZUC2Y.js";
|
|
14
14
|
import {
|
|
15
15
|
curveBounds,
|
|
16
16
|
curveMidpoint,
|
|
17
17
|
polylinePx
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-735MPLUL.js";
|
|
19
19
|
|
|
20
20
|
// src/graphLabels.ts
|
|
21
21
|
var MIN_DELETION_LABEL_PX = 26;
|