@jbrowse/bandage-core 4.0.25 → 4.0.27

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.
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  HPRC_GBZ,
4
+ LAYOUT_MODES,
4
5
  cutGbzRegion,
5
6
  featuresOnBackbone,
6
7
  figureSvg,
@@ -9,15 +10,17 @@ import {
9
10
  genesFromGff3Lines,
10
11
  genesFromText,
11
12
  graphBackbone,
13
+ haplotypeIndexBeside,
12
14
  layoutModeByValue,
13
15
  loadBandage,
14
16
  loadGraph,
15
17
  openGbz,
16
- parseRegion
17
- } from "../chunks/chunk-B5ZHLU4N.js";
18
+ parseRegion,
19
+ refNameBinding
20
+ } from "../chunks/chunk-VNCGWA2N.js";
18
21
 
19
22
  // src/cli/figure.ts
20
- import { open, readFile, writeFile } from "node:fs/promises";
23
+ import { access, open, readFile, writeFile } from "node:fs/promises";
21
24
  import path from "node:path";
22
25
  import { parseArgs } from "node:util";
23
26
  import { gunzipSync } from "node:zlib";
@@ -27,16 +30,13 @@ function byteSource(location) {
27
30
  if (isUrl(location)) {
28
31
  return {
29
32
  async read(length, position) {
30
- const res = await fetch(location, {
33
+ const res = await fetchOk(location, {
31
34
  headers: { range: `bytes=${position}-${position + length - 1}` }
32
35
  });
33
- if (!res.ok) {
34
- throw new Error(`HTTP ${res.status} reading ${location}`);
35
- }
36
36
  return new Uint8Array(await res.arrayBuffer());
37
37
  },
38
38
  async stat() {
39
- const res = await fetch(location, { method: "HEAD" });
39
+ const res = await fetchOk(location, { method: "HEAD" });
40
40
  return { size: Number(res.headers.get("content-length")) };
41
41
  }
42
42
  };
@@ -53,8 +53,15 @@ function byteSource(location) {
53
53
  }
54
54
  };
55
55
  }
56
+ async function fetchOk(location, init) {
57
+ const res = await fetch(location, init);
58
+ if (!res.ok) {
59
+ throw new Error(`HTTP ${res.status} reading ${location}`);
60
+ }
61
+ return res;
62
+ }
56
63
  async function text(location) {
57
- const bytes = isUrl(location) ? new Uint8Array(await (await fetch(location)).arrayBuffer()) : await readFile(location);
64
+ const bytes = isUrl(location) ? new Uint8Array(await (await fetchOk(location)).arrayBuffer()) : await readFile(location);
58
65
  const gzipped = bytes[0] === 31 && bytes[1] === 139;
59
66
  return new TextDecoder().decode(gzipped ? gunzipSync(bytes) : bytes);
60
67
  }
@@ -63,30 +70,48 @@ async function readGenes(genes, graph, resolve) {
63
70
  if (!backbone) {
64
71
  return [];
65
72
  }
66
- const file = resolve(genes.file);
73
+ const bind = refNameBinding(
74
+ backbone.contigs.map((c) => c.refName),
75
+ genes.refNames
76
+ );
67
77
  const format = genes.format ?? (/\.bed(\.gz)?$/i.test(genes.file) ? "bed" : "gff3");
68
- if (!genes.index) {
69
- return featuresOnBackbone(genesFromText(await text(file)), backbone);
78
+ const { names, features } = genes.index ? await indexedGenes(
79
+ resolve(genes.file),
80
+ resolve(genes.index),
81
+ format,
82
+ backbone.contigs,
83
+ bind
84
+ ) : namedBy(genesFromText(await text(resolve(genes.file))));
85
+ if (!names.some((name) => bind(name) !== void 0)) {
86
+ const shown = names.slice(0, 4).join(", ");
87
+ console.warn(
88
+ `bandage-figure: no sequence in ${genes.file} names ${backbone.contigs.map((c) => c.contig).join(", ")}` + (names.length ? `; it has ${shown}${names.length > 4 ? ", \u2026" : ""}` : "") + `. Give the file's name under genes.refNames, as { "chr6": "NC_000006.12" }`
89
+ );
70
90
  }
71
- const index = resolve(genes.index);
91
+ return featuresOnBackbone(features, backbone, genes.refNames);
92
+ }
93
+ function namedBy(features) {
94
+ return { names: [...new Set(features.map((f) => f.refName))], features };
95
+ }
96
+ async function indexedGenes(file, index, format, contigs, bind) {
72
97
  const csi = index.endsWith(".csi");
73
98
  const tabix = new TabixIndexedFile(
74
99
  isUrl(file) ? { url: file, ...csi ? { csiUrl: index } : { tbiUrl: index } } : { path: file, ...csi ? { csiPath: index } : { tbiPath: index } }
75
100
  );
76
- const indexed = new Set(await tabix.getReferenceSequenceNames());
101
+ const names = await tabix.getReferenceSequenceNames();
77
102
  const lines = [];
78
- for (const contig of backbone.contigs) {
79
- const refName = [contig.contig, contig.refName].find((n) => indexed.has(n));
80
- if (refName !== void 0) {
81
- await tabix.getLines(refName, contig.start, contig.end, {
103
+ for (const contig of contigs) {
104
+ const name = names.find((n) => bind(n) === contig.refName);
105
+ if (name !== void 0) {
106
+ await tabix.getLines(name, contig.start, contig.end, {
82
107
  lineCallback: (line) => lines.push(line)
83
108
  });
84
109
  }
85
110
  }
86
- return featuresOnBackbone(
87
- format === "gff3" ? genesFromGff3Lines(lines) : genesFromBed(lines.join("\n")),
88
- backbone
89
- );
111
+ return {
112
+ names,
113
+ features: format === "gff3" ? genesFromGff3Lines(lines) : genesFromBed(lines.join("\n"))
114
+ };
90
115
  }
91
116
  var engine = async (request) => {
92
117
  const bandage = await loadBandage();
@@ -94,15 +119,39 @@ var engine = async (request) => {
94
119
  const result = bandage.computeLayout(request.graph, request.options);
95
120
  return { result, duration: performance.now() - start };
96
121
  };
122
+ async function exists(location) {
123
+ try {
124
+ await (isUrl(location) ? fetchOk(location, { method: "HEAD" }) : access(location));
125
+ return true;
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+ async function siblingIndex(db) {
131
+ const sibling = haplotypeIndexBeside(db);
132
+ return sibling && await exists(sibling) ? sibling : void 0;
133
+ }
134
+ var DRAWABLE = LAYOUT_MODES.filter((m) => m.drawsNodes).map(
135
+ (m) => m.value
136
+ );
137
+ function checkLayout(layout) {
138
+ if (layout !== void 0 && !DRAWABLE.includes(layout)) {
139
+ throw new Error(
140
+ `a figure draws no "${layout}" layout: one of ${DRAWABLE.join(", ")}`
141
+ );
142
+ }
143
+ }
97
144
  async function renderSpec(spec, base) {
145
+ checkLayout(spec.layout);
98
146
  const resolve = (location) => isUrl(location) ? location : path.resolve(base, location);
99
147
  let source;
100
148
  if (spec.gbz) {
101
149
  const { db, index } = spec.gbz.db === "hprc" ? HPRC_GBZ : spec.gbz;
150
+ const indexLocation = index ? resolve(index) : await siblingIndex(resolve(db));
102
151
  const cut = await cutGbzRegion(
103
152
  await openGbz(
104
153
  byteSource(resolve(db)),
105
- index ? byteSource(resolve(index)) : void 0
154
+ indexLocation ? byteSource(indexLocation) : void 0
106
155
  ),
107
156
  spec.gbz
108
157
  );
@@ -136,14 +185,32 @@ async function renderSpec(spec, base) {
136
185
  spec
137
186
  });
138
187
  }
188
+ var USAGE = "usage: bandage-figure <spec.json> [-o figure.svg]";
189
+ function args() {
190
+ try {
191
+ return parseArgs({
192
+ allowPositionals: true,
193
+ options: {
194
+ out: { type: "string", short: "o" },
195
+ help: { type: "boolean", short: "h" }
196
+ }
197
+ });
198
+ } catch (e) {
199
+ console.error(`${e instanceof Error ? e.message : String(e)}
200
+ ${USAGE}`);
201
+ process.exit(2);
202
+ }
203
+ }
139
204
  async function main() {
140
- const { positionals, values } = parseArgs({
141
- allowPositionals: true,
142
- options: { out: { type: "string", short: "o" } }
143
- });
205
+ const { positionals, values } = args();
206
+ if (values.help) {
207
+ process.stdout.write(`${USAGE}
208
+ `);
209
+ return;
210
+ }
144
211
  const file = positionals[0];
145
212
  if (!file) {
146
- console.error("usage: bandage-figure <spec.json> [-o figure.svg]");
213
+ console.error(USAGE);
147
214
  process.exit(2);
148
215
  }
149
216
  const spec = JSON.parse(await readFile(file, "utf8"));
@@ -154,4 +221,9 @@ async function main() {
154
221
  process.stdout.write(svg);
155
222
  }
156
223
  }
157
- await main();
224
+ try {
225
+ await main();
226
+ } catch (e) {
227
+ console.error(`bandage-figure: ${e instanceof Error ? e.message : String(e)}`);
228
+ process.exit(1);
229
+ }
package/dist/index.js CHANGED
@@ -90,6 +90,7 @@ import {
90
90
  svgCanvas,
91
91
  svgPath,
92
92
  translateCurves,
93
+ trimOrigins,
93
94
  version,
94
95
  viewportOf,
95
96
  walkHighlight,
@@ -101,7 +102,7 @@ import {
101
102
  wellKnownSample,
102
103
  yToXOf,
103
104
  zoomAbout
104
- } from "./chunks/chunk-B5ZHLU4N.js";
105
+ } from "./chunks/chunk-VNCGWA2N.js";
105
106
 
106
107
  // src/index.ts
107
108
  import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
@@ -721,14 +722,16 @@ function extentOf(commands) {
721
722
  }
722
723
  return { x0, x1 };
723
724
  }
724
- function shapeOf(d, color, alpha) {
725
+ function shapeOf(d, color, alpha, track) {
725
726
  const commands = parsePath(d);
726
- return { commands, color, alpha, ...extentOf(commands) };
727
+ return { commands, color, track, alpha, ...extentOf(commands) };
727
728
  }
728
729
  function layerOf(layout, type) {
729
730
  const { shapes } = layout;
731
+ const trackOf = (s) => type === "haplotype" ? s.id : void 0;
730
732
  const rects = [...shapes.rectangles, ...shapes.verticalRectangles].filter((r) => r.type === type).map((r) => ({
731
733
  color: r.color,
734
+ track: trackOf(r),
732
735
  alpha: r.alpha ?? 1,
733
736
  x0: r.xStart,
734
737
  x1: r.xEnd + 1,
@@ -736,9 +739,9 @@ function layerOf(layout, type) {
736
739
  y1: r.yEnd + 1
737
740
  }));
738
741
  const curves = curvePaths(shapes.curves, type).map(
739
- (c) => shapeOf(c.path, c.color, c.alpha ?? 1)
742
+ (c) => shapeOf(c.path, c.color, c.alpha ?? 1, trackOf(c))
740
743
  );
741
- const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, c.color, 1));
744
+ const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, c.color, 1, trackOf(c)));
742
745
  return { rects, shapes: [...curves, ...corners] };
743
746
  }
744
747
  function tubeMapPicture(layout) {
@@ -797,11 +800,11 @@ function trace(ctx, commands, x, y) {
797
800
  }
798
801
  }
799
802
  }
800
- function fillByColor(ctx, items, visible, addTo) {
803
+ function fillByColor(ctx, items, visible, colorOf, addTo) {
801
804
  const byColor = /* @__PURE__ */ new Map();
802
805
  for (const item of items) {
803
806
  if (visible(item)) {
804
- const key = `${item.color}|${item.alpha}`;
807
+ const key = `${colorOf(item)}|${item.alpha}`;
805
808
  const list = byColor.get(key);
806
809
  if (list) {
807
810
  list.push(item);
@@ -811,7 +814,7 @@ function fillByColor(ctx, items, visible, addTo) {
811
814
  }
812
815
  }
813
816
  for (const list of byColor.values()) {
814
- ctx.fillStyle = list[0].color;
817
+ ctx.fillStyle = colorOf(list[0]);
815
818
  ctx.globalAlpha = list[0].alpha;
816
819
  ctx.beginPath();
817
820
  for (const item of list) {
@@ -826,16 +829,18 @@ var MIN_OUTLINE_ALPHA = 0.15;
826
829
  function outlineAlpha(screenWidth) {
827
830
  return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX));
828
831
  }
832
+ var NODE_TINT_ALPHA = 0.55;
829
833
  function drawTubeMap(ctx, picture, frame) {
830
834
  const { x, y, width } = frame;
831
835
  const visible = (item) => x(item.x1) >= 0 && x(item.x0) <= width;
836
+ const colorOf = (item) => item.track !== void 0 && frame.tubeColors?.[item.track] || item.color;
832
837
  for (const layer of picture.layers) {
833
- fillByColor(ctx, layer.rects, visible, (r) => {
838
+ fillByColor(ctx, layer.rects, visible, colorOf, (r) => {
834
839
  const left = x(r.x0);
835
840
  const top = y(r.y0);
836
841
  ctx.rect(left, top, x(r.x1) - left, y(r.y1) - top);
837
842
  });
838
- fillByColor(ctx, layer.shapes, visible, (s) => {
843
+ fillByColor(ctx, layer.shapes, visible, colorOf, (s) => {
839
844
  trace(ctx, s.commands, x, y);
840
845
  });
841
846
  }
@@ -847,8 +852,10 @@ function drawTubeMap(ctx, picture, frame) {
847
852
  const lit = node.name === frame.highlightNode;
848
853
  ctx.beginPath();
849
854
  trace(ctx, node.commands, x, y);
850
- ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : fill;
855
+ const tint = lit ? void 0 : frame.nodeColors?.get(node.name);
856
+ ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : tint ?? fill;
851
857
  ctx.strokeStyle = lit ? "#ff0000" : stroke;
858
+ ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
852
859
  ctx.fill();
853
860
  ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0));
854
861
  ctx.stroke();
@@ -897,6 +904,21 @@ function drawMismatches(ctx, marks, frame) {
897
904
  }
898
905
  }
899
906
 
907
+ // src/tubeMap/nodeColors.ts
908
+ import { abgrToCssRgba as abgrToCssRgba2 } from "@jbrowse/core/util/colorBits";
909
+ function tubeMapNodeColors(graph, colorScheme, referenceRamp) {
910
+ if (colorScheme === "uniform" || colorScheme === "grey") {
911
+ return void 0;
912
+ }
913
+ const range = { ...computeColorSchemeRange(graph), referenceRamp };
914
+ return new Map(
915
+ graph.nodes.map((node, index) => [
916
+ node.id,
917
+ abgrToCssRgba2(getNodeColor(node, index, colorScheme, range))
918
+ ])
919
+ );
920
+ }
921
+
900
922
  // src/tubeMap/warp.ts
901
923
  var MAX_GAP_PX = 24;
902
924
  var isReal = (c) => c.bp1 > c.bp0;
@@ -1696,13 +1718,22 @@ function bubbleSubgraph(graph, segmentIds) {
1696
1718
  const keep = new Set(segmentIds);
1697
1719
  const nodes = graph.nodes.filter((n) => keep.has(n.name));
1698
1720
  const ids = new Set(nodes.map((n) => n.id));
1699
- const paths = graph.paths?.map((p) => ({ ...p, nodeIds: p.nodeIds.filter((id) => ids.has(id)) })).filter((p) => p.nodeIds.length > 0);
1721
+ const kept = /* @__PURE__ */ new Map();
1722
+ const paths = graph.paths?.flatMap((p) => {
1723
+ const first = p.nodeIds.findIndex((id) => ids.has(id));
1724
+ if (first < 0) {
1725
+ return [];
1726
+ }
1727
+ const nodeIds = p.nodeIds.filter((id) => ids.has(id));
1728
+ kept.set(p.name, { first, nodeIds });
1729
+ return [{ ...p, nodeIds }];
1730
+ });
1700
1731
  return {
1701
1732
  name: graph.name,
1702
1733
  nodes,
1703
1734
  edges: graph.edges.filter((e) => ids.has(e.from) && ids.has(e.to)),
1704
1735
  ...paths?.length ? { paths } : {},
1705
- anchorPaths: graph.anchorPaths,
1736
+ anchorPaths: trimOrigins(graph, kept),
1706
1737
  pathVisits: graph.pathVisits,
1707
1738
  anchoredBy: graph.anchoredBy,
1708
1739
  referencePath: graph.referencePath
@@ -1815,6 +1846,7 @@ export {
1815
1846
  tubeMapFrame,
1816
1847
  tubeMapGenes,
1817
1848
  tubeMapNodeAt,
1849
+ tubeMapNodeColors,
1818
1850
  tubeMapPicture,
1819
1851
  tubeX,
1820
1852
  version,
@@ -16,5 +16,6 @@ export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region' | 'ge
16
16
  file: string;
17
17
  index?: string;
18
18
  format?: 'gff3' | 'bed';
19
+ refNames?: Record<string, string>;
19
20
  };
20
21
  }
@@ -20,6 +20,11 @@ export interface FigureOptions {
20
20
  start: number;
21
21
  end: number;
22
22
  };
23
+ colorDomain?: {
24
+ start: number;
25
+ end: number;
26
+ };
27
+ fitToDrawing?: boolean;
23
28
  genes?: GeneModel[];
24
29
  spec?: unknown;
25
30
  }
@@ -13,6 +13,7 @@ export declare const HPRC_GBZ: {
13
13
  db: string;
14
14
  index: string;
15
15
  };
16
+ export declare function haplotypeIndexBeside(db: string): string | undefined;
16
17
  export declare function parseRegion(text: string): {
17
18
  refName: string;
18
19
  start: number;
@@ -17,6 +17,7 @@ export interface GraphLabel {
17
17
  kind: 'node' | 'deletion';
18
18
  leader?: Leader;
19
19
  }
20
+ export declare function groupDigits(n: number): string;
20
21
  export declare function formatBp(bp: number): string;
21
22
  interface SizeLabelArgs {
22
23
  nodePositions: Record<string, NodeSegment[]>;
@@ -20,6 +20,7 @@ export { REFERENCE_STRIP_ZONE_PX, drawReferenceStrip, referenceStripBlocks, stri
20
20
  export type { StripBlock } from './referenceStrip.js';
21
21
  export { drawTubeMap, tubeMapPicture } from './tubeMap/draw.js';
22
22
  export type { TubeMapFrame, TubeMapPicture } from './tubeMap/draw.js';
23
+ export { tubeMapNodeColors } from './tubeMap/nodeColors.js';
23
24
  export { referenceKnots, warpX } from './tubeMap/warp.js';
24
25
  export { tubeMapFrame, tubeMapNodeAt } from './tubeMap/frame.js';
25
26
  export type { TubeMapTransform, TubeMapView } from './tubeMap/frame.js';
@@ -17,6 +17,7 @@ export interface LayoutMode {
17
17
  cutMargins: boolean;
18
18
  wholeWalks: boolean;
19
19
  drawsLocally: (graph: Graph) => boolean;
20
+ drawsNodes: boolean;
20
21
  }
21
22
  export declare const LAYOUT_MODES: readonly [{
22
23
  readonly value: "auto";
@@ -27,6 +28,7 @@ export declare const LAYOUT_MODES: readonly [{
27
28
  readonly wholeWalks: false;
28
29
  readonly available: (graph: Graph) => boolean;
29
30
  readonly drawsLocally: (graph: Graph) => boolean;
31
+ readonly drawsNodes: true;
30
32
  }, {
31
33
  readonly value: "samplerows";
32
34
  readonly label: "Sample rows";
@@ -36,6 +38,7 @@ export declare const LAYOUT_MODES: readonly [{
36
38
  readonly wholeWalks: false;
37
39
  readonly available: (graph: Graph) => boolean;
38
40
  readonly drawsLocally: (graph: Graph) => boolean;
41
+ readonly drawsNodes: true;
39
42
  }, {
40
43
  readonly value: "walkrows";
41
44
  readonly label: "Walk rows";
@@ -45,6 +48,7 @@ export declare const LAYOUT_MODES: readonly [{
45
48
  readonly wholeWalks: true;
46
49
  readonly available: (graph: Graph) => boolean;
47
50
  readonly drawsLocally: (graph: Graph) => boolean;
51
+ readonly drawsNodes: false;
48
52
  }, {
49
53
  readonly value: "ordered";
50
54
  readonly label: "Ordered";
@@ -54,6 +58,7 @@ export declare const LAYOUT_MODES: readonly [{
54
58
  readonly wholeWalks: false;
55
59
  readonly available: (graph: Graph) => boolean;
56
60
  readonly drawsLocally: (graph: Graph) => boolean;
61
+ readonly drawsNodes: true;
57
62
  }, {
58
63
  readonly value: "tubemap";
59
64
  readonly label: "Tube map";
@@ -63,6 +68,7 @@ export declare const LAYOUT_MODES: readonly [{
63
68
  readonly wholeWalks: false;
64
69
  readonly available: typeof hasTubeMapPaths;
65
70
  readonly drawsLocally: typeof hasTubeMapPaths;
71
+ readonly drawsNodes: false;
66
72
  }, {
67
73
  readonly value: "tubemapref";
68
74
  readonly label: "Tube map on reference";
@@ -72,6 +78,7 @@ export declare const LAYOUT_MODES: readonly [{
72
78
  readonly wholeWalks: false;
73
79
  readonly available: (graph: Graph) => boolean;
74
80
  readonly drawsLocally: (graph: Graph) => boolean;
81
+ readonly drawsNodes: false;
75
82
  }, {
76
83
  readonly value: "force";
77
84
  readonly label: "Force-directed layout";
@@ -81,6 +88,7 @@ export declare const LAYOUT_MODES: readonly [{
81
88
  readonly wholeWalks: false;
82
89
  readonly available: () => true;
83
90
  readonly drawsLocally: () => false;
91
+ readonly drawsNodes: true;
84
92
  }];
85
93
  export type LayoutModeValue = (typeof LAYOUT_MODES)[number]['value'];
86
94
  export declare const LAYOUT_MODE_VALUES: ("auto" | "ordered" | "samplerows" | "walkrows" | "tubemap" | "tubemapref" | "force")[];
@@ -94,6 +102,7 @@ export declare function layoutModeByValue(value: string): {
94
102
  readonly wholeWalks: false;
95
103
  readonly available: (graph: Graph) => boolean;
96
104
  readonly drawsLocally: (graph: Graph) => boolean;
105
+ readonly drawsNodes: true;
97
106
  } | {
98
107
  readonly value: "samplerows";
99
108
  readonly label: "Sample rows";
@@ -103,6 +112,7 @@ export declare function layoutModeByValue(value: string): {
103
112
  readonly wholeWalks: false;
104
113
  readonly available: (graph: Graph) => boolean;
105
114
  readonly drawsLocally: (graph: Graph) => boolean;
115
+ readonly drawsNodes: true;
106
116
  } | {
107
117
  readonly value: "walkrows";
108
118
  readonly label: "Walk rows";
@@ -112,6 +122,7 @@ export declare function layoutModeByValue(value: string): {
112
122
  readonly wholeWalks: true;
113
123
  readonly available: (graph: Graph) => boolean;
114
124
  readonly drawsLocally: (graph: Graph) => boolean;
125
+ readonly drawsNodes: false;
115
126
  } | {
116
127
  readonly value: "ordered";
117
128
  readonly label: "Ordered";
@@ -121,6 +132,7 @@ export declare function layoutModeByValue(value: string): {
121
132
  readonly wholeWalks: false;
122
133
  readonly available: (graph: Graph) => boolean;
123
134
  readonly drawsLocally: (graph: Graph) => boolean;
135
+ readonly drawsNodes: true;
124
136
  } | {
125
137
  readonly value: "tubemap";
126
138
  readonly label: "Tube map";
@@ -130,6 +142,7 @@ export declare function layoutModeByValue(value: string): {
130
142
  readonly wholeWalks: false;
131
143
  readonly available: typeof hasTubeMapPaths;
132
144
  readonly drawsLocally: typeof hasTubeMapPaths;
145
+ readonly drawsNodes: false;
133
146
  } | {
134
147
  readonly value: "tubemapref";
135
148
  readonly label: "Tube map on reference";
@@ -139,6 +152,7 @@ export declare function layoutModeByValue(value: string): {
139
152
  readonly wholeWalks: false;
140
153
  readonly available: (graph: Graph) => boolean;
141
154
  readonly drawsLocally: (graph: Graph) => boolean;
155
+ readonly drawsNodes: false;
142
156
  } | {
143
157
  readonly value: "force";
144
158
  readonly label: "Force-directed layout";
@@ -148,4 +162,5 @@ export declare function layoutModeByValue(value: string): {
148
162
  readonly wholeWalks: false;
149
163
  readonly available: () => true;
150
164
  readonly drawsLocally: () => false;
165
+ readonly drawsNodes: true;
151
166
  };
@@ -18,3 +18,7 @@ export declare function surveyPaths(paths: PathSteps[], lengthOf: (segmentId: st
18
18
  export declare function chooseReferencePath(anchorPaths: PathOrigin[], preferred: string | undefined): PathOrigin | undefined;
19
19
  export declare function anchorFromPaths(graph: Graph, preferred: string | undefined): Graph;
20
20
  export declare function anchorGraph(graph: Graph, preferred: string | undefined): Graph;
21
+ export declare function trimOrigins(graph: Graph, kept: Map<string, {
22
+ first: number;
23
+ nodeIds: string[];
24
+ }>): PathOrigin[] | undefined;
@@ -19,7 +19,8 @@ export declare function wellKnownSample(assemblyName: string): string | undefine
19
19
  export declare function graphBackbone(graph: Graph): Backbone | undefined;
20
20
  export declare function backboneAssembly<T extends AssemblyNames>(backbone: Backbone | undefined, assemblies: T[]): T | undefined;
21
21
  export declare function assemblyWalk(graph: Graph, assembly: AssemblyNames): PathOrigin | undefined;
22
- export declare function refNameBinding(refNames: Iterable<string>): (name: string) => string | undefined;
22
+ export declare function contigNames(contig: string): string[];
23
+ export declare function refNameBinding(refNames: Iterable<string>, aliases?: Record<string, string>): (name: string) => string | undefined;
23
24
  export declare function featuresOnBackbone<T extends {
24
25
  refName: string;
25
- }>(features: readonly T[], backbone: Backbone): T[];
26
+ }>(features: readonly T[], backbone: Backbone, aliases?: Record<string, string>): T[];
@@ -31,6 +31,7 @@ type Command = {
31
31
  export declare function parsePath(d: string): Command[];
32
32
  interface Filled {
33
33
  color: string;
34
+ track?: number;
34
35
  alpha: number;
35
36
  x0: number;
36
37
  x1: number;
@@ -62,6 +63,8 @@ export interface TubeMapFrame extends TubeMapTransform {
62
63
  width: number;
63
64
  highlightNode?: string | null;
64
65
  darkMode?: boolean;
66
+ tubeColors?: readonly string[];
67
+ nodeColors?: ReadonlyMap<string, string>;
65
68
  }
66
69
  export declare function drawTubeMap(ctx: CanvasRenderingContext2D, picture: TubeMapPicture, frame: TubeMapFrame): void;
67
70
  export declare function mismatchesLegible(yScale: number): boolean;
@@ -0,0 +1,4 @@
1
+ import type { ResolvedColorScheme } from '../colorSchemes.js';
2
+ import type { ReferenceRamp } from '../renderer/GeometryBuilder.js';
3
+ import type { Graph } from '../types.js';
4
+ export declare function tubeMapNodeColors(graph: Graph, colorScheme: ResolvedColorScheme, referenceRamp?: ReferenceRamp): Map<string, string> | undefined;
@@ -1 +1 @@
1
- export declare const version = "4.0.25";
1
+ export declare const version = "4.0.27";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/bandage-core",
3
- "version": "4.0.25",
3
+ "version": "4.0.27",
4
4
  "description": "Pangenome graph layout and drawing with no host: GFA in, the Bandage force-directed engine and reference-anchored layouts, a Canvas2D renderer, hit testing and labels out",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {