@jbrowse/bandage-core 4.0.24 → 4.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,10 +8,13 @@ import {
8
8
  FADED_ALPHA,
9
9
  FIT_PADDING,
10
10
  FORCE_LAYOUT_LABEL,
11
+ HALO_FACTOR,
12
+ HPRC_GBZ,
11
13
  LABEL_CHAR_PX,
12
14
  LABEL_PAD,
13
15
  LABEL_PX,
14
16
  LAYOUT_MODES,
17
+ LEGEND_INSET_PX,
15
18
  LIFT_BACKDROP_COLOR,
16
19
  NODE_WIDTHS,
17
20
  REFERENCE_RAMP_MAX_HUE,
@@ -29,6 +32,7 @@ import {
29
32
  computeReferenceRamp,
30
33
  contains,
31
34
  curveBounds,
35
+ cutGbzRegion,
32
36
  cutWindowGFA,
33
37
  deletionEdges,
34
38
  drawingBounds,
@@ -42,15 +46,20 @@ import {
42
46
  fadeAbgr,
43
47
  featuresOnBackbone,
44
48
  figureSvg,
45
- firstNodeAtOrAfter,
46
49
  fitTransform,
47
50
  forceLayout,
48
51
  formatBp,
52
+ geneCoverageNote,
53
+ genePins,
54
+ genesFromBed,
55
+ genesFromGff3Lines,
56
+ genesFromText,
49
57
  getNodeColor,
50
58
  graphBackbone,
51
59
  haplotypeWanted,
52
60
  isBackbone,
53
61
  layoutExtent,
62
+ layoutLabels,
54
63
  layoutModeByValue,
55
64
  loadBandage,
56
65
  loadGraph,
@@ -58,19 +67,16 @@ import {
58
67
  meanDepth,
59
68
  modeUsesLayoutEngine,
60
69
  nodeInk,
61
- nodeLabelBudget,
62
70
  nodeWidthPx,
63
- occupancy,
71
+ openGbz,
64
72
  padded,
65
73
  panSNContig,
66
74
  panSNHaplotype,
67
75
  panSNSample,
76
+ parseRegion,
68
77
  pathColorsLegible,
69
78
  pathLegend,
70
79
  pathRibbonOffsets,
71
- placeLabels,
72
- placeSizeLabels,
73
- polylineSlice,
74
80
  rangeText,
75
81
  refNameBinding,
76
82
  referenceOrder,
@@ -79,23 +85,24 @@ import {
79
85
  resolveColorScheme,
80
86
  resolveEncoding,
81
87
  resolveReferenceSample,
82
- rowLabelBox,
83
88
  schemeCss,
84
89
  screenToLayout,
85
- sizeLabelCandidates,
86
90
  svgCanvas,
87
91
  svgPath,
88
92
  translateCurves,
93
+ trimOrigins,
94
+ version,
89
95
  viewportOf,
90
96
  walkHighlight,
91
97
  walkKey,
92
98
  walkLift,
99
+ walkPosition,
93
100
  walkRows,
94
101
  walkRowsExtent,
95
102
  wellKnownSample,
96
103
  yToXOf,
97
104
  zoomAbout
98
- } from "./chunks/chunk-4AOHEU76.js";
105
+ } from "./chunks/chunk-75KVO2WV.js";
99
106
 
100
107
  // src/index.ts
101
108
  import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
@@ -212,14 +219,14 @@ var SpatialIndex = class {
212
219
  }
213
220
  };
214
221
  var EdgeSpatialIndex = class {
215
- constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions, version = 0) {
222
+ constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions, version2 = 0) {
216
223
  this.cells = /* @__PURE__ */ new Map();
217
224
  this.edgeCurves = baseEdgeCurves(
218
225
  nodePositions,
219
226
  graph,
220
227
  axis,
221
228
  deletions,
222
- version
229
+ version2
223
230
  );
224
231
  const boxes = [];
225
232
  let extentX = 0;
@@ -332,19 +339,19 @@ function distanceToEdgeCurves(px, py, curves, yToX = 1) {
332
339
  return minDist;
333
340
  }
334
341
  var nodeCache = /* @__PURE__ */ new WeakMap();
335
- function getSpatialIndex(nodePositions, version) {
342
+ function getSpatialIndex(nodePositions, version2) {
336
343
  const cached = nodeCache.get(nodePositions);
337
- if (cached?.version === version) {
344
+ if (cached?.version === version2) {
338
345
  return cached.index;
339
346
  }
340
347
  const index = new SpatialIndex(nodePositions);
341
- nodeCache.set(nodePositions, { version, index });
348
+ nodeCache.set(nodePositions, { version: version2, index });
342
349
  return index;
343
350
  }
344
351
  var edgeCache = /* @__PURE__ */ new WeakMap();
345
- function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version, deletions) {
352
+ function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version2, deletions) {
346
353
  const cached = edgeCache.get(nodePositions);
347
- if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version === version && cached.deletions === deletions) {
354
+ if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version === version2 && cached.deletions === deletions) {
348
355
  return cached.index;
349
356
  }
350
357
  const index = new EdgeSpatialIndex(
@@ -354,23 +361,23 @@ function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version, del
354
361
  axis,
355
362
  void 0,
356
363
  deletions,
357
- version
364
+ version2
358
365
  );
359
366
  edgeCache.set(nodePositions, {
360
367
  graph,
361
368
  drawPaths,
362
369
  scaleX: axis.scaleX,
363
370
  scaleY: axis.scaleY,
364
- version,
371
+ version: version2,
365
372
  index,
366
373
  deletions
367
374
  });
368
375
  return index;
369
376
  }
370
377
  var MIN_NODE_REACH_PX = 5;
371
- function findHoveredNode(nodePositions, graphX, graphY, axis, version = 0, ink) {
378
+ function findHoveredNode(nodePositions, graphX, graphY, axis, version2 = 0, ink) {
372
379
  const yToX = yToXOf(axis);
373
- const index = getSpatialIndex(nodePositions, version);
380
+ const index = getSpatialIndex(nodePositions, version2);
374
381
  const reach = (px) => Math.max(MIN_NODE_REACH_PX, px) / axis.scaleX;
375
382
  const queryReach = reach(ink?.maxHalfWidthPx ?? 0);
376
383
  const candidates = index.query(graphX, graphY, queryReach, queryReach / yToX);
@@ -393,7 +400,7 @@ function findHoveredNode(nodePositions, graphX, graphY, axis, version = 0, ink)
393
400
  }
394
401
  return hovered;
395
402
  }
396
- function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths, version = 0, deletions, hiddenEdges) {
403
+ function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths, version2 = 0, deletions, hiddenEdges) {
397
404
  const yToX = yToXOf(axis);
398
405
  const edgeThreshold = 10 / axis.scaleX;
399
406
  const edgeIndex = getEdgeSpatialIndex(
@@ -401,7 +408,7 @@ function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths,
401
408
  graph,
402
409
  drawPaths,
403
410
  axis,
404
- version,
411
+ version2,
405
412
  deletions
406
413
  );
407
414
  const candidates = edgeIndex.query(
@@ -470,6 +477,7 @@ var REFERENCE_STRIP_PX = 12;
470
477
  var REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8;
471
478
  var OVERHANG_PX = 8;
472
479
  var OVERHANG_CAP_PX = OVERHANG_PX + 3;
480
+ var MIN_STAGGER_PX = 12;
473
481
  var PALE = abgrToCssRgba(fadeAbgr(LIFT_BACKDROP_COLOR, FADED_ALPHA));
474
482
  function referenceStripBlocks(graph, {
475
483
  colorScheme,
@@ -553,7 +561,8 @@ function stripPixels(blocks, frame, width, dpr) {
553
561
  claimed = px1;
554
562
  }
555
563
  }
556
- return out;
564
+ const covered = out.length > 0 ? out.at(-1).x1 - out[0].x0 : 0;
565
+ return covered >= out.length * MIN_STAGGER_PX ? out : out.map(({ tier: _, ...b }) => b);
557
566
  }
558
567
  function drawOverhang(ctx, edgeX, inward, ink) {
559
568
  const capX = inward > 0 ? edgeX : edgeX - OVERHANG_CAP_PX;
@@ -580,9 +589,11 @@ function drawReferenceStrip(ctx, blocks, frame, {
580
589
  const ink = darkMode ? "#ffffff" : "#18181c";
581
590
  ctx.clearRect(0, 0, width, REFERENCE_STRIP_ZONE_PX);
582
591
  for (const b of stripPixels(blocks, frame, width, dpr)) {
583
- const rows = b.colors.length > 1 ? b.colors.length : 2;
592
+ const { tier } = b;
593
+ const staggered = tier !== void 0 && b.colors.length === 1;
594
+ const rows = staggered ? 2 : b.colors.length;
584
595
  b.colors.forEach((color, i) => {
585
- const row = b.colors.length > 1 ? i : b.tier;
596
+ const row = staggered ? tier : i;
586
597
  const y0 = rowEdge(row, rows, dpr);
587
598
  ctx.fillStyle = color;
588
599
  ctx.fillRect(b.x0, y0, b.x1 - b.x0, rowEdge(row + 1, rows, dpr) - y0);
@@ -715,10 +726,11 @@ function shapeOf(d, color, alpha) {
715
726
  const commands = parsePath(d);
716
727
  return { commands, color, alpha, ...extentOf(commands) };
717
728
  }
718
- function layerOf(layout, type) {
729
+ function layerOf(layout, type, tubes) {
719
730
  const { shapes } = layout;
731
+ const colorOf = (s) => tubes?.[s.id] ?? s.color;
720
732
  const rects = [...shapes.rectangles, ...shapes.verticalRectangles].filter((r) => r.type === type).map((r) => ({
721
- color: r.color,
733
+ color: colorOf(r),
722
734
  alpha: r.alpha ?? 1,
723
735
  x0: r.xStart,
724
736
  x1: r.xEnd + 1,
@@ -726,22 +738,30 @@ function layerOf(layout, type) {
726
738
  y1: r.yEnd + 1
727
739
  }));
728
740
  const curves = curvePaths(shapes.curves, type).map(
729
- (c) => shapeOf(c.path, c.color, c.alpha ?? 1)
741
+ (c) => shapeOf(c.path, colorOf(c), c.alpha ?? 1)
730
742
  );
731
- const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, c.color, 1));
743
+ const corners = shapes.corners.filter((c) => c.type === type).map((c) => shapeOf(c.path, colorOf(c), 1));
732
744
  return { rects, shapes: [...curves, ...corners] };
733
745
  }
734
- function tubeMapPicture(layout) {
746
+ function tubeMapPicture(layout, colors = {}) {
735
747
  const nodes = [];
736
748
  layout.nodes.forEach((node) => {
737
749
  if (node.order >= 0) {
738
750
  const commands = parsePath(nodeOutlinePath(node));
739
- nodes.push({ name: node.name, commands, ...extentOf(commands) });
751
+ nodes.push({
752
+ name: node.name,
753
+ commands,
754
+ ...extentOf(commands),
755
+ color: colors.nodes?.get(node.name)
756
+ });
740
757
  }
741
758
  });
742
759
  return {
743
760
  bounds: layout.bounds,
744
- layers: [layerOf(layout, "haplotype"), layerOf(layout, "read")],
761
+ layers: [
762
+ layerOf(layout, "haplotype", colors.tubes),
763
+ layerOf(layout, "read")
764
+ ],
745
765
  nodes,
746
766
  mismatches: tubeMapMismatches(layout).filter(
747
767
  (m) => m.kind !== "insertion" || !m.softClip
@@ -816,6 +836,7 @@ var MIN_OUTLINE_ALPHA = 0.15;
816
836
  function outlineAlpha(screenWidth) {
817
837
  return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX));
818
838
  }
839
+ var NODE_TINT_ALPHA = 0.55;
819
840
  function drawTubeMap(ctx, picture, frame) {
820
841
  const { x, y, width } = frame;
821
842
  const visible = (item) => x(item.x1) >= 0 && x(item.x0) <= width;
@@ -837,8 +858,10 @@ function drawTubeMap(ctx, picture, frame) {
837
858
  const lit = node.name === frame.highlightNode;
838
859
  ctx.beginPath();
839
860
  trace(ctx, node.commands, x, y);
840
- ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : fill;
861
+ const tint = lit ? void 0 : node.color;
862
+ ctx.fillStyle = lit ? "rgba(255,192,203,0.5)" : tint ?? fill;
841
863
  ctx.strokeStyle = lit ? "#ff0000" : stroke;
864
+ ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1;
842
865
  ctx.fill();
843
866
  ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0));
844
867
  ctx.stroke();
@@ -887,6 +910,21 @@ function drawMismatches(ctx, marks, frame) {
887
910
  }
888
911
  }
889
912
 
913
+ // src/tubeMap/nodeColors.ts
914
+ import { abgrToCssRgba as abgrToCssRgba2 } from "@jbrowse/core/util/colorBits";
915
+ function tubeMapNodeColors(graph, colorScheme, referenceRamp) {
916
+ if (colorScheme === "uniform" || colorScheme === "grey") {
917
+ return void 0;
918
+ }
919
+ const range = { ...computeColorSchemeRange(graph), referenceRamp };
920
+ return new Map(
921
+ graph.nodes.map((node, index) => [
922
+ node.id,
923
+ abgrToCssRgba2(getNodeColor(node, index, colorScheme, range))
924
+ ])
925
+ );
926
+ }
927
+
890
928
  // src/tubeMap/warp.ts
891
929
  var MAX_GAP_PX = 24;
892
930
  var isReal = (c) => c.bp1 > c.bp0;
@@ -1052,7 +1090,7 @@ function niceStep(raw) {
1052
1090
  const m = raw / pow;
1053
1091
  return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow;
1054
1092
  }
1055
- function placeLabels2(candidates) {
1093
+ function placeLabels(candidates) {
1056
1094
  const placed = [];
1057
1095
  for (const a of candidates) {
1058
1096
  if (placed.every(
@@ -1089,7 +1127,7 @@ function scaleMarks(boxes, x, width, measure) {
1089
1127
  return {
1090
1128
  spans: [{ x0: from, x1: to }],
1091
1129
  ticks,
1092
- labels: placeLabels2(positions)
1130
+ labels: placeLabels(positions)
1093
1131
  };
1094
1132
  }
1095
1133
  function boxMarks(boxes, x, width, measure) {
@@ -1130,7 +1168,7 @@ function boxMarks(boxes, x, width, measure) {
1130
1168
  return {
1131
1169
  spans,
1132
1170
  ticks,
1133
- labels: placeLabels2([...ends, ...lengths, ...boundaries])
1171
+ labels: placeLabels([...ends, ...lengths, ...boundaries])
1134
1172
  };
1135
1173
  }
1136
1174
  function rulerMarks(boxes, x, width, measure, referenceAxis = false) {
@@ -1686,232 +1724,32 @@ function bubbleSubgraph(graph, segmentIds) {
1686
1724
  const keep = new Set(segmentIds);
1687
1725
  const nodes = graph.nodes.filter((n) => keep.has(n.name));
1688
1726
  const ids = new Set(nodes.map((n) => n.id));
1689
- const paths = graph.paths?.map((p) => ({ ...p, nodeIds: p.nodeIds.filter((id) => ids.has(id)) })).filter((p) => p.nodeIds.length > 0);
1727
+ const lengthOf = new Map(graph.nodes.map((n) => [n.id, n.length]));
1728
+ const bpOf = (steps) => steps.reduce((sum, id) => sum + (lengthOf.get(id) ?? 0), 0);
1729
+ const stretches = /* @__PURE__ */ new Map();
1730
+ const paths = graph.paths?.flatMap((p) => {
1731
+ const first = p.nodeIds.findIndex((id) => ids.has(id));
1732
+ if (first < 0) {
1733
+ return [];
1734
+ }
1735
+ const nodeIds = p.nodeIds.filter((id) => ids.has(id));
1736
+ stretches.set(p.name, {
1737
+ dropped: bpOf(p.nodeIds.slice(0, first)),
1738
+ bp: bpOf(nodeIds)
1739
+ });
1740
+ return [{ ...p, nodeIds }];
1741
+ });
1690
1742
  return {
1691
1743
  name: graph.name,
1692
1744
  nodes,
1693
1745
  edges: graph.edges.filter((e) => ids.has(e.from) && ids.has(e.to)),
1694
1746
  ...paths?.length ? { paths } : {},
1695
- anchorPaths: graph.anchorPaths,
1747
+ anchorPaths: graph.anchorPaths && trimOrigins(graph.anchorPaths, graph.paths ?? [], stretches),
1696
1748
  pathVisits: graph.pathVisits,
1697
1749
  anchoredBy: graph.anchoredBy,
1698
1750
  referencePath: graph.referencePath
1699
1751
  };
1700
1752
  }
1701
-
1702
- // src/labelLayout.ts
1703
- var HALO_FACTOR = 3.4;
1704
- var LEGEND_INSET_PX = 6;
1705
- var ROUTE_STACK = 8;
1706
- var GENE_PIN_DROP_PX = 18;
1707
- var GENE_STACK = 2;
1708
- var TOPMOST_BASELINE = LABEL_PX + LABEL_PAD;
1709
- function geneCoverageNote(pin) {
1710
- const length = pin.gene.end - pin.gene.start;
1711
- if (pin.covered >= 0.98) {
1712
- return void 0;
1713
- }
1714
- const [per, unit] = length >= 1e6 ? [1e6, "Mb"] : length >= 1e3 ? [1e3, "kb"] : [1, "bp"];
1715
- const amount = (bp) => +(bp / per).toFixed(per === 1 ? 0 : 1);
1716
- return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`;
1717
- }
1718
- function byExtent(items, extent) {
1719
- return [...items].sort((a, b) => extent(b) - extent(a));
1720
- }
1721
- function layoutLabels(m) {
1722
- const { paneWidth: width, canvasHeight: height, translateX, translateY } = m;
1723
- const { scaleX, scaleY } = m.axisScale;
1724
- const frame = { width, height };
1725
- const screen = (p) => ({
1726
- x: p.x * scaleX + translateX,
1727
- y: p.y * scaleY + translateY
1728
- });
1729
- const reserved = m.drawnRowLabels.map(
1730
- ({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
1731
- );
1732
- const stripPx = m.referenceStripZonePx ?? 0;
1733
- if (stripPx > 0) {
1734
- reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
1735
- }
1736
- if (m.legendSize.width > 0) {
1737
- reserved.push({
1738
- x0: width - LEGEND_INSET_PX - m.legendSize.width,
1739
- x1: width,
1740
- y0: 0,
1741
- y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
1742
- });
1743
- }
1744
- if (m.poppedFrom) {
1745
- reserved.push({
1746
- x0: 0,
1747
- x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,
1748
- y0: 0,
1749
- y1: 44
1750
- });
1751
- }
1752
- const take = occupancy(frame, reserved);
1753
- const onRows = m.drawnRowLabels.length > 0;
1754
- const rowsTop = onRows ? Math.min(...m.drawnRowLabels.map((r) => r.y)) : Infinity;
1755
- const halo = m.contigThickness * HALO_FACTOR;
1756
- const byBubble = byExtent(m.bubbleHalos, (h) => h.members);
1757
- const bubbles = placeLabels(
1758
- byBubble.map((h) => {
1759
- const { x, y } = screen({
1760
- x: h.labelAt.x,
1761
- y: Math.min(h.labelAt.y, rowsTop)
1762
- });
1763
- const baseline = y - halo / 2 - 6;
1764
- return {
1765
- item: h,
1766
- x,
1767
- y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,
1768
- text: h.label
1769
- };
1770
- }),
1771
- frame,
1772
- take
1773
- );
1774
- const genes = placeLabels(
1775
- byExtent(m.genePins, (pin) => pin.gene.end - pin.gene.start).map((pin) => {
1776
- const { x, y } = screen(pin.at);
1777
- const note = geneCoverageNote(pin);
1778
- return {
1779
- item: pin,
1780
- x,
1781
- y: y + m.contigThickness + GENE_PIN_DROP_PX,
1782
- text: note ? `${pin.gene.name} \xB7 ${note}` : pin.gene.name,
1783
- fallback: note ? `${pin.gene.name} \u2026` : void 0,
1784
- stack: GENE_STACK
1785
- };
1786
- }),
1787
- frame,
1788
- take
1789
- );
1790
- const sizeCandidates = m.nodePositions && m.labelsNodeSizes ? sizeLabelCandidates({
1791
- nodePositions: m.nodePositions,
1792
- nodeLengths: m.nodeLengths,
1793
- deletions: m.showDeletionEdges ? m.deletions : [],
1794
- alleleDeletions: m.alleleDeletions,
1795
- axis: m.axisScale,
1796
- translateX,
1797
- translateY,
1798
- width,
1799
- height,
1800
- version: m.positionsVersion
1801
- }) : { deletions: [], nodes: [] };
1802
- const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
1803
- const routes2 = placeLabels(
1804
- byBubble.flatMap(
1805
- (h) => h.routes.map((route) => {
1806
- const { x, y } = screen(route.at);
1807
- return {
1808
- item: { halo: h, route },
1809
- x,
1810
- y: y + 4,
1811
- text: route.text,
1812
- stack: ROUTE_STACK
1813
- };
1814
- })
1815
- ),
1816
- frame,
1817
- take
1818
- );
1819
- const nodeLabels = placeSizeLabels(
1820
- sizeCandidates.nodes,
1821
- take,
1822
- nodeLabelBudget(width, height)
1823
- );
1824
- return {
1825
- bubbles,
1826
- genes,
1827
- routes: routes2,
1828
- sizes: [...deletionLabels, ...nodeLabels]
1829
- };
1830
- }
1831
-
1832
- // src/genes/genePins.ts
1833
- function genePins(graph, genes, positions) {
1834
- const byRefName = /* @__PURE__ */ new Map();
1835
- for (const node of graph.nodes) {
1836
- if (isBackbone(node)) {
1837
- const { refName } = node.stable;
1838
- if (positions[node.id]?.length) {
1839
- const entry = byRefName.get(refName) ?? byRefName.set(refName, { nodes: [], reach: 0 }).get(refName);
1840
- entry.nodes.push(node);
1841
- entry.reach = Math.max(entry.reach, node.length);
1842
- }
1843
- }
1844
- }
1845
- for (const { nodes } of byRefName.values()) {
1846
- nodes.sort((a, b) => a.stable.start - b.stable.start);
1847
- }
1848
- const pins = [];
1849
- for (const gene of genes) {
1850
- const backbone = byRefName.get(gene.refName);
1851
- if (!backbone) {
1852
- continue;
1853
- }
1854
- const parts = [];
1855
- const exonsByNode = [];
1856
- let at;
1857
- let atDistance = Infinity;
1858
- let covered = 0;
1859
- const mid = (gene.start + gene.end) / 2;
1860
- const { nodes, reach } = backbone;
1861
- for (let i = firstNodeAtOrAfter(nodes, gene.start - reach); i < nodes.length && nodes[i].stable.start < gene.end; i++) {
1862
- const node = nodes[i];
1863
- const nodeStart = node.stable.start;
1864
- const nodeEnd = nodeStart + node.length;
1865
- if (nodeEnd <= gene.start) {
1866
- continue;
1867
- }
1868
- const line = positions[node.id];
1869
- covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start);
1870
- const nodeParts = [];
1871
- for (const exon of gene.exons) {
1872
- const a = Math.max(exon.start, nodeStart);
1873
- const b = Math.min(exon.end, nodeEnd);
1874
- if (b <= a) {
1875
- continue;
1876
- }
1877
- const stretch = polylineSlice(
1878
- line,
1879
- (a - nodeStart) / node.length,
1880
- (b - nodeStart) / node.length
1881
- );
1882
- if (stretch.length === 1) {
1883
- stretch.push({ ...stretch[0] });
1884
- }
1885
- nodeParts.push(svgPath(stretch));
1886
- }
1887
- if (nodeParts.length > 0) {
1888
- parts.push(...nodeParts);
1889
- exonsByNode.push({ nodeId: node.id, d: nodeParts.join("") });
1890
- }
1891
- const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd);
1892
- const distance = Math.abs(pinBp - mid);
1893
- if (distance < atDistance) {
1894
- atDistance = distance;
1895
- const [p] = polylineSlice(
1896
- line,
1897
- (pinBp - nodeStart) / node.length,
1898
- (pinBp - nodeStart) / node.length
1899
- );
1900
- at = p;
1901
- }
1902
- }
1903
- if (at) {
1904
- pins.push({
1905
- gene,
1906
- exons: parts.join(""),
1907
- exonsByNode,
1908
- at,
1909
- covered: covered / (gene.end - gene.start)
1910
- });
1911
- }
1912
- }
1913
- return pins;
1914
- }
1915
1753
  export {
1916
1754
  BUBBLE_KIND_COLORS,
1917
1755
  BUBBLE_KIND_NAMES,
@@ -1924,6 +1762,7 @@ export {
1924
1762
  FIT_PADDING,
1925
1763
  FORCE_LAYOUT_LABEL,
1926
1764
  HALO_FACTOR,
1765
+ HPRC_GBZ,
1927
1766
  LABEL_CHAR_PX,
1928
1767
  LABEL_PAD,
1929
1768
  LABEL_PX,
@@ -1949,6 +1788,7 @@ export {
1949
1788
  computeReferenceRamp,
1950
1789
  connectorAt,
1951
1790
  contains,
1791
+ cutGbzRegion,
1952
1792
  cutWindowGFA,
1953
1793
  deletionEdges,
1954
1794
  drawReferenceStrip,
@@ -1973,6 +1813,9 @@ export {
1973
1813
  formatBp,
1974
1814
  geneCoverageNote,
1975
1815
  genePins,
1816
+ genesFromBed,
1817
+ genesFromGff3Lines,
1818
+ genesFromText,
1976
1819
  getDpr,
1977
1820
  graphBackbone,
1978
1821
  haplotypeWanted,
@@ -1986,10 +1829,12 @@ export {
1986
1829
  modeUsesLayoutEngine,
1987
1830
  nodeInk,
1988
1831
  nodeWidthPx,
1832
+ openGbz,
1989
1833
  padded,
1990
1834
  panSNContig,
1991
1835
  panSNHaplotype,
1992
1836
  panSNSample,
1837
+ parseRegion,
1993
1838
  pathColorsLegible,
1994
1839
  pathLegend,
1995
1840
  rangeText,
@@ -2012,12 +1857,15 @@ export {
2012
1857
  tubeMapFrame,
2013
1858
  tubeMapGenes,
2014
1859
  tubeMapNodeAt,
1860
+ tubeMapNodeColors,
2015
1861
  tubeMapPicture,
2016
1862
  tubeX,
1863
+ version,
2017
1864
  viewportOf,
2018
1865
  walkHighlight,
2019
1866
  walkKey,
2020
1867
  walkLift,
1868
+ walkPosition,
2021
1869
  walkRows,
2022
1870
  walkRowsExtent,
2023
1871
  warpX,
@@ -1,20 +1,20 @@
1
1
  import type { BubbleSpread } from '../bubbleSpreads.js';
2
2
  import type { FigureOptions } from '../figure.js';
3
+ import type { GbzSource } from '../gbzCut.js';
3
4
  import type { LayoutModeValue } from '../layoutModes.js';
4
5
  import type { WalkLayer } from '../walkEncoding.js';
5
- export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region'> {
6
+ export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region' | 'genes' | 'spec'> {
6
7
  gfa?: string;
7
- gbz?: {
8
- db: string;
9
- index?: string;
10
- region: string;
11
- haplotypes?: string[];
12
- referenceSample?: string;
13
- };
8
+ gbz?: GbzSource;
14
9
  region?: string;
15
10
  referencePath?: string;
16
11
  layout?: LayoutModeValue;
17
12
  quality?: number;
18
13
  bubbleSpread?: BubbleSpread;
19
14
  walks?: (string | WalkLayer)[];
15
+ genes?: {
16
+ file: string;
17
+ index?: string;
18
+ format?: 'gff3' | 'bed';
19
+ };
20
20
  }
@@ -1,5 +1,6 @@
1
1
  import type { ColorScheme } from './colorSchemes.js';
2
2
  import type { FacetBy } from './facetGrid.js';
3
+ import type { GeneModel } from './genes/genePins.js';
3
4
  import type { NodeWidth } from './nodeWidths.js';
4
5
  import type { Graph, LayoutResult } from './types.js';
5
6
  import type { WalkLayer } from './walkEncoding.js';
@@ -19,6 +20,12 @@ export interface FigureOptions {
19
20
  start: number;
20
21
  end: number;
21
22
  };
22
- metadata?: string;
23
+ colorDomain?: {
24
+ start: number;
25
+ end: number;
26
+ };
27
+ fitToDrawing?: boolean;
28
+ genes?: GeneModel[];
29
+ spec?: unknown;
23
30
  }
24
31
  export declare function figureSvg(graph: Graph, layout: LayoutResult, o?: FigureOptions): string;