@jbrowse/bandage-core 8.2.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 (61) 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-GQ2AOHU4.js → chunk-2ZT7ZF7A.js} +13 -31
  8. package/dist/chunks/{chunk-DE4UFDUZ.js → chunk-3TGUKQWQ.js} +4 -4
  9. package/dist/chunks/{chunk-KCRL5JB2.js → chunk-75HR3756.js} +55 -17
  10. package/dist/chunks/{chunk-DRA7733Q.js → chunk-BPRXQ2SS.js} +8 -3
  11. package/dist/chunks/{chunk-TEDQJPMI.js → chunk-CWFNEKST.js} +22 -7
  12. package/dist/chunks/chunk-FUVI4WQP.js +273 -0
  13. package/dist/chunks/{chunk-MFKORG3F.js → chunk-FX4RDMDN.js} +5 -1
  14. package/dist/chunks/{chunk-4S47XRYA.js → chunk-GOZYKNQ2.js} +16 -11
  15. package/dist/chunks/chunk-INJ2UVOT.js +370 -0
  16. package/dist/chunks/{chunk-TL4B342F.js → chunk-MXDQGA7A.js} +1 -1
  17. package/dist/chunks/{chunk-SKKYLBRA.js → chunk-RRZMMYBG.js} +1 -1
  18. package/dist/chunks/{chunk-3FXPFVKJ.js → chunk-UID3QBWH.js} +1 -1
  19. package/dist/chunks/{chunk-2WNBVB55.js → chunk-WBCOREDZ.js} +121 -74
  20. package/dist/chunks/{chunk-OWFPAIJI.js → chunk-WEDZ76AK.js} +1 -1
  21. package/dist/chunks/{chunk-CF62U3QS.js → chunk-WNUZP3WQ.js} +2 -2
  22. package/dist/chunks/{chunk-WOO7QYKB.js → chunk-XKWD2RY3.js} +8 -2
  23. package/dist/chunks/{chunk-6VBDMZBZ.js → chunk-XLCTQHBE.js} +1 -1
  24. package/dist/chunks/{chunk-733L2TS6.js → chunk-XXT2JCAC.js} +10 -7
  25. package/dist/cli/figure.js +9 -9
  26. package/dist/figure.js +6 -6
  27. package/dist/figureSettings.js +3 -3
  28. package/dist/index.js +45 -33
  29. package/dist/layout/forceCache.js +3 -3
  30. package/dist/layout/tubeMapLayout.js +12 -6
  31. package/dist/layoutModes.js +3 -3
  32. package/dist/pipeline.js +2 -2
  33. package/dist/renderer/Canvas2DRenderer.js +1 -1
  34. package/dist/renderer/recordingCanvas.js +12 -1
  35. package/dist/tubeMap/axis.js +1 -1
  36. package/dist/tubeMap/coarsen.js +11 -23
  37. package/dist/tubeMap/connectors.js +3 -4
  38. package/dist/tubeMap/deviations.js +15 -19
  39. package/dist/tubeMap/draw.js +11 -5
  40. package/dist/tubeMap/genes.js +2 -2
  41. package/dist/tubeMap/mismatches.js +1 -1
  42. package/dist/tubeMap/reads.js +1 -1
  43. package/dist/types/bubbles/bubbleHalos.d.ts +2 -0
  44. package/dist/types/bubbles/bubbleLine.d.ts +3 -0
  45. package/dist/types/bubbles/bubblesFromGraph.d.ts +12 -2
  46. package/dist/types/bubbles/superbubbles.d.ts +5 -0
  47. package/dist/types/index.d.ts +5 -4
  48. package/dist/types/layout/tubeMapLayout.d.ts +15 -2
  49. package/dist/types/layoutModes.d.ts +5 -5
  50. package/dist/types/renderer/Canvas2DRenderer.d.ts +6 -3
  51. package/dist/types/tubeMap/axis.d.ts +6 -3
  52. package/dist/types/tubeMap/connectors.d.ts +3 -11
  53. package/dist/types/tubeMap/deviations.d.ts +2 -3
  54. package/dist/types/tubeMap/draw.d.ts +5 -1
  55. package/dist/types/tubeMap/mismatches.d.ts +1 -1
  56. package/dist/types/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/dist/viewport.js +3 -3
  59. package/package.json +1 -1
  60. package/dist/chunks/chunk-W2I4DOL4.js +0 -230
  61. 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
  };
@@ -1,35 +1,14 @@
1
- import {
2
- isBackbone
3
- } from "./chunk-FYQWPEEY.js";
4
-
5
1
  // src/tubeMap/connectors.ts
6
- function referenceNodes(graph, layout) {
7
- const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
8
- const out = [];
9
- layout.nodes.forEach((node) => {
10
- const graphNode = nodeById.get(node.name);
11
- if (node.order >= 0 && graphNode && isBackbone(graphNode)) {
12
- out.push({
13
- node: node.name,
14
- bp0: graphNode.stable.start,
15
- bp1: graphNode.stable.start + node.sequenceLength,
16
- x0: node.x,
17
- x1: node.x + node.pixelWidth
18
- });
19
- }
20
- });
21
- return out;
22
- }
23
- function tubeMapConnectors(nodes, bpToScreen, tubeToScreen, width) {
2
+ function tubeMapConnectors(boxes, bpToScreen, tubeToScreen, width) {
24
3
  const out = [];
25
- for (const n of nodes) {
26
- const bottom0 = tubeToScreen(n.x0);
27
- const bottom1 = tubeToScreen(n.x1);
4
+ for (const box of boxes) {
5
+ const bottom0 = tubeToScreen(box.x0);
6
+ const bottom1 = tubeToScreen(box.x1);
28
7
  if (bottom1 >= 0 && bottom0 <= width) {
29
8
  out.push({
30
- node: n.node,
31
- top0: bpToScreen(n.bp0),
32
- top1: bpToScreen(n.bp1),
9
+ node: box.name,
10
+ top0: bpToScreen(box.bp0),
11
+ top1: bpToScreen(box.bp1),
33
12
  bottom0,
34
13
  bottom1
35
14
  });
@@ -90,7 +69,10 @@ function drawTubeMapConnectors(ctx, connectors, bottom, {
90
69
  ctx.strokeStyle = `rgba(${ink},0.35)`;
91
70
  ctx.lineWidth = 0.5;
92
71
  ctx.stroke();
93
- 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;
94
76
  if (lit) {
95
77
  ctx.beginPath();
96
78
  band(ctx, lit, bottom);
@@ -105,8 +87,8 @@ function drawTubeMapConnectors(ctx, connectors, bottom, {
105
87
  }
106
88
 
107
89
  export {
108
- referenceNodes,
109
90
  tubeMapConnectors,
110
91
  connectorAt,
111
- drawTubeMapConnectors
92
+ drawTubeMapConnectors,
93
+ drawLitConnector
112
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-3FXPFVKJ.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
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  tubeMapMismatches
3
- } from "./chunk-733L2TS6.js";
3
+ } from "./chunk-XXT2JCAC.js";
4
4
  import {
5
5
  readColor
6
- } from "./chunk-TEDQJPMI.js";
6
+ } from "./chunk-CWFNEKST.js";
7
7
 
8
8
  // src/tubeMap/draw.ts
9
9
  import { curvePaths, nodeOutlinePath } from "@jbrowse/tubemap-core";
@@ -53,9 +53,9 @@ function extentOf(commands) {
53
53
  }
54
54
  return { x0, x1 };
55
55
  }
56
- function shapeOf(d, color, track) {
56
+ function shapeOf(d, color, id, haplotype) {
57
57
  const commands = parsePath(d);
58
- return { commands, color, track, ...extentOf(commands) };
58
+ return { commands, color, id, haplotype, ...extentOf(commands) };
59
59
  }
60
60
  function trackColors(layout, pathColors) {
61
61
  return new Map(
@@ -67,20 +67,21 @@ function trackColors(layout, pathColors) {
67
67
  }
68
68
  function layerOf(layout, type, colors) {
69
69
  const { shapes } = layout;
70
+ const haplotype = type === "haplotype";
70
71
  const colorOf = (s) => colors.get(s.id);
71
- const trackOf = (s) => type === "haplotype" ? s.id : void 0;
72
72
  const rects = [...shapes.rectangles, ...shapes.verticalRectangles].filter((r) => r.type === type).map((r) => ({
73
73
  color: colorOf(r),
74
- track: trackOf(r),
74
+ id: r.id,
75
+ haplotype,
75
76
  x0: r.xStart,
76
77
  x1: r.xEnd + 1,
77
78
  y0: r.yStart,
78
79
  y1: r.yEnd + 1
79
80
  }));
80
81
  const curves = curvePaths(shapes.curves, type).map(
81
- (c) => shapeOf(c.path, colorOf(c), trackOf(c))
82
+ (c) => shapeOf(c.path, colorOf(c), c.id, haplotype)
82
83
  );
83
- const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, colorOf(c), trackOf(c)));
84
+ const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, colorOf(c), c.id, haplotype));
84
85
  return { rects, shapes: [...curves, ...corners] };
85
86
  }
86
87
  function tubeMapPicture({
@@ -177,7 +178,7 @@ var NODE_TINT_ALPHA = 0.55;
177
178
  function drawTubeMap(ctx, picture, frame) {
178
179
  const { x, y, width } = frame;
179
180
  const visible = (item) => x(item.x1) >= 0 && x(item.x0) <= width;
180
- const colorOf = (item) => item.track !== void 0 && frame.tubeColors?.[item.track] || item.color;
181
+ const colorOf = (item) => item.haplotype && frame.tubeColors?.[item.id] || item.color;
181
182
  ctx.globalAlpha = 1;
182
183
  for (const layer of picture.layers) {
183
184
  fillByColor(ctx, layer.rects, visible, colorOf, (r) => {
@@ -191,24 +192,40 @@ function drawTubeMap(ctx, picture, frame) {
191
192
  }
192
193
  const stroke = frame.darkMode ? "#d0d0d0" : "#000000";
193
194
  const fill = frame.darkMode ? "rgba(40,40,40,0.4)" : "rgba(255,255,255,0.4)";
194
- ctx.lineWidth = 2 * Math.max(0.25, Math.min(1, frame.yScale));
195
+ ctx.lineWidth = outlineWidth(frame);
195
196
  for (const node of picture.nodes) {
196
- if (visible(node)) {
197
- const lit = node.name === frame.highlightNode;
197
+ if (visible(node) && node.name !== frame.highlightNode) {
198
198
  ctx.beginPath();
199
199
  trace(ctx, node.commands, x, y);
200
- const tint = lit ? void 0 : frame.nodeColors?.get(node.name);
201
- ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : tint ?? fill;
202
- ctx.strokeStyle = lit ? "#ff0000" : stroke;
200
+ const tint = frame.nodeColors?.get(node.name);
201
+ ctx.fillStyle = tint ?? fill;
202
+ ctx.strokeStyle = stroke;
203
203
  ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
204
204
  ctx.fill();
205
- ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0));
205
+ ctx.globalAlpha = outlineAlpha(x(node.x1) - x(node.x0));
206
206
  ctx.stroke();
207
207
  ctx.globalAlpha = 1;
208
208
  }
209
209
  }
210
+ drawTubeMapHighlight(ctx, picture, frame);
210
211
  drawMismatches(ctx, picture.mismatches, frame);
211
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
+ }
212
229
  var MISMATCH_FONT_PX = 12;
213
230
  var MIN_MISMATCH_FONT_PX = 6;
214
231
  function mismatchesLegible(yScale) {
@@ -235,6 +252,7 @@ function drawMismatches(ctx, marks, frame) {
235
252
  }
236
253
  ctx.fill();
237
254
  ctx.font = `${fontPx}px monospace`;
255
+ ctx.textAlign = "left";
238
256
  ctx.textBaseline = "alphabetic";
239
257
  ctx.fillStyle = darkMode ? "#ffffff" : "#000000";
240
258
  for (const m of marks) {
@@ -248,12 +266,32 @@ function drawMismatches(ctx, marks, frame) {
248
266
  }
249
267
  }
250
268
  }
269
+ function tubeMapTrackAt(picture, { x, y }, sx, sy) {
270
+ for (let l = picture.layers.length - 1; l >= 0; l--) {
271
+ for (const r of picture.layers[l].rects) {
272
+ if (x(r.x0) <= sx && sx <= x(r.x1) && y(r.y0) <= sy && sy <= y(r.y1)) {
273
+ return r.id;
274
+ }
275
+ }
276
+ }
277
+ return void 0;
278
+ }
279
+ var INSERTION_SLOP_PX = 4;
280
+ function tubeMapMismatchAt(picture, frame, sx, sy) {
281
+ const { x, y, yScale } = frame;
282
+ return mismatchesLegible(yScale) ? picture.mismatches.find(
283
+ (m) => y(m.y) <= sy && sy <= y(m.y + m.height) && (m.kind === "insertion" ? Math.abs(x(m.x) - sx) <= INSERTION_SLOP_PX : x(m.x0) <= sx && sx <= x(m.x1))
284
+ ) : void 0;
285
+ }
251
286
 
252
287
  export {
253
288
  parsePath,
254
289
  trackColors,
255
290
  tubeMapPicture,
256
291
  drawTubeMap,
292
+ drawTubeMapHighlight,
257
293
  mismatchesLegible,
258
- mismatchOnScreen
294
+ mismatchOnScreen,
295
+ tubeMapTrackAt,
296
+ tubeMapMismatchAt
259
297
  };
@@ -6,17 +6,22 @@ import {
6
6
  } from "./chunk-FYQWPEEY.js";
7
7
 
8
8
  // src/tubeMap/axis.ts
9
- function referenceBoxes(graph, layout) {
9
+ function referenceBoxes({
10
+ graph,
11
+ layout
12
+ }) {
10
13
  const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
11
14
  const byRefName = /* @__PURE__ */ new Map();
12
- layout.nodes.forEach((node) => {
15
+ layout.nodes.forEach((node, index) => {
13
16
  const graphNode = nodeById.get(node.name);
14
17
  if (node.order >= 0 && graphNode && isBackbone(graphNode)) {
15
18
  const { refName, start } = graphNode.stable;
16
19
  const boxes = byRefName.get(refName) ?? byRefName.set(refName, []).get(refName);
17
20
  boxes.push({
21
+ name: node.name,
22
+ index,
18
23
  bp0: start,
19
- bp1: start + node.sequenceLength,
24
+ bp1: start + graphNode.length,
20
25
  x0: node.x,
21
26
  x1: node.x + node.pixelWidth
22
27
  });
@@ -30,16 +30,30 @@ function mismatchesByStep(record, lengths) {
30
30
  return byStep;
31
31
  }
32
32
  const starts = stepStarts(lengths, record.pathLength);
33
+ const known = starts.flatMap((start, s) => start === void 0 ? [] : [s]);
34
+ const startOf = (k) => starts[known[k]];
35
+ const endOf = (k) => startOf(k) + lengths[known[k]];
36
+ let first = 0;
33
37
  const stepAt = (pos2, atEnd = false) => {
34
- for (let s = 0; s < starts.length; s++) {
35
- const start = starts[s];
36
- const length = lengths[s];
37
- if (start !== void 0 && length !== void 0 && start <= pos2 && (pos2 < start + length || atEnd && pos2 === start + length)) {
38
- return { s, local: pos2 - start, room: start + length - pos2 };
38
+ while (first < known.length && endOf(first) < pos2) {
39
+ first++;
40
+ }
41
+ for (let k = first; k < known.length && startOf(k) <= pos2; k++) {
42
+ const end = endOf(k);
43
+ if (pos2 < end || atEnd && pos2 === end) {
44
+ return { s: known[k], local: pos2 - startOf(k), room: end - pos2 };
39
45
  }
40
46
  }
41
47
  return void 0;
42
48
  };
49
+ const unplacedFrom = (pos2) => {
50
+ for (let k = first; k < known.length; k++) {
51
+ if (startOf(k) > pos2) {
52
+ return startOf(k) - pos2;
53
+ }
54
+ }
55
+ return Infinity;
56
+ };
43
57
  const add = (s, mismatch) => {
44
58
  const list = byStep[s];
45
59
  const last = list.at(-1);
@@ -68,7 +82,7 @@ function mismatchesByStep(record, lengths) {
68
82
  let left = op.length;
69
83
  while (left > 0) {
70
84
  const at = stepAt(pos);
71
- const take = at ? Math.min(left, at.room) : left;
85
+ const take = Math.min(left, at ? at.room : unplacedFrom(pos));
72
86
  if (at) {
73
87
  add(at.s, { type: "deletion", pos: at.local, length: take });
74
88
  }
@@ -106,7 +120,8 @@ function tubeMapReads(graph, records, firstId) {
106
120
  })),
107
121
  firstNodeOffset: first === 0 ? record.pathStart : 0,
108
122
  finalNodeCoverLength: last === record.path.length - 1 ? lastLength - (record.pathLength - record.pathEnd) : lastLength,
109
- mapping_quality: record.mappingQuality === 255 ? 0 : record.mappingQuality,
123
+ // 255 is GAF's missing MAPQ
124
+ mapping_quality: record.mappingQuality === 255 ? void 0 : record.mappingQuality,
110
125
  is_secondary: record.secondary,
111
126
  is_reverse: record.strand === "-"
112
127
  });