@jbrowse/bandage-core 6.0.1 → 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.
- package/README.md +23 -7
- package/dist/alleleProjection/projectAlleles.js +10 -0
- package/dist/anchoredNodes.js +24 -0
- package/dist/bubbleSpreads.js +11 -0
- package/dist/bubbles/bubbleHalos.js +11 -0
- package/dist/bubbles/bubbleLine.js +14 -0
- package/dist/bubbles/bubblesFromGraph.js +10 -0
- package/dist/bubbles/classifyBubble.js +15 -0
- package/dist/bubbles/popBubble.js +9 -0
- package/dist/chunks/chunk-3C2LGJKY.js +108 -0
- package/dist/chunks/chunk-3PLYZLYV.js +513 -0
- package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
- package/dist/chunks/chunk-3WHKR5TY.js +726 -0
- package/dist/chunks/chunk-47DYCZOK.js +115 -0
- package/dist/chunks/chunk-4S47XRYA.js +149 -0
- package/dist/chunks/chunk-527AI7CP.js +122 -0
- package/dist/chunks/chunk-57ZIXHZA.js +90 -0
- package/dist/chunks/chunk-5QT6NPSO.js +40 -0
- package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
- package/dist/chunks/chunk-6Q6HFBJT.js +170 -0
- package/dist/chunks/chunk-6SBUFFTB.js +97 -0
- package/dist/chunks/chunk-6YRRNX2P.js +61 -0
- package/dist/chunks/chunk-72N5DJHZ.js +155 -0
- package/dist/chunks/chunk-73RSLGZT.js +73 -0
- package/dist/chunks/chunk-7CHJKABZ.js +82 -0
- package/dist/chunks/chunk-7XHHVYG7.js +13 -0
- package/dist/chunks/chunk-AB35TJST.js +67 -0
- package/dist/chunks/chunk-AE3UQZY3.js +43 -0
- package/dist/chunks/chunk-BJV2W4KZ.js +257 -0
- package/dist/chunks/chunk-BPLIXMH7.js +52 -0
- package/dist/chunks/chunk-BXEPA4KS.js +179 -0
- package/dist/chunks/chunk-C44YRP5F.js +85 -0
- package/dist/chunks/chunk-CDLPYR5O.js +135 -0
- package/dist/chunks/chunk-CPMHA4W5.js +6 -0
- package/dist/chunks/chunk-CR7EGAFE.js +241 -0
- package/dist/chunks/chunk-CZ54VG2H.js +431 -0
- package/dist/chunks/chunk-D3M7SBKL.js +210 -0
- package/dist/chunks/chunk-EJ5OZTCK.js +52 -0
- package/dist/chunks/chunk-ES2N6V56.js +83 -0
- package/dist/chunks/chunk-EZPBJPK6.js +54 -0
- package/dist/chunks/chunk-FYQWPEEY.js +65 -0
- package/dist/chunks/chunk-GQ2AOHU4.js +112 -0
- package/dist/chunks/chunk-HB6PDCOU.js +177 -0
- package/dist/chunks/chunk-HCANBJDD.js +326 -0
- package/dist/chunks/chunk-HIJVRRYE.js +174 -0
- package/dist/chunks/chunk-HNQPEMYE.js +297 -0
- package/dist/chunks/chunk-IGVV37VI.js +60 -0
- package/dist/chunks/chunk-IMW3AK27.js +102 -0
- package/dist/chunks/chunk-JKJWSY4V.js +32 -0
- package/dist/chunks/chunk-KCW7L3NV.js +6 -0
- package/dist/chunks/chunk-KQFOCDFY.js +73 -0
- package/dist/chunks/chunk-L3O7AWLI.js +52 -0
- package/dist/chunks/chunk-L5RYUPZI.js +53 -0
- package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
- package/dist/chunks/chunk-LTUWJKHD.js +112 -0
- package/dist/chunks/chunk-LVZZH2NI.js +181 -0
- package/dist/chunks/chunk-MFKORG3F.js +44 -0
- package/dist/chunks/chunk-MHGHYHTL.js +205 -0
- package/dist/chunks/chunk-MI35EUIE.js +76 -0
- package/dist/chunks/chunk-MNUSQCLG.js +29 -0
- package/dist/chunks/chunk-NNODIW4U.js +26 -0
- package/dist/chunks/chunk-P7B3GDAY.js +232 -0
- package/dist/chunks/chunk-PBENCR5L.js +60 -0
- package/dist/chunks/chunk-PUG7LXDF.js +70 -0
- package/dist/chunks/chunk-Q3KCTA4N.js +202 -0
- package/dist/chunks/chunk-QCYM44UL.js +476 -0
- package/dist/chunks/chunk-QYBZZI5H.js +0 -0
- package/dist/chunks/chunk-RAKOB6IV.js +169 -0
- package/dist/chunks/chunk-RVS2QCOX.js +135 -0
- package/dist/chunks/chunk-SDGM243E.js +190 -0
- package/dist/chunks/chunk-ST2IBUAN.js +119 -0
- package/dist/chunks/chunk-SV27AJCC.js +101 -0
- package/dist/chunks/chunk-SZNZ2T6D.js +198 -0
- package/dist/chunks/chunk-TDQLINVI.js +16 -0
- package/dist/chunks/chunk-TEDQJPMI.js +131 -0
- package/dist/chunks/chunk-TZFWUT2C.js +66 -0
- package/dist/chunks/chunk-UVBGRJQX.js +50 -0
- package/dist/chunks/chunk-VHW7FDHU.js +42 -0
- package/dist/chunks/chunk-VKB57ZYE.js +218 -0
- package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
- package/dist/chunks/chunk-WOO7QYKB.js +121 -0
- package/dist/chunks/chunk-WSIFAZU7.js +132 -0
- package/dist/chunks/chunk-X32PABB7.js +117 -0
- package/dist/chunks/chunk-YJQ7CT7F.js +23 -0
- package/dist/cli/figure.js +84 -17
- package/dist/colorSchemes.js +10 -0
- package/dist/deletionEdges.js +13 -0
- package/dist/el.js +8 -0
- package/dist/facetGrid.js +16 -0
- package/dist/figure.js +51 -0
- package/dist/gaf/parseGaf.js +12 -0
- package/dist/gbzCut.js +19 -0
- package/dist/gbzJoin.js +6 -0
- package/dist/gbzWindow.js +28 -0
- package/dist/genes/exonOutline.js +11 -0
- package/dist/genes/geneFiles.js +14 -0
- package/dist/genes/genePins.js +10 -0
- package/dist/gfa/gfaConverter.js +11 -0
- package/dist/gfa-core/gfaParser.js +8 -0
- package/dist/gfa-core/index.js +9 -0
- package/dist/graphLabels.js +24 -0
- package/dist/index.js +262 -1407
- package/dist/labelLayout.js +24 -0
- package/dist/layout/anchoredLayout.js +11 -0
- package/dist/layout/deletionRoutes.js +10 -0
- package/dist/layout/drawnScale.js +22 -0
- package/dist/layout/mergeRuns.js +15 -0
- package/dist/layout/orderedLayout.js +12 -0
- package/dist/layout/orientToReference.js +7 -0
- package/dist/layout/placeOffReference.js +9 -0
- package/dist/layout/referenceSeeds.js +11 -0
- package/dist/layout/rowSpacing.js +6 -0
- package/dist/layout/sampleRowLayout.js +11 -0
- package/dist/layout/trimToWindow.js +99 -0
- package/dist/layout/tubeMapLayout.js +28 -0
- package/dist/layout/walkRowDraw.js +65 -0
- package/dist/layout/walkRowLayout.js +13 -0
- package/dist/layout/walkRows.js +11 -0
- package/dist/layout/walkStrip.js +40 -0
- package/dist/layoutModes.js +35 -0
- package/dist/loadBandage.js +6 -0
- package/dist/nodeWidths.js +18 -0
- package/dist/overlayLabels.js +18 -0
- package/dist/pansn.js +18 -0
- package/dist/pathAnchoring.js +18 -0
- package/dist/pathColors.js +31 -0
- package/dist/pipeline.js +40 -0
- package/dist/reference.js +24 -0
- package/dist/referenceRampCss.js +29 -0
- package/dist/referenceSpan.js +11 -0
- package/dist/referenceStrip.js +35 -0
- package/dist/renderer/Canvas2DRenderer.js +9 -0
- package/dist/renderer/GeometryBuilder.js +42 -0
- package/dist/renderer/colorBits.js +8 -0
- package/dist/renderer/recordingCanvas.js +44 -0
- package/dist/renderer/svgCanvas.js +6 -0
- package/dist/renderer/types.js +0 -0
- package/dist/tubeMap/axis.js +25 -0
- package/dist/tubeMap/coarsen.js +361 -0
- package/dist/tubeMap/connectors.js +13 -0
- package/dist/tubeMap/deviations.js +85 -0
- package/dist/tubeMap/draw.js +15 -0
- package/dist/tubeMap/frame.js +9 -0
- package/dist/tubeMap/genes.js +20 -0
- package/dist/tubeMap/mismatches.js +6 -0
- package/dist/tubeMap/nodeColors.js +21 -0
- package/dist/tubeMap/reads.js +17 -0
- package/dist/tubeMap/warp.js +8 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types.js +0 -0
- package/dist/util/SpatialIndex.js +13 -0
- package/dist/util/edgeCurves.js +10 -0
- package/dist/util/geometry.js +39 -0
- package/dist/util/hitDetection.js +20 -0
- package/dist/util/wheelZoom.js +6 -0
- package/dist/version.js +6 -0
- package/dist/viewport.js +33 -0
- package/dist/walkEncoding.js +39 -0
- package/dist/walkHighlight.js +26 -0
- package/dist/walkKey.js +17 -0
- package/package.json +38 -5
- package/dist/chunks/chunk-EXSFRX2F.js +0 -7400
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ROW_HEIGHT_PX
|
|
3
|
+
} from "./chunk-CPMHA4W5.js";
|
|
4
|
+
import {
|
|
5
|
+
isBackbone
|
|
6
|
+
} from "./chunk-FYQWPEEY.js";
|
|
7
|
+
|
|
8
|
+
// src/layout/orderedLayout.ts
|
|
9
|
+
var GUTTER = 12;
|
|
10
|
+
var LANE_PX = ROW_HEIGHT_PX;
|
|
11
|
+
function nodeWidth(node) {
|
|
12
|
+
return 10 + 8 * Math.log2(1 + node.length);
|
|
13
|
+
}
|
|
14
|
+
function compare(a, b) {
|
|
15
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
16
|
+
}
|
|
17
|
+
function compareKeys(a, b) {
|
|
18
|
+
return compare(a.at, b.at) || compare(a.side, b.side) || compare(a.hops, b.hops);
|
|
19
|
+
}
|
|
20
|
+
var UNREACHED = { at: Infinity, side: 0, hops: 0 };
|
|
21
|
+
function referenceOrder(graph) {
|
|
22
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
23
|
+
const key = orderKeys(graph);
|
|
24
|
+
return graph.nodes.map((n) => n.id).sort(
|
|
25
|
+
(a, b) => compareKeys(key.get(a) ?? UNREACHED, key.get(b) ?? UNREACHED) || compare(byId.get(a).name, byId.get(b).name) || compare(a, b)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function placeByWalks(graph, backbone) {
|
|
29
|
+
const isNode = new Set(graph.nodes.map((n) => n.id));
|
|
30
|
+
const startOf = new Map(backbone.map((n) => [n.id, n.stable.start]));
|
|
31
|
+
const next = /* @__PURE__ */ new Map();
|
|
32
|
+
const prev = /* @__PURE__ */ new Map();
|
|
33
|
+
backbone.forEach((n, i) => {
|
|
34
|
+
prev.set(n.id, backbone[i - 1]?.id);
|
|
35
|
+
next.set(n.id, backbone[i + 1]?.id);
|
|
36
|
+
});
|
|
37
|
+
let head = backbone[0]?.id;
|
|
38
|
+
const insertAfter = (at, id) => {
|
|
39
|
+
const after = next.get(at);
|
|
40
|
+
next.set(at, id);
|
|
41
|
+
prev.set(id, at);
|
|
42
|
+
next.set(id, after);
|
|
43
|
+
if (after !== void 0) {
|
|
44
|
+
prev.set(after, id);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const insertBefore = (at, id) => {
|
|
48
|
+
const before = prev.get(at);
|
|
49
|
+
if (before === void 0) {
|
|
50
|
+
head = id;
|
|
51
|
+
prev.set(id, void 0);
|
|
52
|
+
next.set(id, at);
|
|
53
|
+
prev.set(at, id);
|
|
54
|
+
} else {
|
|
55
|
+
insertAfter(before, id);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
for (const path of graph.paths ?? []) {
|
|
59
|
+
let rising = 0;
|
|
60
|
+
let falling = 0;
|
|
61
|
+
let last;
|
|
62
|
+
for (const id of path.nodeIds) {
|
|
63
|
+
const start = startOf.get(id);
|
|
64
|
+
if (start !== void 0) {
|
|
65
|
+
if (last !== void 0 && start !== last) {
|
|
66
|
+
if (start > last) {
|
|
67
|
+
rising++;
|
|
68
|
+
} else {
|
|
69
|
+
falling++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
last = start;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const ids = falling > rising ? [...path.nodeIds].reverse() : path.nodeIds;
|
|
76
|
+
let from;
|
|
77
|
+
const leading = /* @__PURE__ */ new Set();
|
|
78
|
+
for (const id of ids) {
|
|
79
|
+
if (next.has(id)) {
|
|
80
|
+
if (from === void 0) {
|
|
81
|
+
for (const lead of leading) {
|
|
82
|
+
insertBefore(id, lead);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
from = id;
|
|
86
|
+
} else if (isNode.has(id)) {
|
|
87
|
+
if (from === void 0) {
|
|
88
|
+
leading.add(id);
|
|
89
|
+
} else {
|
|
90
|
+
insertAfter(from, id);
|
|
91
|
+
from = id;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const placed = [];
|
|
97
|
+
for (let id = head; id !== void 0; id = next.get(id)) {
|
|
98
|
+
placed.push(id);
|
|
99
|
+
}
|
|
100
|
+
return placed;
|
|
101
|
+
}
|
|
102
|
+
function placeBySearch(adjacent, key) {
|
|
103
|
+
const queue = [...key.entries()].sort(([a, ka], [b, kb]) => compareKeys(ka, kb) || compare(a, b)).map(([id]) => id);
|
|
104
|
+
const placed = queue.length;
|
|
105
|
+
for (const id of queue) {
|
|
106
|
+
const from = key.get(id);
|
|
107
|
+
for (const nb of adjacent.get(id) ?? []) {
|
|
108
|
+
if (!key.has(nb)) {
|
|
109
|
+
key.set(nb, { at: from.at, side: 1, hops: from.hops + 1 });
|
|
110
|
+
queue.push(nb);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const searched = new Set(queue.slice(placed));
|
|
115
|
+
const sided = /* @__PURE__ */ new Set();
|
|
116
|
+
for (const seed of searched) {
|
|
117
|
+
if (sided.has(seed)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const { at } = key.get(seed);
|
|
121
|
+
const run = [seed];
|
|
122
|
+
sided.add(seed);
|
|
123
|
+
let farHalf = true;
|
|
124
|
+
for (const id of run) {
|
|
125
|
+
const { hops } = key.get(id);
|
|
126
|
+
let deadEnd = true;
|
|
127
|
+
let touchesLeft = false;
|
|
128
|
+
for (const nb of adjacent.get(id) ?? []) {
|
|
129
|
+
const other = key.get(nb);
|
|
130
|
+
if (other.at === at && searched.has(nb)) {
|
|
131
|
+
deadEnd &&= other.hops <= hops;
|
|
132
|
+
if (!sided.has(nb)) {
|
|
133
|
+
sided.add(nb);
|
|
134
|
+
run.push(nb);
|
|
135
|
+
}
|
|
136
|
+
} else if (other.at !== at) {
|
|
137
|
+
touchesLeft ||= other.at < at;
|
|
138
|
+
farHalf &&= other.at < at;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
farHalf &&= !deadEnd || touchesLeft;
|
|
142
|
+
}
|
|
143
|
+
if (farHalf) {
|
|
144
|
+
for (const id of run) {
|
|
145
|
+
const k = key.get(id);
|
|
146
|
+
key.set(id, { ...k, side: -1, hops: -k.hops });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function orderKeys(graph) {
|
|
152
|
+
const ids = new Set(graph.nodes.map((n) => n.id));
|
|
153
|
+
const adjacent = /* @__PURE__ */ new Map();
|
|
154
|
+
for (const e of graph.edges) {
|
|
155
|
+
if (!ids.has(e.from) || !ids.has(e.to)) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
;
|
|
159
|
+
(adjacent.get(e.from) ?? adjacent.set(e.from, []).get(e.from)).push(e.to);
|
|
160
|
+
(adjacent.get(e.to) ?? adjacent.set(e.to, []).get(e.to)).push(e.from);
|
|
161
|
+
}
|
|
162
|
+
const backbone = graph.nodes.filter(isBackbone).sort(
|
|
163
|
+
(a, b) => compare(a.stable.start, b.stable.start) || compare(a.name, b.name)
|
|
164
|
+
);
|
|
165
|
+
const key = /* @__PURE__ */ new Map();
|
|
166
|
+
placeByWalks(graph, backbone).forEach((id, rank) => {
|
|
167
|
+
key.set(id, { at: rank, side: 0, hops: 0 });
|
|
168
|
+
});
|
|
169
|
+
placeBySearch(adjacent, key);
|
|
170
|
+
return key;
|
|
171
|
+
}
|
|
172
|
+
function layerGraph(graph) {
|
|
173
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
174
|
+
const order = referenceOrder(graph);
|
|
175
|
+
const key = new Map(order.map((id, i) => [id, i]));
|
|
176
|
+
const isRef = (id) => isBackbone(byId.get(id));
|
|
177
|
+
const preds = new Map(order.map((id) => [id, []]));
|
|
178
|
+
const succs = new Map(order.map((id) => [id, []]));
|
|
179
|
+
const link = (a, b) => {
|
|
180
|
+
preds.get(b).push(a);
|
|
181
|
+
succs.get(a).push(b);
|
|
182
|
+
};
|
|
183
|
+
for (const e of graph.edges) {
|
|
184
|
+
if (e.from === e.to || !key.has(e.from) || !key.has(e.to)) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (key.get(e.from) < key.get(e.to)) {
|
|
188
|
+
link(e.from, e.to);
|
|
189
|
+
} else {
|
|
190
|
+
link(e.to, e.from);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const backbone = order.filter(isRef);
|
|
194
|
+
for (let i = 1; i < backbone.length; i++) {
|
|
195
|
+
link(backbone[i - 1], backbone[i]);
|
|
196
|
+
}
|
|
197
|
+
const layerOf = /* @__PURE__ */ new Map();
|
|
198
|
+
for (const id of order) {
|
|
199
|
+
let l = 0;
|
|
200
|
+
for (const p of preds.get(id)) {
|
|
201
|
+
l = Math.max(l, layerOf.get(p) + 1);
|
|
202
|
+
}
|
|
203
|
+
layerOf.set(id, l);
|
|
204
|
+
}
|
|
205
|
+
for (let i = order.length - 1; i >= 0; i--) {
|
|
206
|
+
const id = order[i];
|
|
207
|
+
const s = succs.get(id);
|
|
208
|
+
if (isRef(id) || s.length === 0) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
let minSucc = Infinity;
|
|
212
|
+
for (const t of s) {
|
|
213
|
+
minSucc = Math.min(minSucc, layerOf.get(t));
|
|
214
|
+
}
|
|
215
|
+
const lo = layerOf.get(id);
|
|
216
|
+
const hi = Math.max(lo, minSucc - 1);
|
|
217
|
+
layerOf.set(id, Math.floor((lo + hi) / 2));
|
|
218
|
+
}
|
|
219
|
+
const layers = [];
|
|
220
|
+
for (const id of order) {
|
|
221
|
+
;
|
|
222
|
+
(layers[layerOf.get(id)] ??= []).push(id);
|
|
223
|
+
}
|
|
224
|
+
return { layers, layerOf, preds };
|
|
225
|
+
}
|
|
226
|
+
function orderedLayout(graph) {
|
|
227
|
+
if (!graph.nodes.some(isBackbone)) {
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
231
|
+
const isRef = (id) => isBackbone(byId.get(id));
|
|
232
|
+
const { layers, layerOf: layer, preds } = layerGraph(graph);
|
|
233
|
+
const layerWidth = layers.map((ids) => {
|
|
234
|
+
let w = 0;
|
|
235
|
+
for (const id of ids) {
|
|
236
|
+
w = Math.max(w, nodeWidth(byId.get(id)));
|
|
237
|
+
}
|
|
238
|
+
return w;
|
|
239
|
+
});
|
|
240
|
+
const layerX = [];
|
|
241
|
+
let x = 0;
|
|
242
|
+
for (let l = 0; l < layers.length; l++) {
|
|
243
|
+
layerX[l] = x;
|
|
244
|
+
x += layerWidth[l] + GUTTER;
|
|
245
|
+
}
|
|
246
|
+
const y = /* @__PURE__ */ new Map();
|
|
247
|
+
for (const ids of layers) {
|
|
248
|
+
const free = [];
|
|
249
|
+
for (const id of ids) {
|
|
250
|
+
if (isRef(id)) {
|
|
251
|
+
y.set(id, 0);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const placed = preds.get(id).filter((p) => y.has(p));
|
|
255
|
+
let ideal = 0;
|
|
256
|
+
for (const p of placed) {
|
|
257
|
+
ideal += y.get(p) / placed.length;
|
|
258
|
+
}
|
|
259
|
+
free.push({ id, ideal: ideal === 0 ? LANE_PX : ideal });
|
|
260
|
+
}
|
|
261
|
+
free.sort((a, b) => a.ideal - b.ideal);
|
|
262
|
+
const taken = /* @__PURE__ */ new Set([0]);
|
|
263
|
+
for (const { id, ideal } of free) {
|
|
264
|
+
let lane = Math.round(ideal / LANE_PX);
|
|
265
|
+
if (lane === 0) {
|
|
266
|
+
lane = ideal >= 0 ? 1 : -1;
|
|
267
|
+
}
|
|
268
|
+
for (let d = 1; taken.has(lane); d++) {
|
|
269
|
+
if (!taken.has(lane + d)) {
|
|
270
|
+
lane += d;
|
|
271
|
+
} else if (!taken.has(lane - d)) {
|
|
272
|
+
lane -= d;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
taken.add(lane);
|
|
276
|
+
y.set(id, lane * LANE_PX);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const nodePositions = {};
|
|
280
|
+
for (const n of graph.nodes) {
|
|
281
|
+
const l = layer.get(n.id);
|
|
282
|
+
const w = nodeWidth(n);
|
|
283
|
+
const cx = layerX[l] + layerWidth[l] / 2;
|
|
284
|
+
const ny = y.get(n.id);
|
|
285
|
+
nodePositions[n.id] = [
|
|
286
|
+
{ x: cx - w / 2, y: ny },
|
|
287
|
+
{ x: cx + w / 2, y: ny }
|
|
288
|
+
];
|
|
289
|
+
}
|
|
290
|
+
return { nodePositions, referenceAxis: false, pixelRows: true };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export {
|
|
294
|
+
referenceOrder,
|
|
295
|
+
layerGraph,
|
|
296
|
+
orderedLayout
|
|
297
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildNeighbors
|
|
3
|
+
} from "./chunk-ES2N6V56.js";
|
|
4
|
+
import {
|
|
5
|
+
drawnNodeLength
|
|
6
|
+
} from "./chunk-6JEWNXCQ.js";
|
|
7
|
+
import {
|
|
8
|
+
isBackbone
|
|
9
|
+
} from "./chunk-FYQWPEEY.js";
|
|
10
|
+
|
|
11
|
+
// src/layout/referenceSeeds.ts
|
|
12
|
+
var LANE_GAP = 30;
|
|
13
|
+
function referenceSeeds(graph, scaling) {
|
|
14
|
+
const { opts } = scaling;
|
|
15
|
+
const drawn = new Map(
|
|
16
|
+
scaling.nodes.map((node) => [node.id, drawnNodeLength(opts, node.length)])
|
|
17
|
+
);
|
|
18
|
+
const drawnOf = (id) => drawn.get(id) ?? opts.minimumNodeLength;
|
|
19
|
+
const backbone = graph.nodes.filter(isBackbone).sort((a, b) => a.stable.start - b.stable.start);
|
|
20
|
+
const seeds = /* @__PURE__ */ new Map();
|
|
21
|
+
let x = 0;
|
|
22
|
+
for (const node of backbone) {
|
|
23
|
+
seeds.set(node.id, { x, y: 0 });
|
|
24
|
+
x += drawnOf(node.id) + opts.edgeLength;
|
|
25
|
+
}
|
|
26
|
+
const adjacent = buildNeighbors(graph);
|
|
27
|
+
const depth = new Map(backbone.map((node) => [node.id, 0]));
|
|
28
|
+
const queue = backbone.map((node) => node.id);
|
|
29
|
+
for (const id of queue) {
|
|
30
|
+
const at = seeds.get(id);
|
|
31
|
+
const next = depth.get(id) + 1;
|
|
32
|
+
for (const neighbour of adjacent.get(id) ?? []) {
|
|
33
|
+
if (depth.has(neighbour)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
depth.set(neighbour, next);
|
|
37
|
+
seeds.set(neighbour, { x: at.x + drawnOf(id) / 2, y: LANE_GAP * next });
|
|
38
|
+
queue.push(neighbour);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
let unreached = 0;
|
|
42
|
+
for (const node of graph.nodes) {
|
|
43
|
+
if (!seeds.has(node.id)) {
|
|
44
|
+
seeds.set(node.id, {
|
|
45
|
+
x: unreached++ * x / graph.nodes.length,
|
|
46
|
+
y: LANE_GAP * 3
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return seeds;
|
|
51
|
+
}
|
|
52
|
+
function seededNodes(graph, scaling) {
|
|
53
|
+
const seeds = referenceSeeds(graph, scaling);
|
|
54
|
+
return scaling.nodes.map((node) => ({ ...node, ...seeds.get(node.id) }));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
referenceSeeds,
|
|
59
|
+
seededNodes
|
|
60
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
REFERENCE_RAMP_ALT_COLOR,
|
|
3
|
+
REFERENCE_RAMP_MAX_HUE,
|
|
4
|
+
hslToRgb
|
|
5
|
+
} from "./chunk-3WHKR5TY.js";
|
|
6
|
+
|
|
7
|
+
// src/walkEncoding.ts
|
|
8
|
+
import { packAbgr } from "@jbrowse/core/util/colorBits";
|
|
9
|
+
var WALK_FIELDS = [
|
|
10
|
+
{ value: "progress", label: "Progress along the walk" },
|
|
11
|
+
{ value: "walk", label: "One colour for the walk" },
|
|
12
|
+
{ value: "reference", label: "Reference position" }
|
|
13
|
+
];
|
|
14
|
+
var WALK_SCHEMES = [
|
|
15
|
+
{ value: "grey", label: "Grey to black", ends: ["#a3a3a3", "#151515"] },
|
|
16
|
+
{ value: "blue", label: "Cyan to navy", ends: ["#6ee0f0", "#0b2c7a"] },
|
|
17
|
+
{ value: "red", label: "Yellow to red", ends: ["#ffd24a", "#b3121b"] },
|
|
18
|
+
{ value: "purple", label: "Pink to plum", ends: ["#f5a3d8", "#5e1a6e"] },
|
|
19
|
+
{ value: "green", label: "Lime to forest", ends: ["#b6e36c", "#0b5d3b"] },
|
|
20
|
+
{ value: "rainbow", label: "Rainbow" }
|
|
21
|
+
];
|
|
22
|
+
var FAMILIES = ["blue", "red", "purple", "green"];
|
|
23
|
+
var isField = (v) => WALK_FIELDS.some((f) => f.value === v);
|
|
24
|
+
var isScheme = (v) => WALK_SCHEMES.some((s) => s.value === v);
|
|
25
|
+
function resolveEncoding(layer, reference, picked, alone = true) {
|
|
26
|
+
const { field: askedField, scheme: askedScheme } = layer.color ?? {};
|
|
27
|
+
const field = isField(askedField) ? askedField : alone ? "progress" : "walk";
|
|
28
|
+
const family = reference ? "grey" : FAMILIES[picked % FAMILIES.length];
|
|
29
|
+
const scheme = isScheme(askedScheme) ? askedScheme === "rainbow" && field !== "reference" ? family : askedScheme : family;
|
|
30
|
+
return { field, scheme };
|
|
31
|
+
}
|
|
32
|
+
var BASE_T = 0.5;
|
|
33
|
+
var toLinear = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
34
|
+
var toGamma = (c) => c <= 31308e-7 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
35
|
+
function oklab(hex) {
|
|
36
|
+
const [r, g, b] = [1, 3, 5].map(
|
|
37
|
+
(i) => toLinear(Number.parseInt(hex.slice(i, i + 2), 16) / 255)
|
|
38
|
+
);
|
|
39
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
40
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
41
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
42
|
+
return [
|
|
43
|
+
0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
44
|
+
1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
45
|
+
0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
function oklabToRgb([L, a, b]) {
|
|
49
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
50
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
51
|
+
const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
52
|
+
return [
|
|
53
|
+
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
|
54
|
+
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
|
55
|
+
-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
|
|
56
|
+
].map((c) => toGamma(Math.max(0, Math.min(1, c))) * 255);
|
|
57
|
+
}
|
|
58
|
+
function schemeRgb(scheme, t) {
|
|
59
|
+
const x = Math.max(0, Math.min(1, t));
|
|
60
|
+
const family = WALK_SCHEMES.find((s) => s.value === scheme);
|
|
61
|
+
if (!family || !("ends" in family)) {
|
|
62
|
+
return hslToRgb(x * REFERENCE_RAMP_MAX_HUE, 0.7, 0.5).map((c) => c * 255);
|
|
63
|
+
}
|
|
64
|
+
const from = oklab(family.ends[0]);
|
|
65
|
+
const to = oklab(family.ends[1]);
|
|
66
|
+
return oklabToRgb(from.map((v, i) => v + (to[i] - v) * x));
|
|
67
|
+
}
|
|
68
|
+
function schemeCss(scheme, t) {
|
|
69
|
+
const [r, g, b] = schemeRgb(scheme, t).map(Math.round);
|
|
70
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
71
|
+
}
|
|
72
|
+
function schemeColor(scheme, t) {
|
|
73
|
+
const [r, g, b] = schemeRgb(scheme, t).map(Math.round);
|
|
74
|
+
return packAbgr(r, g, b, 255);
|
|
75
|
+
}
|
|
76
|
+
function encodedColor({ field, scheme }, t) {
|
|
77
|
+
return field === "walk" ? schemeColor(scheme, BASE_T) : t === void 0 ? NO_VALUE_COLOR : schemeColor(scheme, t);
|
|
78
|
+
}
|
|
79
|
+
var NO_VALUE_COLOR = REFERENCE_RAMP_ALT_COLOR;
|
|
80
|
+
function encodingStops({ field, scheme }) {
|
|
81
|
+
return field === "walk" ? [{ offset: 0, color: schemeCss(scheme, BASE_T) }] : Array.from({ length: 7 }, (_, i) => ({
|
|
82
|
+
offset: i / 6,
|
|
83
|
+
color: schemeCss(scheme, i / 6)
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
function encodingSwatchCss(encoding) {
|
|
87
|
+
const stops = encodingStops(encoding);
|
|
88
|
+
return stops.length === 1 ? stops[0].color : `linear-gradient(to right, ${stops.map((s) => `${s.color} ${s.offset * 100}%`).join(", ")})`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
WALK_FIELDS,
|
|
93
|
+
WALK_SCHEMES,
|
|
94
|
+
resolveEncoding,
|
|
95
|
+
schemeRgb,
|
|
96
|
+
schemeCss,
|
|
97
|
+
schemeColor,
|
|
98
|
+
encodedColor,
|
|
99
|
+
NO_VALUE_COLOR,
|
|
100
|
+
encodingStops,
|
|
101
|
+
encodingSwatchCss
|
|
102
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/colorSchemes.ts
|
|
2
|
+
var COLOR_SCHEMES = [
|
|
3
|
+
// Mirrors `layoutModes`' 'auto': a value the model resolves rather than a
|
|
4
|
+
// colour of its own. It exists because 'uniform' was the default, so a
|
|
5
|
+
// launched graph opened flat grey and both tutorials spent a step saying "now
|
|
6
|
+
// pick a colour" — one figure literally drives that click. A graph with
|
|
7
|
+
// reference coordinates resolves to 'reference-position', which is the one
|
|
8
|
+
// ramp a linear track beside it can be painted with too; a graph with none
|
|
9
|
+
// keeps 'uniform', because there the ramp would be a hue nothing earned.
|
|
10
|
+
//
|
|
11
|
+
// Resolve through `effectiveColorScheme`; the dropdown reads the raw prop, so
|
|
12
|
+
// "Auto" stays selectable and visible as what it is.
|
|
13
|
+
{ value: "auto", label: "Auto" },
|
|
14
|
+
{ value: "uniform", label: "Uniform" },
|
|
15
|
+
{ value: "random", label: "Random" },
|
|
16
|
+
{ value: "rainbow", label: "Rainbow" },
|
|
17
|
+
{ value: "depth", label: "Depth" },
|
|
18
|
+
{ value: "node-length", label: "Node length" },
|
|
19
|
+
{ value: "stable-rank", label: "Stable rank" },
|
|
20
|
+
{ value: "reference-position", label: "Reference position" },
|
|
21
|
+
{ value: "grey", label: "Grey" }
|
|
22
|
+
];
|
|
23
|
+
var COLOR_SCHEME_VALUES = COLOR_SCHEMES.map((s) => s.value);
|
|
24
|
+
function resolveColorScheme(scheme, graph) {
|
|
25
|
+
return scheme !== "auto" ? scheme : graph?.anchoredBy ? "reference-position" : "uniform";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
COLOR_SCHEMES,
|
|
30
|
+
COLOR_SCHEME_VALUES,
|
|
31
|
+
resolveColorScheme
|
|
32
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/tubeMap/warp.ts
|
|
2
|
+
var MAX_GAP_PX = 24;
|
|
3
|
+
var isReal = (c) => c.bp1 > c.bp0;
|
|
4
|
+
function referenceKnots(columns, bpToScreen) {
|
|
5
|
+
const real = columns.filter(isReal);
|
|
6
|
+
if (real.length === 0) {
|
|
7
|
+
const first = columns[0];
|
|
8
|
+
return first ? [{ tx: first.x0, sx: bpToScreen(first.bp0) }] : [];
|
|
9
|
+
}
|
|
10
|
+
const screen = real.map((c) => ({
|
|
11
|
+
c,
|
|
12
|
+
s0: bpToScreen(c.bp0),
|
|
13
|
+
s1: bpToScreen(c.bp1),
|
|
14
|
+
trimLeft: 0,
|
|
15
|
+
trimRight: 0
|
|
16
|
+
}));
|
|
17
|
+
for (let i = 0; i + 1 < screen.length; i++) {
|
|
18
|
+
const left = screen[i];
|
|
19
|
+
const right = screen[i + 1];
|
|
20
|
+
const demand = Math.min(right.c.x0 - left.c.x1, MAX_GAP_PX);
|
|
21
|
+
const short = demand - (right.s0 - left.s1);
|
|
22
|
+
if (short > 0) {
|
|
23
|
+
const capLeft = (left.s1 - left.s0) / 2;
|
|
24
|
+
const capRight = (right.s1 - right.s0) / 2;
|
|
25
|
+
let fromLeft = Math.min(short / 2, capLeft);
|
|
26
|
+
const fromRight = Math.min(short - fromLeft, capRight);
|
|
27
|
+
fromLeft = Math.min(short - fromRight, capLeft);
|
|
28
|
+
left.trimRight = fromLeft;
|
|
29
|
+
right.trimLeft = fromRight;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const knots = [];
|
|
33
|
+
for (const { c, s0, s1, trimLeft, trimRight } of screen) {
|
|
34
|
+
knots.push(
|
|
35
|
+
{ tx: c.x0, sx: s0 + trimLeft },
|
|
36
|
+
{ tx: c.x1, sx: s1 - trimRight }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return knots;
|
|
40
|
+
}
|
|
41
|
+
function warpX(knots, tx) {
|
|
42
|
+
const n = knots.length;
|
|
43
|
+
if (n === 0) {
|
|
44
|
+
return tx;
|
|
45
|
+
}
|
|
46
|
+
const first = knots[0];
|
|
47
|
+
const last = knots[n - 1];
|
|
48
|
+
if (tx <= first.tx) {
|
|
49
|
+
return first.sx + (tx - first.tx);
|
|
50
|
+
}
|
|
51
|
+
if (tx >= last.tx) {
|
|
52
|
+
return last.sx + (tx - last.tx);
|
|
53
|
+
}
|
|
54
|
+
let lo = 0;
|
|
55
|
+
let hi = n - 1;
|
|
56
|
+
while (hi - lo > 1) {
|
|
57
|
+
const mid = lo + hi >> 1;
|
|
58
|
+
if (knots[mid].tx <= tx) {
|
|
59
|
+
lo = mid;
|
|
60
|
+
} else {
|
|
61
|
+
hi = mid;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const a = knots[lo];
|
|
65
|
+
const b = knots[hi];
|
|
66
|
+
const span = b.tx - a.tx;
|
|
67
|
+
return span > 0 ? a.sx + (tx - a.tx) / span * (b.sx - a.sx) : a.sx;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
referenceKnots,
|
|
72
|
+
warpX
|
|
73
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
placeOffReference
|
|
3
|
+
} from "./chunk-TZFWUT2C.js";
|
|
4
|
+
import {
|
|
5
|
+
ROW_HEIGHT_PX
|
|
6
|
+
} from "./chunk-CPMHA4W5.js";
|
|
7
|
+
import {
|
|
8
|
+
backboneNodes,
|
|
9
|
+
backbonePositions,
|
|
10
|
+
referenceSpan
|
|
11
|
+
} from "./chunk-FYQWPEEY.js";
|
|
12
|
+
|
|
13
|
+
// src/layout/anchoredLayout.ts
|
|
14
|
+
function rankRows(graph) {
|
|
15
|
+
const present = /* @__PURE__ */ new Set();
|
|
16
|
+
for (const node of graph.nodes) {
|
|
17
|
+
if (node.stable) {
|
|
18
|
+
present.add(node.stable.rank);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return new Map([...present].sort((a, b) => a - b).map((r, i) => [r, i]));
|
|
22
|
+
}
|
|
23
|
+
function anchoredLayout(graph, region) {
|
|
24
|
+
const backbone = backboneNodes(graph);
|
|
25
|
+
if (backbone.length === 0) {
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
const span = referenceSpan(backbone, region);
|
|
29
|
+
const rows = rankRows(graph);
|
|
30
|
+
const nodePositions = backbonePositions(backbone);
|
|
31
|
+
const alleleDeletions = placeOffReference({
|
|
32
|
+
graph,
|
|
33
|
+
span,
|
|
34
|
+
rowY: (node) => (node.stable ? rows.get(node.stable.rank) ?? rows.size : rows.size) * ROW_HEIGHT_PX,
|
|
35
|
+
positions: nodePositions
|
|
36
|
+
});
|
|
37
|
+
const rowLabels = [...rows].map(([rank, row]) => ({
|
|
38
|
+
label: rank === 0 ? "Reference (rank 0)" : `Rank ${rank}`,
|
|
39
|
+
y: row * ROW_HEIGHT_PX
|
|
40
|
+
}));
|
|
41
|
+
return {
|
|
42
|
+
nodePositions,
|
|
43
|
+
rowLabels,
|
|
44
|
+
referenceAxis: true,
|
|
45
|
+
pixelRows: true,
|
|
46
|
+
alleleDeletions
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
anchoredLayout
|
|
52
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clampZoom
|
|
3
|
+
} from "./chunk-VKB57ZYE.js";
|
|
4
|
+
|
|
5
|
+
// src/viewport.ts
|
|
6
|
+
function axisScaleOf(scale, pixelRows) {
|
|
7
|
+
return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows };
|
|
8
|
+
}
|
|
9
|
+
function viewportOf(t, axis, width, height) {
|
|
10
|
+
return {
|
|
11
|
+
minX: -t.translateX / axis.scaleX,
|
|
12
|
+
minY: -t.translateY / axis.scaleY,
|
|
13
|
+
maxX: (width - t.translateX) / axis.scaleX,
|
|
14
|
+
maxY: (height - t.translateY) / axis.scaleY
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function padded(v, panes) {
|
|
18
|
+
const w = (v.maxX - v.minX) * panes;
|
|
19
|
+
const h = (v.maxY - v.minY) * panes;
|
|
20
|
+
return {
|
|
21
|
+
minX: v.minX - w,
|
|
22
|
+
minY: v.minY - h,
|
|
23
|
+
maxX: v.maxX + w,
|
|
24
|
+
maxY: v.maxY + h
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function contains(outer, inner) {
|
|
28
|
+
return inner.minX >= outer.minX && inner.maxX <= outer.maxX && inner.minY >= outer.minY && inner.maxY <= outer.maxY;
|
|
29
|
+
}
|
|
30
|
+
function zoomAbout(t, factor, centerX, centerY, pixelRows) {
|
|
31
|
+
const scale = clampZoom(t.scale * factor);
|
|
32
|
+
const ratio = scale / t.scale;
|
|
33
|
+
return {
|
|
34
|
+
scale,
|
|
35
|
+
translateX: centerX - (centerX - t.translateX) * ratio,
|
|
36
|
+
translateY: pixelRows ? t.translateY : centerY - (centerY - t.translateY) * ratio
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function screenToLayout(x, y, t, axis) {
|
|
40
|
+
return {
|
|
41
|
+
x: (x - t.translateX) / axis.scaleX,
|
|
42
|
+
y: (y - t.translateY) / axis.scaleY
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
axisScaleOf,
|
|
48
|
+
viewportOf,
|
|
49
|
+
padded,
|
|
50
|
+
contains,
|
|
51
|
+
zoomAbout,
|
|
52
|
+
screenToLayout
|
|
53
|
+
};
|