@jbrowse/bandage-core 9.0.0 → 9.1.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.
Files changed (41) hide show
  1. package/dist/bubbles/bubbleHalos.js +9 -5
  2. package/dist/bubbles/bubbleLine.js +3 -1
  3. package/dist/bubbles/bubblesFromGraph.js +8 -4
  4. package/dist/bubbles/classifyBubble.js +2 -2
  5. package/dist/bubbles/superbubbles.js +21 -0
  6. package/dist/chunks/{chunk-ISEI2FH3.js → chunk-2AWUWGAD.js} +10 -4
  7. package/dist/chunks/{chunk-YOYLCLIG.js → chunk-2ZT7ZF7A.js} +6 -2
  8. package/dist/chunks/{chunk-TLQ7F2ZT.js → chunk-3TGUKQWQ.js} +4 -4
  9. package/dist/chunks/{chunk-LLP3FXPM.js → chunk-75HR3756.js} +24 -7
  10. package/dist/chunks/chunk-FUVI4WQP.js +273 -0
  11. package/dist/chunks/{chunk-MFKORG3F.js → chunk-FX4RDMDN.js} +5 -1
  12. package/dist/chunks/{chunk-4S47XRYA.js → chunk-GOZYKNQ2.js} +16 -11
  13. package/dist/chunks/{chunk-TL4B342F.js → chunk-MXDQGA7A.js} +1 -1
  14. package/dist/chunks/{chunk-SKKYLBRA.js → chunk-RRZMMYBG.js} +1 -1
  15. package/dist/chunks/{chunk-AMS3MAFP.js → chunk-UID3QBWH.js} +1 -1
  16. package/dist/chunks/{chunk-2WNBVB55.js → chunk-WBCOREDZ.js} +121 -74
  17. package/dist/chunks/{chunk-WOO7QYKB.js → chunk-XKWD2RY3.js} +8 -2
  18. package/dist/chunks/{chunk-6VBDMZBZ.js → chunk-XLCTQHBE.js} +1 -1
  19. package/dist/cli/figure.js +6 -6
  20. package/dist/figure.js +6 -6
  21. package/dist/figureSettings.js +3 -3
  22. package/dist/index.js +34 -24
  23. package/dist/layout/forceCache.js +3 -3
  24. package/dist/pipeline.js +2 -2
  25. package/dist/renderer/Canvas2DRenderer.js +1 -1
  26. package/dist/renderer/recordingCanvas.js +12 -1
  27. package/dist/tubeMap/connectors.js +3 -1
  28. package/dist/tubeMap/draw.js +3 -1
  29. package/dist/types/bubbles/bubbleHalos.d.ts +2 -0
  30. package/dist/types/bubbles/bubbleLine.d.ts +3 -0
  31. package/dist/types/bubbles/bubblesFromGraph.d.ts +12 -2
  32. package/dist/types/bubbles/superbubbles.d.ts +5 -0
  33. package/dist/types/index.d.ts +3 -2
  34. package/dist/types/renderer/Canvas2DRenderer.d.ts +6 -3
  35. package/dist/types/tubeMap/connectors.d.ts +1 -0
  36. package/dist/types/tubeMap/draw.d.ts +1 -0
  37. package/dist/types/version.d.ts +1 -1
  38. package/dist/version.js +1 -1
  39. package/dist/viewport.js +3 -3
  40. package/package.json +1 -1
  41. package/dist/chunks/{chunk-5OIVTEF7.js → chunk-OOXIMURX.js} +3 -3
@@ -1,11 +1,15 @@
1
1
  import {
2
- bubbleHalos
3
- } from "../chunks/chunk-ISEI2FH3.js";
4
- import "../chunks/chunk-WOO7QYKB.js";
5
- import "../chunks/chunk-MFKORG3F.js";
2
+ WHOLE_FRACTION,
3
+ bubbleHalos,
4
+ coversGraph
5
+ } from "../chunks/chunk-2AWUWGAD.js";
6
+ import "../chunks/chunk-XKWD2RY3.js";
7
+ import "../chunks/chunk-FX4RDMDN.js";
6
8
  import "../chunks/chunk-2CVHXFK7.js";
7
9
  import "../chunks/chunk-CPMHA4W5.js";
8
10
  import "../chunks/chunk-FYQWPEEY.js";
9
11
  export {
10
- bubbleHalos
12
+ WHOLE_FRACTION,
13
+ bubbleHalos,
14
+ coversGraph
11
15
  };
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  SATURATED_PATH_COUNT,
3
3
  bubbleDescription,
4
+ bubbleKey,
4
5
  bubbleLabel,
5
6
  parseBubbleLine,
6
7
  sameBubble
7
- } from "../chunks/chunk-MFKORG3F.js";
8
+ } from "../chunks/chunk-FX4RDMDN.js";
8
9
  export {
9
10
  SATURATED_PATH_COUNT,
10
11
  bubbleDescription,
12
+ bubbleKey,
11
13
  bubbleLabel,
12
14
  parseBubbleLine,
13
15
  sameBubble
@@ -1,10 +1,14 @@
1
1
  import {
2
- bubblesFromGraph
3
- } from "../chunks/chunk-4S47XRYA.js";
4
- import "../chunks/chunk-MFKORG3F.js";
2
+ bubblesFromGraph,
3
+ walkIndexOf,
4
+ walkRoutes
5
+ } from "../chunks/chunk-GOZYKNQ2.js";
6
+ import "../chunks/chunk-FX4RDMDN.js";
5
7
  import "../chunks/chunk-HNQPEMYE.js";
6
8
  import "../chunks/chunk-CPMHA4W5.js";
7
9
  import "../chunks/chunk-FYQWPEEY.js";
8
10
  export {
9
- bubblesFromGraph
11
+ bubblesFromGraph,
12
+ walkIndexOf,
13
+ walkRoutes
10
14
  };
@@ -4,8 +4,8 @@ import {
4
4
  bubbleSegmentIds,
5
5
  classifyBubble,
6
6
  formatBp
7
- } from "../chunks/chunk-WOO7QYKB.js";
8
- import "../chunks/chunk-MFKORG3F.js";
7
+ } from "../chunks/chunk-XKWD2RY3.js";
8
+ import "../chunks/chunk-FX4RDMDN.js";
9
9
  export {
10
10
  BUBBLE_KIND_COLORS,
11
11
  BUBBLE_KIND_NAMES,
@@ -0,0 +1,21 @@
1
+ import {
2
+ MAX_SUPERBUBBLES,
3
+ graphBubbles,
4
+ superbubblesFromGraph
5
+ } from "../chunks/chunk-FUVI4WQP.js";
6
+ import "../chunks/chunk-VHW7FDHU.js";
7
+ import "../chunks/chunk-2AWUWGAD.js";
8
+ import "../chunks/chunk-GOZYKNQ2.js";
9
+ import "../chunks/chunk-XKWD2RY3.js";
10
+ import "../chunks/chunk-FX4RDMDN.js";
11
+ import "../chunks/chunk-HNQPEMYE.js";
12
+ import "../chunks/chunk-ST2IBUAN.js";
13
+ import "../chunks/chunk-BPLIXMH7.js";
14
+ import "../chunks/chunk-2CVHXFK7.js";
15
+ import "../chunks/chunk-CPMHA4W5.js";
16
+ import "../chunks/chunk-FYQWPEEY.js";
17
+ export {
18
+ MAX_SUPERBUBBLES,
19
+ graphBubbles,
20
+ superbubblesFromGraph
21
+ };
@@ -2,7 +2,7 @@ import {
2
2
  bubbleSegmentIds,
3
3
  classifyBubble,
4
4
  formatBp
5
- } from "./chunk-WOO7QYKB.js";
5
+ } from "./chunk-XKWD2RY3.js";
6
6
  import {
7
7
  svgPath
8
8
  } from "./chunk-2CVHXFK7.js";
@@ -12,6 +12,9 @@ import {
12
12
 
13
13
  // src/bubbles/bubbleHalos.ts
14
14
  var WHOLE_FRACTION = 0.9;
15
+ function coversGraph(members, nodes) {
16
+ return members + 2 >= nodes || members >= WHOLE_FRACTION * nodes;
17
+ }
15
18
  var NAMED_WALKS = 2;
16
19
  var MIN_CHIPPED_BP = 1e3;
17
20
  function bubbleHalos(graph, bubbles, positions, walkLabel = (name) => name, repeats = []) {
@@ -24,13 +27,14 @@ function bubbleHalos(graph, bubbles, positions, walkLabel = (name) => name, repe
24
27
  let minY = Infinity;
25
28
  const nodeIds = [];
26
29
  const ends = [];
27
- for (const name of bubbleSegmentIds(bubble)) {
30
+ const names = bubbleSegmentIds(bubble);
31
+ for (const [i, name] of names.entries()) {
28
32
  const node = byName.get(name);
29
33
  const line = node && positions[node.id];
30
34
  if (!node || !line?.length) {
31
35
  continue;
32
36
  }
33
- if (isBackbone(node) && (node.stable.start < bubble.start || node.stable.start >= bubble.end)) {
37
+ if (bubble.key ? i === 0 || i === names.length - 1 : isBackbone(node) && (node.stable.start < bubble.start || node.stable.start >= bubble.end)) {
34
38
  ends.push(line[Math.floor(line.length / 2)]);
35
39
  continue;
36
40
  }
@@ -90,7 +94,7 @@ function bubbleHalos(graph, bubbles, positions, walkLabel = (name) => name, repe
90
94
  labelAt,
91
95
  members: nodeIds.length,
92
96
  nodeIds,
93
- whole: nodeIds.length >= WHOLE_FRACTION * graph.nodes.length,
97
+ whole: coversGraph(nodeIds.length, graph.nodes.length),
94
98
  routes
95
99
  });
96
100
  }
@@ -104,5 +108,7 @@ function routeText(route, walkLabel) {
104
108
  }
105
109
 
106
110
  export {
111
+ WHOLE_FRACTION,
112
+ coversGraph,
107
113
  bubbleHalos
108
114
  };
@@ -69,7 +69,10 @@ function drawTubeMapConnectors(ctx, connectors, bottom, {
69
69
  ctx.strokeStyle = `rgba(${ink},0.35)`;
70
70
  ctx.lineWidth = 0.5;
71
71
  ctx.stroke();
72
- const lit = connectors.find((c) => c.node === highlightNode);
72
+ drawLitConnector(ctx, connectors, bottom, highlightNode);
73
+ }
74
+ function drawLitConnector(ctx, connectors, bottom, node) {
75
+ const lit = bottom > 0 && node ? connectors.find((c) => c.node === node) : void 0;
73
76
  if (lit) {
74
77
  ctx.beginPath();
75
78
  band(ctx, lit, bottom);
@@ -86,5 +89,6 @@ function drawTubeMapConnectors(ctx, connectors, bottom, {
86
89
  export {
87
90
  tubeMapConnectors,
88
91
  connectorAt,
89
- drawTubeMapConnectors
92
+ drawTubeMapConnectors,
93
+ drawLitConnector
90
94
  };
@@ -16,7 +16,7 @@ import {
16
16
  } from "./chunk-MIW3HYPE.js";
17
17
  import {
18
18
  Canvas2DRenderer
19
- } from "./chunk-2WNBVB55.js";
19
+ } from "./chunk-WBCOREDZ.js";
20
20
  import {
21
21
  EXON_COLOR,
22
22
  exonOutlineTree,
@@ -42,10 +42,10 @@ import {
42
42
  } from "./chunk-HBEKUQO4.js";
43
43
  import {
44
44
  version
45
- } from "./chunk-AMS3MAFP.js";
45
+ } from "./chunk-UID3QBWH.js";
46
46
  import {
47
47
  axisScaleOf
48
- } from "./chunk-TL4B342F.js";
48
+ } from "./chunk-MXDQGA7A.js";
49
49
  import {
50
50
  walkRowLabels,
51
51
  walkRowsExtent
@@ -91,7 +91,7 @@ import {
91
91
  FIT_PADDING,
92
92
  drawingBounds,
93
93
  fitTransform
94
- } from "./chunk-5OIVTEF7.js";
94
+ } from "./chunk-OOXIMURX.js";
95
95
  import {
96
96
  deletionDrawing,
97
97
  deletionEdges
@@ -192,24 +192,40 @@ function drawTubeMap(ctx, picture, frame) {
192
192
  }
193
193
  const stroke = frame.darkMode ? "#d0d0d0" : "#000000";
194
194
  const fill = frame.darkMode ? "rgba(40,40,40,0.4)" : "rgba(255,255,255,0.4)";
195
- ctx.lineWidth = 2 * Math.max(0.25, Math.min(1, frame.yScale));
195
+ ctx.lineWidth = outlineWidth(frame);
196
196
  for (const node of picture.nodes) {
197
- if (visible(node)) {
198
- const lit = node.name === frame.highlightNode;
197
+ if (visible(node) && node.name !== frame.highlightNode) {
199
198
  ctx.beginPath();
200
199
  trace(ctx, node.commands, x, y);
201
- const tint = lit ? void 0 : frame.nodeColors?.get(node.name);
202
- ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : tint ?? fill;
203
- ctx.strokeStyle = lit ? "#ff0000" : stroke;
200
+ const tint = frame.nodeColors?.get(node.name);
201
+ ctx.fillStyle = tint ?? fill;
202
+ ctx.strokeStyle = stroke;
204
203
  ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
205
204
  ctx.fill();
206
- ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0));
205
+ ctx.globalAlpha = outlineAlpha(x(node.x1) - x(node.x0));
207
206
  ctx.stroke();
208
207
  ctx.globalAlpha = 1;
209
208
  }
210
209
  }
210
+ drawTubeMapHighlight(ctx, picture, frame);
211
211
  drawMismatches(ctx, picture.mismatches, frame);
212
212
  }
213
+ function outlineWidth(frame) {
214
+ return 2 * Math.max(0.25, Math.min(1, frame.yScale));
215
+ }
216
+ function drawTubeMapHighlight(ctx, picture, frame) {
217
+ const node = frame.highlightNode ? picture.nodes.find((n) => n.name === frame.highlightNode) : void 0;
218
+ if (node) {
219
+ ctx.beginPath();
220
+ trace(ctx, node.commands, frame.x, frame.y);
221
+ ctx.globalAlpha = 1;
222
+ ctx.lineWidth = outlineWidth(frame);
223
+ ctx.fillStyle = "rgba(255,192,203,0.5)";
224
+ ctx.strokeStyle = "#ff0000";
225
+ ctx.fill();
226
+ ctx.stroke();
227
+ }
228
+ }
213
229
  var MISMATCH_FONT_PX = 12;
214
230
  var MIN_MISMATCH_FONT_PX = 6;
215
231
  function mismatchesLegible(yScale) {
@@ -273,6 +289,7 @@ export {
273
289
  trackColors,
274
290
  tubeMapPicture,
275
291
  drawTubeMap,
292
+ drawTubeMapHighlight,
276
293
  mismatchesLegible,
277
294
  mismatchOnScreen,
278
295
  tubeMapTrackAt,
@@ -0,0 +1,273 @@
1
+ import {
2
+ bubbleSubgraph
3
+ } from "./chunk-VHW7FDHU.js";
4
+ import {
5
+ coversGraph
6
+ } from "./chunk-2AWUWGAD.js";
7
+ import {
8
+ bubblesFromGraph,
9
+ walkIndexOf,
10
+ walkRoutes
11
+ } from "./chunk-GOZYKNQ2.js";
12
+ import {
13
+ bubbleSegmentIds
14
+ } from "./chunk-XKWD2RY3.js";
15
+ import {
16
+ SATURATED_PATH_COUNT
17
+ } from "./chunk-FX4RDMDN.js";
18
+ import {
19
+ strandSides
20
+ } from "./chunk-2CVHXFK7.js";
21
+ import {
22
+ isBackbone
23
+ } from "./chunk-FYQWPEEY.js";
24
+
25
+ // src/bubbles/superbubbles.ts
26
+ var MAX_SUPERBUBBLES = 50;
27
+ function slotGraph(graph, index) {
28
+ const slots = graph.nodes.length * 2;
29
+ const seen = /* @__PURE__ */ new Set();
30
+ const pairs = [];
31
+ const degree = new Int32Array(slots);
32
+ for (const e of graph.edges) {
33
+ const a = index.get(e.from);
34
+ const b = index.get(e.to);
35
+ if (a === void 0 || b === void 0) {
36
+ continue;
37
+ }
38
+ const sides = strandSides(e);
39
+ const ka = a * 2 + (sides.from === "end" ? 1 : 0);
40
+ const kb = b * 2 + (sides.to === "end" ? 1 : 0);
41
+ const id = Math.min(ka, kb) * slots + Math.max(ka, kb);
42
+ if (seen.has(id)) {
43
+ continue;
44
+ }
45
+ seen.add(id);
46
+ pairs.push(ka, kb);
47
+ degree[ka]++;
48
+ if (ka !== kb) {
49
+ degree[kb]++;
50
+ }
51
+ }
52
+ const offset = new Int32Array(slots + 1);
53
+ for (let k = 0; k < slots; k++) {
54
+ offset[k + 1] = offset[k] + degree[k];
55
+ }
56
+ const next = new Int32Array(offset[slots]);
57
+ const fill = offset.slice(0, slots);
58
+ for (let i = 0; i < pairs.length; i += 2) {
59
+ const ka = pairs[i];
60
+ const kb = pairs[i + 1];
61
+ next[fill[ka]++] = kb;
62
+ if (ka !== kb) {
63
+ next[fill[kb]++] = ka;
64
+ }
65
+ }
66
+ return { offset, next };
67
+ }
68
+ function searcher(nodes, offset, next) {
69
+ const visited = new Int32Array(nodes);
70
+ const pending = new Uint8Array(nodes * 2);
71
+ const stack = new Int32Array(nodes * 2);
72
+ const inside = new Int32Array(nodes);
73
+ const touched = [];
74
+ let stamp = 0;
75
+ return (start) => {
76
+ const s = start >> 1;
77
+ stamp++;
78
+ touched.length = 0;
79
+ let open = 0;
80
+ let top = 0;
81
+ let count = 0;
82
+ let found;
83
+ const expect = (k) => {
84
+ if (!pending[k]) {
85
+ pending[k] = 1;
86
+ open++;
87
+ touched.push(k);
88
+ }
89
+ };
90
+ expect(start);
91
+ stack[top++] = start;
92
+ search: while (top > 0) {
93
+ const k = stack[--top];
94
+ const v = k >> 1;
95
+ visited[v] = stamp;
96
+ if (v !== s) {
97
+ inside[count++] = k;
98
+ }
99
+ if (pending[k]) {
100
+ pending[k] = 0;
101
+ open--;
102
+ }
103
+ if (offset[k] === offset[k + 1]) {
104
+ break;
105
+ }
106
+ for (let p = offset[k]; p < offset[k + 1]; p++) {
107
+ const entry = next[p];
108
+ const u = entry >> 1;
109
+ if (u === s) {
110
+ break search;
111
+ }
112
+ expect(entry ^ 1);
113
+ if (visited[u] === stamp) {
114
+ continue;
115
+ }
116
+ let ready = true;
117
+ for (let q = offset[entry]; q < offset[entry + 1]; q++) {
118
+ if (visited[next[q] >> 1] !== stamp) {
119
+ ready = false;
120
+ break;
121
+ }
122
+ }
123
+ if (ready) {
124
+ stack[top++] = entry ^ 1;
125
+ }
126
+ }
127
+ if (top === 1 && open === 1) {
128
+ if (count > 0) {
129
+ found = {
130
+ source: start,
131
+ sink: stack[0],
132
+ inside: inside.slice(0, count)
133
+ };
134
+ }
135
+ break;
136
+ }
137
+ }
138
+ for (const k of touched) {
139
+ pending[k] = 0;
140
+ }
141
+ return found;
142
+ };
143
+ }
144
+ function reversed(f) {
145
+ return {
146
+ source: f.sink ^ 1,
147
+ sink: f.source ^ 1,
148
+ inside: f.inside.map((k) => k ^ 1).reverse()
149
+ };
150
+ }
151
+ var collator = new Intl.Collator(void 0, { numeric: true });
152
+ function onReference(a, b) {
153
+ return isBackbone(a) && isBackbone(b) && a.stable.refName === b.stable.refName;
154
+ }
155
+ function forward(f, nodes) {
156
+ const a = nodes[f.source >> 1];
157
+ const b = nodes[f.sink >> 1];
158
+ const byRef = onReference(a, b) ? a.stable.start - b.stable.start : 0;
159
+ const bySide = ((f.sink ^ 1) & 1) - (f.source & 1);
160
+ return (byRef || bySide || collator.compare(a.name, b.name)) < 0;
161
+ }
162
+ function nodesOf(f) {
163
+ return [f.source >> 1, ...Array.from(f.inside, (k) => k >> 1), f.sink >> 1];
164
+ }
165
+ function allSuperbubbles(graph) {
166
+ const { nodes } = graph;
167
+ const index = new Map(nodes.map((n, i) => [n.id, i]));
168
+ const { offset, next } = slotGraph(graph, index);
169
+ const find = searcher(nodes.length, offset, next);
170
+ const byEnds = /* @__PURE__ */ new Map();
171
+ for (let k = 0; k < nodes.length * 2; k++) {
172
+ const hit = find(k);
173
+ if (!hit) {
174
+ continue;
175
+ }
176
+ const a = hit.source >> 1;
177
+ const b = hit.sink >> 1;
178
+ const id = Math.min(a, b) * nodes.length + Math.max(a, b);
179
+ if (forward(hit, nodes) || !byEnds.has(id)) {
180
+ byEnds.set(id, hit);
181
+ }
182
+ }
183
+ const found = [...byEnds.values()].map(
184
+ (f) => forward(f, nodes) ? f : reversed(f)
185
+ );
186
+ return { found, offset, next };
187
+ }
188
+ function routesThrough(nodes, offset, next, f) {
189
+ const sink = f.sink >> 1;
190
+ const best = /* @__PURE__ */ new Map([
191
+ [f.source >> 1, { min: 0, max: 0, n: 1 }]
192
+ ]);
193
+ for (const k of [f.source, ...f.inside]) {
194
+ const cur = best.get(k >> 1);
195
+ if (!cur) {
196
+ continue;
197
+ }
198
+ for (let p = offset[k]; p < offset[k + 1]; p++) {
199
+ const u = next[p] >> 1;
200
+ const add = u === sink ? 0 : nodes[u].length;
201
+ const prev = best.get(u);
202
+ best.set(u, {
203
+ min: Math.min(prev?.min ?? Infinity, cur.min + add),
204
+ max: Math.max(prev?.max ?? -Infinity, cur.max + add),
205
+ n: Math.min(SATURATED_PATH_COUNT, cur.n + (prev?.n ?? 0))
206
+ });
207
+ }
208
+ }
209
+ return best.get(sink) ?? { min: 0, max: 0, n: 0 };
210
+ }
211
+ function superbubblesFromGraph(graph, span) {
212
+ const { nodes } = graph;
213
+ const { found, offset, next } = allSuperbubbles(graph);
214
+ const covers = found.map((f) => coversGraph(f.inside.length, nodes.length));
215
+ const members = found.map(nodesOf);
216
+ const holders = /* @__PURE__ */ new Map();
217
+ members.forEach((vs, i) => {
218
+ for (const v of vs) {
219
+ ;
220
+ (holders.get(v) ?? holders.set(v, []).get(v)).push(i);
221
+ }
222
+ });
223
+ const nested = (f, i) => holders.get(f.inside[0] >> 1).some(
224
+ (j) => j !== i && !covers[j] && members[j].length > members[i].length && members[i].every((v) => holders.get(v).includes(j))
225
+ );
226
+ const placed = (f) => span !== void 0 || onReference(nodes[f.source >> 1], nodes[f.sink >> 1]);
227
+ const outermost = found.filter((f, i) => !covers[i] && placed(f) && !nested(f, i)).sort((a, b) => b.inside.length - a.inside.length).slice(0, MAX_SUPERBUBBLES);
228
+ const byId = new Map(nodes.map((n) => [n.id, n]));
229
+ const walkIndex = walkIndexOf(graph);
230
+ return outermost.map((f) => {
231
+ const source = nodes[f.source >> 1];
232
+ const sink = nodes[f.sink >> 1];
233
+ const walked = walkIndex && walkRoutes(graph, byId, walkIndex, source.id, sink.id);
234
+ const crossed = walked?.n ? walked : void 0;
235
+ const routes = crossed ?? routesThrough(nodes, offset, next, f);
236
+ const placedOn = onReference(source, sink);
237
+ return {
238
+ refName: placedOn ? source.stable.refName : span.refName,
239
+ start: placedOn ? source.stable.start + source.length : span.start,
240
+ end: placedOn ? sink.stable.start : span.end,
241
+ segmentCount: f.inside.length + 2,
242
+ pathCount: routes.n,
243
+ inversion: false,
244
+ shortestAlleleLength: routes.min,
245
+ longestAlleleLength: routes.max,
246
+ segments: nodesOf(f).map((v) => nodes[v].name).join(","),
247
+ shortestAllele: void 0,
248
+ longestAllele: void 0,
249
+ partial: walked !== void 0 && walked.left > 0,
250
+ routes: crossed?.routes,
251
+ key: `${source.name}>${sink.name}`,
252
+ ...placedOn ? {} : { offReference: true }
253
+ };
254
+ }).sort(
255
+ (a, b) => a.start - b.start || a.end - b.end || collator.compare(a.key, b.key)
256
+ );
257
+ }
258
+ function graphBubbles(graph, span) {
259
+ const n = graph.nodes.length;
260
+ const reach = bubblesFromGraph(graph);
261
+ return reach.length === 0 ? superbubblesFromGraph(graph, span) : reach.flatMap(
262
+ (b) => coversGraph(b.segmentCount - 2, n) ? superbubblesFromGraph(
263
+ b.segmentCount === n ? graph : bubbleSubgraph(graph, bubbleSegmentIds(b)),
264
+ b
265
+ ) : [b]
266
+ );
267
+ }
268
+
269
+ export {
270
+ MAX_SUPERBUBBLES,
271
+ superbubblesFromGraph,
272
+ graphBubbles
273
+ };
@@ -19,7 +19,10 @@ function parseBubbleLine(line) {
19
19
  };
20
20
  }
21
21
  function sameBubble(a, b) {
22
- return a.refName === b.refName && a.start === b.start && a.end === b.end;
22
+ return a.key !== void 0 || b.key !== void 0 ? a.key === b.key : a.refName === b.refName && a.start === b.start && a.end === b.end;
23
+ }
24
+ function bubbleKey(b) {
25
+ return b.key ?? `${b.refName}:${b.start}-${b.end}`;
23
26
  }
24
27
  function bubbleLabel(bubble) {
25
28
  const { shortestAlleleLength: min, longestAlleleLength: max } = bubble;
@@ -38,6 +41,7 @@ function bubbleDescription(bubble) {
38
41
  export {
39
42
  parseBubbleLine,
40
43
  sameBubble,
44
+ bubbleKey,
41
45
  bubbleLabel,
42
46
  SATURATED_PATH_COUNT,
43
47
  bubbleDescription
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SATURATED_PATH_COUNT
3
- } from "./chunk-MFKORG3F.js";
3
+ } from "./chunk-FX4RDMDN.js";
4
4
  import {
5
5
  referenceOrder
6
6
  } from "./chunk-HNQPEMYE.js";
@@ -38,15 +38,7 @@ function bubblesFromGraph(graph) {
38
38
  const backbone = graph.nodes.filter(isBackbone).sort((a, b) => a.stable.start - b.stable.start);
39
39
  const ends = /* @__PURE__ */ new Set([backbone[0].id, backbone.at(-1).id]);
40
40
  const boundaries = backbone.map((node) => [node, indexOf.get(node.id)]).filter(([node, i]) => ends.has(node.id) || reach[i] === 0).sort(([a, ia], [b, ib]) => ia - ib || a.stable.start - b.stable.start);
41
- const walkIndex = graph.paths?.map((p) => {
42
- const at = /* @__PURE__ */ new Map();
43
- p.nodeIds.forEach((id, i) => {
44
- if (!at.has(id)) {
45
- at.set(id, i);
46
- }
47
- });
48
- return at;
49
- });
41
+ const walkIndex = walkIndexOf(graph);
50
42
  const bubbles = [];
51
43
  for (let i = 0; i + 1 < boundaries.length; i++) {
52
44
  const [start, i0] = boundaries[i];
@@ -112,6 +104,17 @@ function bubblesFromGraph(graph) {
112
104
  }
113
105
  return bubbles;
114
106
  }
107
+ function walkIndexOf(graph) {
108
+ return graph.paths?.map((p) => {
109
+ const at = /* @__PURE__ */ new Map();
110
+ p.nodeIds.forEach((id, i) => {
111
+ if (!at.has(id)) {
112
+ at.set(id, i);
113
+ }
114
+ });
115
+ return at;
116
+ });
117
+ }
115
118
  function walkRoutes(graph, byId, walkIndex, startId, endId) {
116
119
  const seen = /* @__PURE__ */ new Map();
117
120
  let min = Infinity;
@@ -145,5 +148,7 @@ function walkRoutes(graph, byId, walkIndex, startId, endId) {
145
148
  }
146
149
 
147
150
  export {
148
- bubblesFromGraph
151
+ bubblesFromGraph,
152
+ walkIndexOf,
153
+ walkRoutes
149
154
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  clampZoom
3
- } from "./chunk-5OIVTEF7.js";
3
+ } from "./chunk-OOXIMURX.js";
4
4
 
5
5
  // src/viewport.ts
6
6
  function axisScaleOf(scale, pixelRows) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  engineKey,
3
3
  forceLayout
4
- } from "./chunk-5OIVTEF7.js";
4
+ } from "./chunk-OOXIMURX.js";
5
5
 
6
6
  // src/layout/forceCache.ts
7
7
  var DEFAULT_SIZE = 4;
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var version = "9.0.0";
2
+ var version = "9.1.0";
3
3
 
4
4
  export {
5
5
  version