@jbrowse/bandage-core 9.9.2 → 10.0.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 (79) hide show
  1. package/README.md +3 -0
  2. package/dist/bubbles/popBubble.js +3 -2
  3. package/dist/bubbles/superbubbles.js +5 -4
  4. package/dist/chunks/{chunk-YAN2ZPNV.js → chunk-33T67FPZ.js} +1 -1
  5. package/dist/chunks/{chunk-TOVOJNT7.js → chunk-3APK25GI.js} +1 -1
  6. package/dist/chunks/{chunk-A6PSWTLX.js → chunk-4QTNXTGW.js} +1 -1
  7. package/dist/chunks/{chunk-ROQGMTFB.js → chunk-BEWLQADP.js} +6 -6
  8. package/dist/chunks/{chunk-BQ33OOLC.js → chunk-CDPYXHVR.js} +1 -1
  9. package/dist/chunks/{chunk-YS7VA5SI.js → chunk-DCJC32QW.js} +1 -1
  10. package/dist/chunks/{chunk-YGN4KL7W.js → chunk-E5UEYZWK.js} +1 -1
  11. package/dist/chunks/{chunk-IGJCTPFB.js → chunk-EMUUNWRU.js} +52 -43
  12. package/dist/chunks/{chunk-PIN7ZMTZ.js → chunk-EU6FBTQF.js} +4 -4
  13. package/dist/chunks/{chunk-WVBYR6Y7.js → chunk-FG762CCP.js} +3 -8
  14. package/dist/chunks/{chunk-73RSLGZT.js → chunk-FLFBYSJO.js} +1 -1
  15. package/dist/chunks/{chunk-IJRNMO4D.js → chunk-ICXMWYOA.js} +5 -5
  16. package/dist/chunks/{chunk-HOZI3FBG.js → chunk-IM4SJUPO.js} +12 -22
  17. package/dist/chunks/{chunk-6PYZYBBF.js → chunk-KAN2WTG5.js} +1 -1
  18. package/dist/chunks/{chunk-RK3MF4FD.js → chunk-KJYSOHVN.js} +1 -1
  19. package/dist/chunks/{chunk-F4BKWWLS.js → chunk-NP356MC2.js} +4 -4
  20. package/dist/chunks/{chunk-KJEMKHVB.js → chunk-OEPCRURR.js} +1 -1
  21. package/dist/chunks/{chunk-ZBLKAWSD.js → chunk-OOLNLCIO.js} +1 -1
  22. package/dist/chunks/chunk-OPPVSPEF.js +57 -0
  23. package/dist/chunks/{chunk-OA7RIMZ2.js → chunk-PSMKPSO4.js} +89 -22
  24. package/dist/chunks/chunk-Q6IOQP4J.js +292 -0
  25. package/dist/chunks/{chunk-LTUWJKHD.js → chunk-Q7LYHGGQ.js} +114 -6
  26. package/dist/chunks/{chunk-EE37PVQY.js → chunk-QIVPRZ4H.js} +12 -7
  27. package/dist/chunks/{chunk-J7YKQ5YW.js → chunk-QXAB2FLJ.js} +1 -1
  28. package/dist/chunks/chunk-RAEABQNW.js +227 -0
  29. package/dist/chunks/{chunk-EDASNMLY.js → chunk-REE4GUDM.js} +4 -4
  30. package/dist/chunks/{chunk-IZ3W5IU2.js → chunk-RJXPPYA7.js} +36 -36
  31. package/dist/chunks/{chunk-F5BYHLIN.js → chunk-VOWI544A.js} +1 -1
  32. package/dist/chunks/{chunk-P6O4RSOQ.js → chunk-VZNKA3K2.js} +5 -5
  33. package/dist/chunks/{chunk-FPTI3WAV.js → chunk-W4GZCVUK.js} +18 -13
  34. package/dist/chunks/{chunk-MECWJY2R.js → chunk-WI7ZHJ27.js} +5 -5
  35. package/dist/cli/figure.js +50 -48
  36. package/dist/cli/fold.js +8 -6
  37. package/dist/figure.js +31 -29
  38. package/dist/figureData.js +63 -0
  39. package/dist/figureSettings.js +9 -7
  40. package/dist/gbzCut.js +3 -2
  41. package/dist/gbzWindow.js +6 -1
  42. package/dist/gfa/gfaConverter.js +3 -2
  43. package/dist/gfa/graphTables.js +2 -1
  44. package/dist/index.js +173 -156
  45. package/dist/layout/forceCache.js +9 -7
  46. package/dist/layout/smoothChains.js +6 -0
  47. package/dist/layout/trimToWindow.js +17 -23
  48. package/dist/layout/tubeMapLayout.js +5 -4
  49. package/dist/layout/walkRowDraw.js +10 -9
  50. package/dist/layout/walkRowLayout.js +4 -3
  51. package/dist/layout/walkRows.js +3 -2
  52. package/dist/layout/walkStrip.js +11 -10
  53. package/dist/layoutModes.js +11 -10
  54. package/dist/pathAnchoring.js +2 -1
  55. package/dist/pathColors.js +4 -3
  56. package/dist/pathVisits.js +8 -0
  57. package/dist/pipeline.js +8 -6
  58. package/dist/referenceRampCss.js +8 -7
  59. package/dist/referenceStrip.js +8 -7
  60. package/dist/renderer/GeometryBuilder.js +7 -6
  61. package/dist/tubeMap/bundle.js +6 -5
  62. package/dist/tubeMap/coarsen.js +17 -15
  63. package/dist/tubeMap/draw.js +4 -3
  64. package/dist/tubeMap/nodeColors.js +8 -7
  65. package/dist/types/figureData.d.ts +146 -0
  66. package/dist/types/gbzWindow.d.ts +15 -1
  67. package/dist/types/gfa/gfaConverter.d.ts +1 -1
  68. package/dist/types/index.d.ts +4 -1
  69. package/dist/types/layout/deletionRoutes.d.ts +1 -1
  70. package/dist/types/layout/smoothChains.d.ts +2 -0
  71. package/dist/types/pathAnchoring.d.ts +3 -2
  72. package/dist/types/pathVisits.d.ts +64 -0
  73. package/dist/types/types.d.ts +2 -1
  74. package/dist/types/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/dist/viewport.js +9 -7
  77. package/dist/walkEncoding.js +8 -7
  78. package/dist/walkHighlight.js +9 -8
  79. package/package.json +1 -1
@@ -1,3 +1,6 @@
1
+ import {
2
+ PathVisits
3
+ } from "./chunk-RAEABQNW.js";
1
4
  import {
2
5
  panSNSample
3
6
  } from "./chunk-BPLIXMH7.js";
@@ -7,6 +10,32 @@ function walkFields(panSN) {
7
10
  const [sample = "", haplotype = "", ...contig] = panSN.split("#");
8
11
  return { sample, haplotype, contig: contig.join("#") };
9
12
  }
13
+ function namesByList(lists, names, listCount, pathNames) {
14
+ const starts = new Int32Array(listCount + 1);
15
+ for (const list of lists) {
16
+ starts[list + 1]++;
17
+ }
18
+ for (let i = 0; i < listCount; i++) {
19
+ starts[i + 1] += starts[i];
20
+ }
21
+ const fill = starts.slice(0, listCount);
22
+ const grouped = new Int32Array(lists.length);
23
+ lists.forEach((list, i) => {
24
+ grouped[fill[list]++] = names[i];
25
+ });
26
+ const listed = new Int32Array(pathNames.length).fill(-1);
27
+ return Array.from({ length: listCount }, (_, list) => {
28
+ const out = [];
29
+ for (let i = starts[list]; i < starts[list + 1]; i++) {
30
+ const k = grouped[i];
31
+ if (listed[k] !== list) {
32
+ listed[k] = list;
33
+ out.push(pathNames[k]);
34
+ }
35
+ }
36
+ return out;
37
+ });
38
+ }
10
39
  function graphFromTables({ nodes: n, links: l, walks: w }, name = "Imported GFA") {
11
40
  const count = n.names.length;
12
41
  const declared = n.lengths.length;
@@ -95,27 +124,54 @@ function graphFromTables({ nodes: n, links: l, walks: w }, name = "Imported GFA"
95
124
  }
96
125
  edgeLists.push(list);
97
126
  }
127
+ const adjacency = new Int32Array(count + 1);
128
+ for (let a = 0; a < count; a++) {
129
+ adjacency[a + 1] = adjacency[a] + (neighbours[a]?.length ?? 0) / 2;
130
+ }
131
+ const adjacentNode = new Int32Array(adjacency[count]);
132
+ const adjacentList = new Int32Array(adjacency[count]);
133
+ neighbours.forEach((near, a) => {
134
+ for (let j = 0; j < near.length; j += 2) {
135
+ adjacentNode[adjacency[a] + j / 2] = near[j];
136
+ adjacentList[adjacency[a] + j / 2] = near[j + 1];
137
+ }
138
+ });
139
+ const listOf = (a, b) => {
140
+ for (let j = adjacency[a]; j < adjacency[a + 1]; j++) {
141
+ if (adjacentNode[j] === b) {
142
+ return adjacentList[j];
143
+ }
144
+ }
145
+ return -1;
146
+ };
98
147
  const nameIndex = /* @__PURE__ */ new Map();
99
148
  const pathNames = [];
100
- const crossing = Array.from({ length: lists }, () => []);
149
+ const crossedList = new Int32Array(w.offsets[w.names.length] ?? 0);
150
+ const crossedName = new Int32Array(crossedList.length);
151
+ let crossings = 0;
101
152
  const lastWalk = new Int32Array(lists).fill(-1);
102
153
  const paths = [];
103
154
  const anchorPaths = [];
104
- const visits = new Array(count);
105
- const filled = new Int32Array(count);
155
+ const totalSteps = w.offsets[w.names.length] ?? 0;
156
+ const visitPath = new Int32Array(totalSteps);
157
+ const visitStart = new Float64Array(totalSteps);
158
+ const visitReversed = new Uint8Array(totalSteps);
159
+ const fill = new Int32Array(count).fill(-1);
106
160
  const visitOrder = [];
161
+ const blocks = [0];
162
+ const pathSamples = [];
107
163
  for (const p of drawnWalks) {
108
164
  const { sample, haplotype, contig } = walkFields(w.names[p]);
109
165
  const pathName = `${sample}#${+haplotype}#${contig}`;
110
166
  let named = nameIndex.get(pathName);
111
- const repeat = named !== void 0;
112
167
  if (named === void 0) {
113
168
  named = pathNames.length;
114
169
  nameIndex.set(pathName, named);
115
170
  pathNames.push(pathName);
171
+ pathSamples.push(panSNSample(pathName));
116
172
  }
117
173
  const start = w.starts[p];
118
- const visitSample = panSNSample(pathName);
174
+ const visitSample = pathSamples[named];
119
175
  const first = w.offsets[p];
120
176
  const nodeIds = new Array(w.offsets[p + 1] - first);
121
177
  let pos = start;
@@ -123,25 +179,22 @@ function graphFromTables({ nodes: n, links: l, walks: w }, name = "Imported GFA"
123
179
  for (let s = first; s < w.offsets[p + 1]; s++) {
124
180
  const node = w.steps[s];
125
181
  nodeIds[s - first] = ids[node];
126
- if (filled[node] === 0) {
127
- visits[node] = new Array(traversals[node]);
182
+ if (fill[node] === -1) {
183
+ fill[node] = blocks.at(-1);
184
+ blocks.push(fill[node] + traversals[node]);
128
185
  visitOrder.push(node);
129
186
  }
130
- visits[node][filled[node]++] = {
131
- path: pathName,
132
- sample: visitSample,
133
- start: pos,
134
- strand: w.reversed[s] ? "-" : "+"
135
- };
187
+ const at = fill[node]++;
188
+ visitPath[at] = named;
189
+ visitStart[at] = pos;
190
+ visitReversed[at] = w.reversed[s];
136
191
  pos += node < declared ? n.lengths[node] : 0;
137
192
  if (prev >= 0) {
138
- const list = pairList(prev, node);
139
- if (list !== void 0 && lastWalk[list] !== p) {
193
+ const list = listOf(prev, node);
194
+ if (list >= 0 && lastWalk[list] !== p) {
140
195
  lastWalk[list] = p;
141
- const names = crossing[list];
142
- if (!repeat || !names.includes(named)) {
143
- names.push(named);
144
- }
196
+ crossedList[crossings] = list;
197
+ crossedName[crossings++] = named;
145
198
  }
146
199
  }
147
200
  prev = node;
@@ -161,10 +214,16 @@ function graphFromTables({ nodes: n, links: l, walks: w }, name = "Imported GFA"
161
214
  length: pos - start
162
215
  });
163
216
  }
217
+ const listNames = namesByList(
218
+ crossedList.subarray(0, crossings),
219
+ crossedName,
220
+ lists,
221
+ pathNames
222
+ );
164
223
  edges.forEach((edge, i) => {
165
- const names = crossing[edgeLists[i]];
224
+ const names = listNames[edgeLists[i]];
166
225
  if (names.length > 0) {
167
- edge.pathIds = names.map((k) => pathNames[k]);
226
+ edge.pathIds = names;
168
227
  }
169
228
  });
170
229
  const walked = drawnWalks.length > 0;
@@ -174,7 +233,15 @@ function graphFromTables({ nodes: n, links: l, walks: w }, name = "Imported GFA"
174
233
  edges,
175
234
  paths: walked ? paths : void 0,
176
235
  anchorPaths: walked ? anchorPaths : void 0,
177
- pathVisits: walked ? new Map(visitOrder.map((node) => [n.names[node], visits[node]])) : void 0,
236
+ pathVisits: walked ? new PathVisits({
237
+ segments: visitOrder.map((node) => n.names[node]),
238
+ offsets: Int32Array.from(blocks),
239
+ path: visitPath.subarray(0, blocks.at(-1)),
240
+ start: visitStart.subarray(0, blocks.at(-1)),
241
+ reversed: visitReversed.subarray(0, blocks.at(-1)),
242
+ paths: pathNames,
243
+ samples: pathSamples
244
+ }) : void 0,
178
245
  anchoredBy: nodes.some((node) => node.stable) ? "tags" : void 0
179
246
  };
180
247
  }
@@ -0,0 +1,292 @@
1
+ import {
2
+ layoutEngine
3
+ } from "./chunk-VK6XMP6B.js";
4
+ import {
5
+ layoutModeByValue
6
+ } from "./chunk-ICXMWYOA.js";
7
+ import {
8
+ parseRegion
9
+ } from "./chunk-FLFBYSJO.js";
10
+ import {
11
+ layoutGeometryInputs
12
+ } from "./chunk-LOCJN3BZ.js";
13
+ import {
14
+ version
15
+ } from "./chunk-CDPYXHVR.js";
16
+ import {
17
+ axisScaleOf
18
+ } from "./chunk-OOLNLCIO.js";
19
+ import {
20
+ facetLifts,
21
+ walkLift
22
+ } from "./chunk-QIVPRZ4H.js";
23
+ import {
24
+ walkKey
25
+ } from "./chunk-2LQ5BWT7.js";
26
+ import {
27
+ buildGeometry,
28
+ computeReferenceRamp
29
+ } from "./chunk-NP356MC2.js";
30
+ import {
31
+ abgrAlpha,
32
+ abgrBlue,
33
+ abgrGreen,
34
+ abgrRed
35
+ } from "./chunk-G2FD7S3C.js";
36
+ import {
37
+ pathLegend,
38
+ walkLabelsOf
39
+ } from "./chunk-REE4GUDM.js";
40
+ import {
41
+ drawingBounds,
42
+ engineSettingsOf,
43
+ fitTransform,
44
+ forceLayout,
45
+ loadGraph
46
+ } from "./chunk-W4GZCVUK.js";
47
+ import {
48
+ referenceLabel
49
+ } from "./chunk-RAKOB6IV.js";
50
+ import {
51
+ resolveColorScheme
52
+ } from "./chunk-JKJWSY4V.js";
53
+ import {
54
+ deletionDrawing,
55
+ deletionEdges
56
+ } from "./chunk-N6FECAXP.js";
57
+ import {
58
+ isBackbone
59
+ } from "./chunk-FYQWPEEY.js";
60
+ import {
61
+ facetCells,
62
+ facetSettingOf
63
+ } from "./chunk-XV4P5KEQ.js";
64
+
65
+ // src/figureData.ts
66
+ function columns(keys) {
67
+ const out = Object.fromEntries(
68
+ keys.map((k) => [k, []])
69
+ );
70
+ return {
71
+ out,
72
+ push(row) {
73
+ for (const k of keys) {
74
+ out[k].push(row[k]);
75
+ }
76
+ }
77
+ };
78
+ }
79
+ var hex = (v) => v.toString(16).padStart(2, "0");
80
+ var cssHex = (c) => `#${hex(abgrRed(c))}${hex(abgrGreen(c))}${hex(abgrBlue(c))}`;
81
+ var alphaOf = (c) => abgrAlpha(c) / 255;
82
+ function figureData(graph, layout, o = {}) {
83
+ if (layout.tubeMap) {
84
+ throw new Error("figure data has no tables for a tube map yet");
85
+ }
86
+ const width = o.width ?? 1200;
87
+ const height = o.height ?? 800;
88
+ const region = o.region;
89
+ const pixelRows = layout.pixelRows ?? false;
90
+ const bounds = drawingBounds(layout, {
91
+ region: o.fitToDrawing ? void 0 : region
92
+ });
93
+ const fit = fitTransform(bounds, width, height, pixelRows);
94
+ if (!fit) {
95
+ throw new Error("the layout drew nothing to fit");
96
+ }
97
+ const axis = axisScaleOf(fit.scale, pixelRows);
98
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
99
+ const colorScheme = resolveColorScheme(o.colorScheme ?? "auto", graph);
100
+ const rampDomain = o.colorDomain ?? region;
101
+ const referenceRamp = colorScheme === "reference-position" ? computeReferenceRamp(graph, rampDomain) : void 0;
102
+ const drawnDeletions = deletionDrawing(
103
+ graph,
104
+ deletionEdges(graph),
105
+ o.showDeletionEdges
106
+ );
107
+ const layers = o.walks ?? [];
108
+ const walkRamp = layers.some((l) => l.color?.field === "reference") ? referenceRamp ?? computeReferenceRamp(graph, rampDomain) : void 0;
109
+ const lift = layers.length > 0 ? walkLift(graph, layers, walkRamp) : void 0;
110
+ const facet = facetSettingOf(o.facet);
111
+ const facetted = facet.field && lift && lift.walks.length > 1 ? facetLifts(graph, lift, layers, walkRamp) : void 0;
112
+ const labels = walkLabelsOf(
113
+ graph.paths?.length ? pathLegend(graph.paths) : []
114
+ );
115
+ const labelOf = (name) => labels.get(name) ?? name;
116
+ const referenceName = o.referenceName ?? referenceLabel(graph, region);
117
+ const reference = lift?.referenceDomain && {
118
+ ...lift.referenceDomain,
119
+ name: referenceName
120
+ };
121
+ const panels = [];
122
+ if (facetted) {
123
+ const place = facetCells(
124
+ facetted.map((p) => p.walks[0].name),
125
+ facet.field || "walk",
126
+ facet.domain
127
+ );
128
+ const ordered = facetted.map((panel, i) => ({ panel, cell: place.cells[i] })).sort((a, b) => a.cell - b.cell);
129
+ for (const { panel } of ordered) {
130
+ const walk = panel.walks[0];
131
+ const key = walkKey(walk, reference);
132
+ panels.push({
133
+ highlight: panel,
134
+ name: labelOf(walk.name),
135
+ key: key.delta + key.outside + key.reversed,
136
+ range: key.scale ?? ""
137
+ });
138
+ }
139
+ } else {
140
+ panels.push({ highlight: lift, name: "", key: "", range: "" });
141
+ }
142
+ const nodes = columns([
143
+ "panel",
144
+ "node",
145
+ "stroke",
146
+ "x",
147
+ "y",
148
+ "color",
149
+ "alpha",
150
+ "width"
151
+ ]);
152
+ const edges = columns([
153
+ "panel",
154
+ "edge",
155
+ "curve",
156
+ "x0",
157
+ "y0",
158
+ "cx0",
159
+ "cy0",
160
+ "cx1",
161
+ "cy1",
162
+ "x1",
163
+ "y1",
164
+ "color",
165
+ "alpha",
166
+ "width",
167
+ "deletion"
168
+ ]);
169
+ const arrows = columns([
170
+ "panel",
171
+ "edge",
172
+ "x",
173
+ "y",
174
+ "angle",
175
+ "length",
176
+ "halfWidth",
177
+ "color",
178
+ "alpha"
179
+ ]);
180
+ for (const { highlight, name: panel } of panels) {
181
+ const batch = buildGeometry({
182
+ ...layoutGeometryInputs(layout, drawnDeletions),
183
+ graph,
184
+ nodeById,
185
+ colorScheme,
186
+ contigThickness: o.contigThickness ?? 6,
187
+ connectorThickness: o.connectorThickness ?? 2,
188
+ drawPaths: false,
189
+ nodeWidth: o.nodeWidth ?? "depth",
190
+ linearLayout: o.linearLayout,
191
+ highlight,
192
+ axis,
193
+ referenceRamp
194
+ });
195
+ for (const [node, run] of batch.nodeStrokeRuns) {
196
+ for (let i = run.start; i < run.start + run.count; i++) {
197
+ const s = batch.nodeStrokes[i];
198
+ for (const p of s.points) {
199
+ nodes.push({
200
+ panel,
201
+ node,
202
+ stroke: i,
203
+ x: p.x,
204
+ y: p.y,
205
+ color: cssHex(s.color),
206
+ alpha: alphaOf(s.color),
207
+ width: s.thickness * 2
208
+ });
209
+ }
210
+ }
211
+ }
212
+ for (const [edge, run] of batch.edgeCurveRuns) {
213
+ for (let i = run.start; i < run.start + run.count; i++) {
214
+ const b = batch.edgeCurves[i];
215
+ for (const c of b.curves) {
216
+ edges.push({
217
+ panel,
218
+ edge,
219
+ curve: i,
220
+ ...c,
221
+ color: cssHex(b.color),
222
+ alpha: alphaOf(b.color),
223
+ width: b.thickness * 2,
224
+ deletion: drawnDeletions.bypassed.has(edge)
225
+ });
226
+ }
227
+ }
228
+ }
229
+ for (const [edge, run] of batch.arrowRuns) {
230
+ for (let i = run.start; i < run.start + run.count; i++) {
231
+ const a = batch.arrows[i];
232
+ arrows.push({
233
+ panel,
234
+ edge,
235
+ x: a.x,
236
+ y: a.y,
237
+ angle: a.angle,
238
+ length: a.length,
239
+ halfWidth: a.halfWidth,
240
+ color: cssHex(a.color),
241
+ alpha: alphaOf(a.color)
242
+ });
243
+ }
244
+ }
245
+ }
246
+ return {
247
+ meta: {
248
+ generator: `@jbrowse/bandage-core@${version}`,
249
+ width,
250
+ scaleX: axis.scaleX,
251
+ scaleY: axis.scaleY,
252
+ pixelRows,
253
+ referenceAxis: layout.referenceAxis ?? false,
254
+ colorScheme,
255
+ referenceName: referenceName ?? null
256
+ },
257
+ panels: {
258
+ panel: panels.map((p) => p.name),
259
+ key: panels.map((p) => p.key),
260
+ range: panels.map((p) => p.range)
261
+ },
262
+ nodes: nodes.out,
263
+ edges: edges.out,
264
+ arrows: arrows.out,
265
+ nodeInfo: {
266
+ node: graph.nodes.map((n) => n.id),
267
+ name: graph.nodes.map((n) => n.name),
268
+ length: graph.nodes.map((n) => n.length),
269
+ depth: graph.nodes.map((n) => n.depth),
270
+ backbone: graph.nodes.map((n) => isBackbone(n))
271
+ },
272
+ rowLabels: {
273
+ label: (layout.rowLabels ?? []).map((r) => r.label),
274
+ y: (layout.rowLabels ?? []).map((r) => r.y)
275
+ }
276
+ };
277
+ }
278
+ async function gfaFigureData(text, name, spec = {}) {
279
+ const region = spec.region ? parseRegion(spec.region) : void 0;
280
+ const graph = loadGraph(text, name, { referencePath: spec.referencePath });
281
+ const layout = layoutModeByValue(spec.layout ?? "force").run(graph, region) ?? (await forceLayout(graph, engineSettingsOf(spec), layoutEngine)).result;
282
+ return figureData(graph, layout, {
283
+ ...spec,
284
+ region,
285
+ walks: spec.walks?.map((w) => typeof w === "string" ? { walk: w } : w)
286
+ });
287
+ }
288
+
289
+ export {
290
+ figureData,
291
+ gfaFigureData
292
+ };
@@ -4,12 +4,16 @@ import {
4
4
  import {
5
5
  wellKnownSample
6
6
  } from "./chunk-RAKOB6IV.js";
7
+ import {
8
+ gfaTables
9
+ } from "./chunk-G45YY72K.js";
7
10
  import {
8
11
  panSNMatchesPrefix,
9
12
  panSNSample
10
13
  } from "./chunk-BPLIXMH7.js";
11
14
 
12
15
  // src/gbzWindow.ts
16
+ import { isReverse, nodeId } from "@gmod/gbz-base";
13
17
  var NoReferenceSampleError = class extends Error {
14
18
  constructor(anchor, referenceSamples) {
15
19
  super(
@@ -82,13 +86,12 @@ var GBZ_CUT_DEFAULTS = {
82
86
  snarls: "contained",
83
87
  limit: 1e5
84
88
  };
85
- async function cutWindowGFA(db, query, start, end, opts) {
86
- if (!query) {
87
- return "";
88
- }
89
- const subgraphs = await db.getSubgraphs({ ...opts, path: query, start, end, haplotypes: "all" }).catch((error) => {
89
+ async function windowCuts(db, query, start, end, opts) {
90
+ return db.getSubgraphs({ ...opts, path: query, start, end, haplotypes: "all" }).catch((error) => {
90
91
  throw nodeLimitError(error, opts.limit, end - start) ?? error;
91
92
  });
93
+ }
94
+ async function joinedGFA(db, subgraphs) {
92
95
  const paths = await db.paths();
93
96
  return joinCuts(
94
97
  await Promise.all(subgraphs.map((subgraph) => subgraph.toGFA())),
@@ -97,6 +100,109 @@ async function cutWindowGFA(db, query, start, end, opts) {
97
100
  )
98
101
  );
99
102
  }
103
+ async function cutWindowGFA(db, query, start, end, opts) {
104
+ return query ? joinedGFA(db, await windowCuts(db, query, start, end, opts)) : "";
105
+ }
106
+ async function cutWindowTables(db, query, start, end, opts) {
107
+ if (!query) {
108
+ return "";
109
+ }
110
+ const subgraphs = await windowCuts(db, query, start, end, opts);
111
+ if (subgraphs.length === 1) {
112
+ return compactTables(subgraphs[0].toCompactSubgraph());
113
+ }
114
+ const text = await joinedGFA(db, subgraphs);
115
+ return text && gfaTables(text) || text;
116
+ }
117
+ function segmentIndex(ids, names) {
118
+ let low = Infinity;
119
+ let high = -Infinity;
120
+ for (const id of ids) {
121
+ low = Math.min(low, id);
122
+ high = Math.max(high, id);
123
+ }
124
+ const span = high - low + 1;
125
+ const table = ids.length > 0 && span <= 16 * ids.length ? new Int32Array(span).fill(-1) : void 0;
126
+ const extra = /* @__PURE__ */ new Map();
127
+ ids.forEach((id, i) => {
128
+ if (table) {
129
+ table[id - low] = i;
130
+ } else {
131
+ extra.set(id, i);
132
+ }
133
+ });
134
+ return (handle) => {
135
+ const id = nodeId(handle);
136
+ const tabled = table !== void 0 && id >= low && id <= high;
137
+ const i = tabled ? table[id - low] : extra.get(id) ?? -1;
138
+ if (i >= 0) {
139
+ return i;
140
+ }
141
+ if (tabled) {
142
+ table[id - low] = names.length;
143
+ } else {
144
+ extra.set(id, names.length);
145
+ }
146
+ names.push(String(id));
147
+ return names.length - 1;
148
+ };
149
+ }
150
+ var WALK_NAME = /^(.*)\[(\d+)-(\d+)\]$/;
151
+ function compactTables(cut) {
152
+ const names = Array.from(cut.nodeIds, String);
153
+ const indexOf = segmentIndex(cut.nodeIds, names);
154
+ const declared = cut.nodeIds.length;
155
+ const linkCount = cut.edges.length / 2;
156
+ const from = new Int32Array(linkCount);
157
+ const to = new Int32Array(linkCount);
158
+ const strands = new Uint8Array(linkCount);
159
+ for (let k = 0; k < linkCount; k++) {
160
+ const a = cut.edges[2 * k];
161
+ const b = cut.edges[2 * k + 1];
162
+ from[k] = indexOf(a);
163
+ to[k] = indexOf(b);
164
+ strands[k] = (isReverse(a) ? 1 : 0) | (isReverse(b) ? 2 : 0);
165
+ }
166
+ const walks = cut.paths.filter((path) => path.steps.length > 0);
167
+ const offsets = new Int32Array(walks.length + 1);
168
+ walks.forEach((walk, p) => {
169
+ offsets[p + 1] = offsets[p] + walk.steps.length;
170
+ });
171
+ const steps = new Int32Array(offsets[walks.length]);
172
+ const reversed = new Uint8Array(steps.length);
173
+ const starts = new Float64Array(walks.length);
174
+ const ends = new Float64Array(walks.length);
175
+ const walkNames = walks.map((walk, p) => {
176
+ const named = WALK_NAME.exec(walk.name);
177
+ starts[p] = named ? +named[2] : 0;
178
+ ends[p] = named ? +named[3] : -1;
179
+ let at = offsets[p];
180
+ for (const handle of walk.steps) {
181
+ steps[at] = indexOf(handle);
182
+ reversed[at++] = isReverse(handle) ? 1 : 0;
183
+ }
184
+ return named ? named[1] : walk.name;
185
+ });
186
+ return {
187
+ nodes: {
188
+ names,
189
+ lengths: Int32Array.from(cut.nodeSequences, (s) => s.length),
190
+ refs: new Int32Array(declared).fill(-1),
191
+ starts: new Float64Array(declared),
192
+ ranks: new Int32Array(declared),
193
+ refNames: []
194
+ },
195
+ links: { from, to, strands },
196
+ walks: {
197
+ names: walkNames,
198
+ starts,
199
+ ends,
200
+ offsets,
201
+ steps,
202
+ reversed
203
+ }
204
+ };
205
+ }
100
206
 
101
207
  export {
102
208
  NoReferenceSampleError,
@@ -108,5 +214,7 @@ export {
108
214
  referencePathQuery,
109
215
  nodeLimitError,
110
216
  GBZ_CUT_DEFAULTS,
111
- cutWindowGFA
217
+ cutWindowGFA,
218
+ cutWindowTables,
219
+ compactTables
112
220
  };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  encodedColor,
3
3
  resolveEncoding
4
- } from "./chunk-F5BYHLIN.js";
4
+ } from "./chunk-VOWI544A.js";
5
5
  import {
6
6
  pathOrigin
7
- } from "./chunk-IGJCTPFB.js";
7
+ } from "./chunk-EMUUNWRU.js";
8
8
  import {
9
9
  panSNContig
10
10
  } from "./chunk-BPLIXMH7.js";
@@ -90,14 +90,19 @@ function reversedNodes(graph, walk) {
90
90
  if (!referencePath || walk.reference) {
91
91
  return { reversed, bp };
92
92
  }
93
- const walkPath = pathOrigin(walk.name).name;
93
+ const visits = graph.pathVisits;
94
+ if (!visits) {
95
+ return { reversed, bp };
96
+ }
97
+ const own = visits.pathIndex(pathOrigin(walk.name).name);
98
+ const theirs = visits.pathIndex(referencePath);
94
99
  const byId = new Map(graph.nodes.map((n) => [n.id, n]));
95
100
  for (const id of walk.progress.keys()) {
96
101
  const node = byId.get(id);
97
- const visits = node ? graph.pathVisits?.get(node.name) : void 0;
98
- const own = visits?.find((v) => v.path === walkPath)?.strand;
99
- const theirs = visits?.find((v) => v.path === referencePath)?.strand;
100
- if (own && theirs && own !== theirs) {
102
+ const slot = node ? visits.slot(node.name) : -1;
103
+ const a = slot < 0 ? -1 : visits.firstBy(slot, own);
104
+ const b = slot < 0 ? -1 : visits.firstBy(slot, theirs);
105
+ if (a >= 0 && b >= 0 && visits.reversed[a] !== visits.reversed[b]) {
101
106
  reversed.add(id);
102
107
  bp += node.length;
103
108
  }
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  pathOrigin,
6
6
  surveyPaths
7
- } from "./chunk-IGJCTPFB.js";
7
+ } from "./chunk-EMUUNWRU.js";
8
8
 
9
9
  // src/gfa/gfaConverter.ts
10
10
  function surveySegments(gfaGraph) {