@jbrowse/bandage-core 5.0.0 → 5.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.
@@ -297,6 +297,16 @@ function attachment(segments, side) {
297
297
  }
298
298
  var TANGENT_SPAN_FACTOR = 0.5;
299
299
  var MAX_TANGENT = 80;
300
+ function ownStrand(id) {
301
+ return id.endsWith("-") ? "-" : "+";
302
+ }
303
+ function strandSides(edge) {
304
+ const own = (id, strand) => (strand ?? ownStrand(id)) === ownStrand(id);
305
+ return {
306
+ from: own(edge.from, edge.fromStrand) ? "end" : "start",
307
+ to: own(edge.to, edge.toStrand) ? "start" : "end"
308
+ };
309
+ }
300
310
  function spanOf(segments) {
301
311
  let min = Infinity;
302
312
  let max = -Infinity;
@@ -409,6 +419,14 @@ function selfLinkOf(edge) {
409
419
  }
410
420
  return edge.fromStrand && edge.toStrand && edge.fromStrand !== edge.toStrand ? edge.fromStrand === "+" ? "end" : "start" : true;
411
421
  }
422
+ function edgeJoinOf(edge, stranded = false) {
423
+ const self = selfLinkOf(edge);
424
+ if (!stranded) {
425
+ return self;
426
+ }
427
+ const sides = strandSides(edge);
428
+ return self === false ? sides : sides.from === sides.to ? sides.from : true;
429
+ }
412
430
  function hairpinCurves(attach, reach, offsetX, offsetY) {
413
431
  const { at, inward } = attach;
414
432
  const dx = inward ? at.x - inward.x : 1;
@@ -447,7 +465,7 @@ function hairpinCurves(attach, reach, offsetX, offsetY) {
447
465
  }
448
466
  ];
449
467
  }
450
- function computeEdgeCurves(fromSegments, toSegments, selfLink, offsetX, offsetY, axis, bypassed = [], maxBowIn) {
468
+ function computeEdgeCurves(fromSegments, toSegments, join, offsetX, offsetY, axis, bypassed = [], maxBowIn) {
451
469
  const scale = axis.scaleX;
452
470
  const yToX = yToXOf(axis);
453
471
  const maxBow = maxBowIn ?? (axis.pixelRows ? MAX_ROW_BOW_PX : void 0);
@@ -456,7 +474,7 @@ function computeEdgeCurves(fromSegments, toSegments, selfLink, offsetX, offsetY,
456
474
  computeEdgeCurves(
457
475
  scaleYOf(fromSegments, yToX),
458
476
  scaleYOf(toSegments, yToX),
459
- selfLink,
477
+ join,
460
478
  offsetX,
461
479
  offsetY * yToX,
462
480
  { scaleX: scale, scaleY: scale },
@@ -468,15 +486,16 @@ function computeEdgeCurves(fromSegments, toSegments, selfLink, offsetX, offsetY,
468
486
  yToX
469
487
  );
470
488
  }
471
- if (typeof selfLink === "string") {
489
+ if (typeof join === "string") {
472
490
  return hairpinCurves(
473
- attachment(fromSegments, selfLink),
491
+ attachment(fromSegments, join),
474
492
  selfLoopReach(fromSegments, scale),
475
493
  offsetX,
476
494
  offsetY
477
495
  );
478
496
  }
479
- const sides = selfLink ? { from: "end", to: "start" } : facingSides(fromSegments, toSegments);
497
+ const loop = join === true;
498
+ const sides = loop ? { from: "end", to: "start" } : join || facingSides(fromSegments, toSegments);
480
499
  const fromAttach = attachment(fromSegments, sides.from);
481
500
  const toAttach = attachment(toSegments, sides.to);
482
501
  const fromEnd = fromAttach.at;
@@ -485,7 +504,7 @@ function computeEdgeCurves(fromSegments, toSegments, selfLink, offsetX, offsetY,
485
504
  const p1y = fromEnd.y + offsetY;
486
505
  const p2x = toStart.x + offsetX;
487
506
  const p2y = toStart.y + offsetY;
488
- if (selfLink) {
507
+ if (loop) {
489
508
  let segDirX = 1;
490
509
  let segDirY = 0;
491
510
  if (fromAttach.inward) {
@@ -591,12 +610,15 @@ function nearerEnd(segments, to) {
591
610
  const end = segments.at(-1);
592
611
  return Math.hypot(start.x - to.x, start.y - to.y) < Math.hypot(end.x - to.x, end.y - to.y) ? "start" : "end";
593
612
  }
594
- function routedEdgeCurves(fromSegments, toSegments, route) {
613
+ function routedEdgeCurves(fromSegments, toSegments, route, sides) {
595
614
  const fromAttach = attachment(
596
615
  fromSegments,
597
- nearerEnd(fromSegments, route[0])
616
+ sides?.from ?? nearerEnd(fromSegments, route[0])
617
+ );
618
+ const toAttach = attachment(
619
+ toSegments,
620
+ sides?.to ?? nearerEnd(toSegments, route.at(-1))
598
621
  );
599
- const toAttach = attachment(toSegments, nearerEnd(toSegments, route.at(-1)));
600
622
  const points = [fromAttach.at, ...route, toAttach.at];
601
623
  const tangent = (i) => {
602
624
  const p = points[i];
@@ -646,16 +668,21 @@ var MIN_DELETION_BP = 1;
646
668
  function bypassedPoints(nodePositions, bypassed) {
647
669
  return bypassed.flatMap((id) => nodePositions[id] ?? []);
648
670
  }
649
- function deletionArcCurves(nodePositions, deletion, axis, route) {
671
+ function deletionArcCurves(nodePositions, deletion, axis, route, stranded = false) {
650
672
  const from = nodePositions[deletion.from];
651
673
  const to = nodePositions[deletion.to];
652
674
  if (!from?.length || !to?.length) {
653
675
  return void 0;
654
676
  }
655
- return route ? routedEdgeCurves(from, to, route) : computeEdgeCurves(
677
+ return route ? routedEdgeCurves(
678
+ from,
679
+ to,
680
+ route,
681
+ stranded ? strandSides(deletion) : void 0
682
+ ) : computeEdgeCurves(
656
683
  from,
657
684
  to,
658
- deletion.from === deletion.to,
685
+ edgeJoinOf(deletion, stranded),
659
686
  0,
660
687
  0,
661
688
  axis,
@@ -723,6 +750,8 @@ function deletionEdges(graph) {
723
750
  edgeIndex,
724
751
  from: edge.from,
725
752
  to: edge.to,
753
+ ...edge.fromStrand ? { fromStrand: edge.fromStrand } : {},
754
+ ...edge.toStrand ? { toStrand: edge.toStrand } : {},
726
755
  refName,
727
756
  start,
728
757
  end,
@@ -1207,19 +1236,11 @@ function takeRoutes(positions, routes) {
1207
1236
  }
1208
1237
 
1209
1238
  // src/layout/mergeRuns.ts
1210
- function ownStrand(id) {
1211
- return id.endsWith("-") ? "-" : "+";
1212
- }
1213
1239
  function flip(strand) {
1214
1240
  return strand === "+" ? "-" : "+";
1215
1241
  }
1216
- function sidesOf(edge) {
1217
- const from = (edge.fromStrand ?? ownStrand(edge.from)) === ownStrand(edge.from) ? "end" : "start";
1218
- const to = (edge.toStrand ?? ownStrand(edge.to)) === ownStrand(edge.to) ? "start" : "end";
1219
- return { from, to };
1220
- }
1221
1242
  function mergeRuns(graph, alone = /* @__PURE__ */ new Set()) {
1222
- const sides = graph.edges.map(sidesOf);
1243
+ const sides = graph.edges.map(strandSides);
1223
1244
  const at = /* @__PURE__ */ new Map();
1224
1245
  const attach = (id, side, ei) => {
1225
1246
  const key = `${id}|${side}`;
@@ -1589,24 +1610,33 @@ function loadGraph(text, name, opts = {}) {
1589
1610
  }
1590
1611
  return graph;
1591
1612
  }
1613
+ var COMPONENT_SEPARATION = 15;
1592
1614
  function engineRequest(graph, scaling, settings) {
1593
1615
  const anchored = graph.nodes.some(isBackbone);
1616
+ const spaced = {
1617
+ ...scaling,
1618
+ opts: {
1619
+ ...scaling.opts,
1620
+ edgeLength: scaling.opts.edgeLength * (settings.spacing ?? 1)
1621
+ }
1622
+ };
1594
1623
  return {
1595
1624
  graph: {
1596
- nodes: anchored ? seededNodes(graph, scaling) : scaling.nodes,
1625
+ nodes: anchored ? seededNodes(graph, spaced) : spaced.nodes,
1597
1626
  edges: graph.edges
1598
1627
  },
1599
1628
  options: {
1600
1629
  quality: settings.quality,
1601
1630
  linearLayout: settings.linearLayout,
1602
- ...scaling.opts,
1631
+ ...spaced.opts,
1632
+ componentSeparation: COMPONENT_SEPARATION * (settings.componentSeparation ?? 1),
1603
1633
  ...anchored ? { rotateComponents: false } : {}
1604
1634
  }
1605
1635
  };
1606
1636
  }
1607
1637
  function engineKey(graph, settings) {
1608
1638
  const anchored = graph.nodes.some(isBackbone);
1609
- return `${settings.quality}|${settings.linearLayout}|${settings.bubbleSpread}|${anchored}|${graph.referencePath ?? ""}`;
1639
+ return `${settings.quality}|${settings.linearLayout}|${settings.bubbleSpread}|${settings.spacing ?? 1}|${settings.componentSeparation ?? 1}|${anchored}|${graph.referencePath ?? ""}`;
1610
1640
  }
1611
1641
  async function forceLayout(graph, settings, engine) {
1612
1642
  const spread = spreadFor(settings.bubbleSpread);
@@ -1654,6 +1684,7 @@ async function forceLayout(graph, settings, engine) {
1654
1684
  result: {
1655
1685
  ...result,
1656
1686
  nodePositions,
1687
+ stranded: true,
1657
1688
  ...deletions.length ? { deletionRoutes, extent: layoutExtent(deletionRoutes) } : {}
1658
1689
  },
1659
1690
  duration
@@ -1888,10 +1919,10 @@ function labelOrder(nodePositions, nodeLengths) {
1888
1919
  return order;
1889
1920
  }
1890
1921
  var arcCache = /* @__PURE__ */ new WeakMap();
1891
- function arcPlacements(nodePositions, deletions, axis, version2, routes) {
1922
+ function arcPlacements(nodePositions, deletions, axis, version2, routes, stranded) {
1892
1923
  const { scaleX, scaleY } = axis;
1893
1924
  const cached2 = arcCache.get(nodePositions);
1894
- if (cached2?.deletions === deletions && cached2.routes === routes && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.version === version2) {
1925
+ if (cached2?.deletions === deletions && cached2.routes === routes && cached2.stranded === stranded && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.version === version2) {
1895
1926
  return cached2.arcs;
1896
1927
  }
1897
1928
  const arcs = [];
@@ -1900,7 +1931,8 @@ function arcPlacements(nodePositions, deletions, axis, version2, routes) {
1900
1931
  nodePositions,
1901
1932
  deletion,
1902
1933
  axis,
1903
- routes?.[deletion.edgeIndex]
1934
+ routes?.[deletion.edgeIndex],
1935
+ stranded
1904
1936
  );
1905
1937
  const apex = curves ? curveMidpoint(curves) : void 0;
1906
1938
  if (curves && apex) {
@@ -1917,6 +1949,7 @@ function arcPlacements(nodePositions, deletions, axis, version2, routes) {
1917
1949
  arcCache.set(nodePositions, {
1918
1950
  deletions,
1919
1951
  routes,
1952
+ stranded,
1920
1953
  scaleX,
1921
1954
  scaleY,
1922
1955
  version: version2,
@@ -1929,6 +1962,7 @@ function sizeLabelCandidates({
1929
1962
  nodeLengths,
1930
1963
  deletions,
1931
1964
  deletionRoutes,
1965
+ stranded = false,
1932
1966
  alleleDeletions,
1933
1967
  axis,
1934
1968
  translateX,
@@ -1945,7 +1979,8 @@ function sizeLabelCandidates({
1945
1979
  deletions,
1946
1980
  axis,
1947
1981
  version2,
1948
- deletionRoutes
1982
+ deletionRoutes,
1983
+ stranded
1949
1984
  )) {
1950
1985
  if (extent >= MIN_DELETION_LABEL_PX) {
1951
1986
  const text = deletionText(deletion.bp);
@@ -3374,7 +3409,7 @@ var COLOR_SCHEMES = [
3374
3409
  { value: "random", label: "Random" },
3375
3410
  { value: "rainbow", label: "Rainbow" },
3376
3411
  { value: "depth", label: "Depth" },
3377
- { value: "node-length", label: "Node Length" },
3412
+ { value: "node-length", label: "Node length" },
3378
3413
  { value: "stable-rank", label: "Stable rank" },
3379
3414
  { value: "reference-position", label: "Reference position" },
3380
3415
  { value: "grey", label: "Grey" }
@@ -3465,10 +3500,10 @@ function brightenAbgr(c, factor) {
3465
3500
 
3466
3501
  // src/util/edgeCurves.ts
3467
3502
  var cache = /* @__PURE__ */ new WeakMap();
3468
- function baseEdgeCurves(nodePositions, graph, axis, deletions, version2, routes) {
3503
+ function baseEdgeCurves(nodePositions, graph, axis, deletions, version2, routes, stranded = false) {
3469
3504
  const { scaleX, scaleY } = axis;
3470
3505
  const cached2 = cache.get(nodePositions);
3471
- if (cached2?.graph === graph && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.deletions === deletions && cached2.routes === routes && cached2.version === version2) {
3506
+ if (cached2?.graph === graph && cached2.scaleX === scaleX && cached2.scaleY === scaleY && cached2.deletions === deletions && cached2.routes === routes && cached2.stranded === stranded && cached2.version === version2) {
3472
3507
  return cached2.curves;
3473
3508
  }
3474
3509
  const curves = /* @__PURE__ */ new Map();
@@ -3481,10 +3516,15 @@ function baseEdgeCurves(nodePositions, graph, axis, deletions, version2, routes)
3481
3516
  const route = bypassed && routes?.[ei];
3482
3517
  curves.set(
3483
3518
  ei,
3484
- route ? routedEdgeCurves(from, to, route) : computeEdgeCurves(
3519
+ route ? routedEdgeCurves(
3485
3520
  from,
3486
3521
  to,
3487
- selfLinkOf(edge),
3522
+ route,
3523
+ stranded ? strandSides(edge) : void 0
3524
+ ) : computeEdgeCurves(
3525
+ from,
3526
+ to,
3527
+ edgeJoinOf(edge, stranded),
3488
3528
  0,
3489
3529
  0,
3490
3530
  axis,
@@ -3499,6 +3539,7 @@ function baseEdgeCurves(nodePositions, graph, axis, deletions, version2, routes)
3499
3539
  scaleY,
3500
3540
  deletions,
3501
3541
  routes,
3542
+ stranded,
3502
3543
  version: version2,
3503
3544
  curves
3504
3545
  });
@@ -3523,6 +3564,7 @@ var ARROW_LENGTH_BASE_PX = 7;
3523
3564
  var ARROW_LENGTH_PER_EDGE_PX = 3;
3524
3565
  var ARROW_HALF_WIDTH_BASE_PX = 3;
3525
3566
  var ARROW_HALF_WIDTH_PER_EDGE_PX = 1.5;
3567
+ var MIN_LINK_HEADS = 3;
3526
3568
  var MIN_ARROW_FIT = 0.5;
3527
3569
  var MIN_ARROW_SCALE = 0.45;
3528
3570
  var ARROW_CURVE_STEPS = 12;
@@ -3587,12 +3629,17 @@ function arrowheadFor({
3587
3629
  toSegments
3588
3630
  }) {
3589
3631
  const length = ARROW_LENGTH_BASE_PX + ARROW_LENGTH_PER_EDGE_PX * edgeHalfWidth;
3590
- const n2 = backFromEnd(curves, scale, yToX, nodeHalfWidth + length);
3632
+ const n2 = backFromEnd(
3633
+ curves,
3634
+ scale,
3635
+ yToX,
3636
+ nodeHalfWidth + MIN_LINK_HEADS * length
3637
+ );
3591
3638
  const lengthPx = walkPx[n2 - 1];
3639
+ const abutting = lengthPx < ABUTTING_PX;
3592
3640
  const inset = Math.min(nodeHalfWidth, lengthPx);
3593
- const room = lengthPx < ABUTTING_PX ? polylinePx(fromSegments, scale, yToX) : lengthPx - inset;
3594
- const fit = Math.min(1, room / length);
3595
- if (fit < MIN_ARROW_FIT) {
3641
+ const fit = abutting ? Math.min(1, polylinePx(fromSegments, scale, yToX) / length) : 1;
3642
+ if (abutting ? fit < MIN_ARROW_FIT : lengthPx < MIN_LINK_HEADS * length) {
3596
3643
  return void 0;
3597
3644
  }
3598
3645
  const tip = walkPointAt(n2, inset);
@@ -3619,6 +3666,13 @@ function polylinePx(segments, scale, yToX) {
3619
3666
  }
3620
3667
  return length * scale;
3621
3668
  }
3669
+ function leavesStart(edge, segments, first, stranded, yToX) {
3670
+ if (stranded) {
3671
+ return strandSides(edge).from === "start";
3672
+ }
3673
+ const gap = (p) => Math.hypot(p.x - first.x0, (p.y - first.y0) * yToX);
3674
+ return gap(segments[0]) < gap(segments.at(-1));
3675
+ }
3622
3676
  function intoDirection(last, toSegments) {
3623
3677
  if (toSegments.length < 2) {
3624
3678
  return void 0;
@@ -3964,6 +4018,7 @@ function buildGeometry(options) {
3964
4018
  referenceRamp,
3965
4019
  deletions,
3966
4020
  deletionRoutes,
4021
+ stranded,
3967
4022
  hiddenEdges,
3968
4023
  version: version2 = 0
3969
4024
  } = options;
@@ -3976,7 +4031,8 @@ function buildGeometry(options) {
3976
4031
  axis,
3977
4032
  deletions,
3978
4033
  version2,
3979
- deletionRoutes
4034
+ deletionRoutes,
4035
+ stranded
3980
4036
  );
3981
4037
  const nodeStrokes = [];
3982
4038
  const nodeStrokeRuns = /* @__PURE__ */ new Map();
@@ -4028,7 +4084,7 @@ function buildGeometry(options) {
4028
4084
  }
4029
4085
  const head = showArrows && arrowColor !== void 0 ? arrowheadFor({
4030
4086
  curves,
4031
- edgeHalfWidth: edgeThickness,
4087
+ edgeHalfWidth: connectorThickness / 2,
4032
4088
  nodeHalfWidth: intoHalfWidth,
4033
4089
  scale,
4034
4090
  yToX,
@@ -4048,7 +4104,13 @@ function buildGeometry(options) {
4048
4104
  const width = fromNode ? nodeWidthPx(fromNode, contigThickness, nodeWidth2, depthNorm) : contigThickness;
4049
4105
  const laneWidth = Math.max(width / laneWalks.length, MIN_WALK_LANE_PX);
4050
4106
  const normals = pointNormalsOf(fromSegments, yToX);
4051
- const { nx, ny } = (edge.fromStrand === "-" ? normals[0] : normals.at(-1)) ?? {
4107
+ const { nx, ny } = (leavesStart(
4108
+ edge,
4109
+ fromSegments,
4110
+ baseCurves[0],
4111
+ stranded,
4112
+ yToX
4113
+ ) ? normals[0] : normals.at(-1)) ?? {
4052
4114
  nx: 0,
4053
4115
  ny: 0
4054
4116
  };
@@ -4932,6 +4994,7 @@ function layoutLabels(m) {
4932
4994
  nodeLengths: m.nodeLengths,
4933
4995
  deletions: m.showDeletionEdges ? m.deletions : [],
4934
4996
  deletionRoutes: m.deletionRoutes,
4997
+ stranded: m.stranded,
4935
4998
  alleleDeletions: m.alleleDeletions,
4936
4999
  axis: m.axisScale,
4937
5000
  translateX,
@@ -5609,6 +5672,154 @@ function stripGeneGaps(frame, rowGenes, gaps) {
5609
5672
  return frame.boxesGenes ? gaps : { untracked: 0, unread: 0, ...gaps, crowded: true };
5610
5673
  }
5611
5674
 
5675
+ // src/reference.ts
5676
+ var WELL_KNOWN_SAMPLES = /* @__PURE__ */ new Map([
5677
+ ["hg38", "GRCh38"],
5678
+ ["hg19", "GRCh37"],
5679
+ ["hs1", "CHM13"],
5680
+ ["t2t-chm13", "CHM13"],
5681
+ ["chm13v2", "CHM13"]
5682
+ ]);
5683
+ function wellKnownSample(assemblyName) {
5684
+ return WELL_KNOWN_SAMPLES.get(assemblyName.toLowerCase());
5685
+ }
5686
+ function sampleOf2(refName) {
5687
+ const sample = refName.includes("#") ? panSNSample(refName) : void 0;
5688
+ return sample === void 0 || isGenericSample(sample) ? void 0 : sample;
5689
+ }
5690
+ function haplotypeOf(refName) {
5691
+ return panSNHaplotype(refName) ?? panSNSample(refName);
5692
+ }
5693
+ function graphHaplotypes(graph) {
5694
+ const refNames = new Set((graph.anchorPaths ?? []).map((p) => p.name));
5695
+ for (const node of graph.nodes) {
5696
+ if (node.stable) {
5697
+ refNames.add(node.stable.refName);
5698
+ }
5699
+ }
5700
+ const bySample = /* @__PURE__ */ new Map();
5701
+ for (const refName of refNames) {
5702
+ const sample = sampleOf2(refName);
5703
+ if (sample !== void 0) {
5704
+ const haplotypes = bySample.get(sample) ?? bySample.set(sample, /* @__PURE__ */ new Set()).get(sample);
5705
+ haplotypes.add(haplotypeOf(refName));
5706
+ }
5707
+ }
5708
+ return bySample;
5709
+ }
5710
+ function backboneFrom(contigs, haplotypes) {
5711
+ const samples = new Set(contigs.map((c) => sampleOf2(c.refName)));
5712
+ const [sample] = samples;
5713
+ if (samples.size !== 1 || sample === void 0) {
5714
+ return { contigs, prefixes: [], named: false };
5715
+ }
5716
+ const own = new Set(contigs.map((c) => haplotypeOf(c.refName)));
5717
+ const unique = [...haplotypes.get(sample) ?? []].every((h) => own.has(h));
5718
+ const shared = new Set(contigs.map((c) => panSNHaplotype(c.refName)));
5719
+ const [haplotype] = shared;
5720
+ return {
5721
+ contigs,
5722
+ prefixes: [
5723
+ ...unique ? [sample] : [],
5724
+ ...shared.size === 1 && haplotype ? [haplotype] : []
5725
+ ],
5726
+ named: true
5727
+ };
5728
+ }
5729
+ function graphBackbone(graph) {
5730
+ const spans = /* @__PURE__ */ new Map();
5731
+ for (const node of graph.nodes) {
5732
+ if (isBackbone(node)) {
5733
+ const { refName, start } = node.stable;
5734
+ const end = start + node.length;
5735
+ const span = spans.get(refName);
5736
+ if (span) {
5737
+ span.start = Math.min(span.start, start);
5738
+ span.end = Math.max(span.end, end);
5739
+ } else {
5740
+ spans.set(refName, {
5741
+ refName,
5742
+ contig: panSNContig(refName),
5743
+ start,
5744
+ end
5745
+ });
5746
+ }
5747
+ }
5748
+ }
5749
+ const contigs = [...spans.values()];
5750
+ return contigs.length ? backboneFrom(contigs, graphHaplotypes(graph)) : void 0;
5751
+ }
5752
+ function referenceLabel(graph, region) {
5753
+ const backbone = graphBackbone(graph);
5754
+ const contig = region ? panSNContig(region.refName) : backbone?.contigs.length === 1 ? backbone.contigs[0].contig : void 0;
5755
+ const sample = backbone?.named ? panSNSample(backbone.contigs[0].refName) : void 0;
5756
+ return contig && (sample ? `${sample} ${contig}` : contig);
5757
+ }
5758
+ function backboneAssembly(backbone, assemblies) {
5759
+ const names = (a) => [a.name, ...a.aliases ?? []];
5760
+ const naming = (prefix) => (a) => names(a).some((n2) => n2.toLowerCase() === prefix.toLowerCase());
5761
+ const prefixes = [...backbone?.prefixes ?? []].reverse();
5762
+ const sample = backbone?.prefixes.find((p) => !p.includes("#"))?.toLowerCase();
5763
+ for (const prefix of prefixes) {
5764
+ const assembly = assemblies.find(naming(prefix));
5765
+ if (assembly) {
5766
+ return assembly;
5767
+ }
5768
+ }
5769
+ return sample === void 0 ? void 0 : assemblies.find(
5770
+ (a) => names(a).some((n2) => wellKnownSample(n2)?.toLowerCase() === sample)
5771
+ );
5772
+ }
5773
+ function assemblyWalk(graph, assembly) {
5774
+ const haplotypes = graphHaplotypes(graph);
5775
+ return graph.anchorPaths?.find(
5776
+ (walk) => backboneAssembly(
5777
+ backboneFrom(
5778
+ [
5779
+ {
5780
+ refName: walk.name,
5781
+ contig: panSNContig(walk.name),
5782
+ start: walk.start,
5783
+ end: walk.start + walk.length
5784
+ }
5785
+ ],
5786
+ haplotypes
5787
+ ),
5788
+ [assembly]
5789
+ )
5790
+ );
5791
+ }
5792
+ function contigNames(contig) {
5793
+ const bare = contig.replace(/^chr/i, "");
5794
+ const forms = !bare ? [] : /^(M|MT)$/i.test(bare) ? ["M", "MT"] : [bare];
5795
+ return [.../* @__PURE__ */ new Set([contig, ...forms.flatMap((f) => [f, `chr${f}`])])];
5796
+ }
5797
+ function refNameBinding(refNames, aliases = {}) {
5798
+ const exact = new Set(refNames);
5799
+ const byName = /* @__PURE__ */ new Map();
5800
+ for (const refName of exact) {
5801
+ const contig = panSNContig(refName);
5802
+ const alias = aliases[contig];
5803
+ for (const name of /* @__PURE__ */ new Set([
5804
+ ...contigNames(contig),
5805
+ ...alias ? [alias] : []
5806
+ ])) {
5807
+ byName.set(name, byName.has(name) ? void 0 : refName);
5808
+ }
5809
+ }
5810
+ return (name) => exact.has(name) ? name : byName.get(name);
5811
+ }
5812
+ function featuresOnBackbone(features, backbone, aliases) {
5813
+ const bind = refNameBinding(
5814
+ backbone.contigs.map((c) => c.refName),
5815
+ aliases
5816
+ );
5817
+ return features.flatMap((f) => {
5818
+ const refName = bind(f.refName);
5819
+ return refName === void 0 ? [] : [refName === f.refName ? f : { ...f, refName }];
5820
+ });
5821
+ }
5822
+
5612
5823
  // src/renderer/svgCanvas.ts
5613
5824
  var n = (v) => +v.toFixed(1);
5614
5825
  function paint(css) {
@@ -5722,7 +5933,7 @@ function svgCanvas(width, height) {
5722
5933
  }
5723
5934
 
5724
5935
  // src/version.ts
5725
- var version = "5.0.0";
5936
+ var version = "5.1.0";
5726
5937
 
5727
5938
  // src/viewport.ts
5728
5939
  function axisScaleOf(scale, pixelRows) {
@@ -5845,9 +6056,10 @@ function figureSvg(graph, layout, o = {}) {
5845
6056
  ])
5846
6057
  );
5847
6058
  const labelOf2 = (name) => labels.get(name) ?? name;
6059
+ const referenceName = o.referenceName ?? referenceLabel(graph, region);
5848
6060
  const reference = lift?.referenceDomain && {
5849
6061
  ...lift.referenceDomain,
5850
- name: region?.refName
6062
+ name: referenceName
5851
6063
  };
5852
6064
  const contigThickness = o.contigThickness ?? 6;
5853
6065
  const nodeWidth2 = o.nodeWidth ?? "depth";
@@ -5953,6 +6165,7 @@ function figureSvg(graph, layout, o = {}) {
5953
6165
  referenceRamp,
5954
6166
  deletions: new Map(deletions.map((d) => [d.edgeIndex, d.bypassed])),
5955
6167
  deletionRoutes: layout.deletionRoutes,
6168
+ stranded: layout.stranded,
5956
6169
  hiddenEdges: new Set(
5957
6170
  o.showDeletionEdges ? [] : deletions.map((d) => d.edgeIndex)
5958
6171
  )
@@ -6036,10 +6249,10 @@ function figureSvg(graph, layout, o = {}) {
6036
6249
  x,
6037
6250
  2,
6038
6251
  encodingStops({ field: "reference", scheme: "rainbow" }),
6039
- "Reference position",
6252
+ referenceName ? `${referenceName} position` : "Reference position",
6040
6253
  "",
6041
6254
  rangeText(
6042
- region?.refName,
6255
+ referenceName,
6043
6256
  referenceRamp.start,
6044
6257
  referenceRamp.start + referenceRamp.span
6045
6258
  )
@@ -6198,14 +6411,22 @@ function genesFromGff3Lines(lines) {
6198
6411
  const start = top?.start ?? Math.min(...rows2.map((r) => r.start));
6199
6412
  const end = top?.end ?? Math.max(...rows2.map((r) => r.end));
6200
6413
  const a = first.attrs;
6201
- const exons = rows2.filter((r) => r.type === "exon").map((r) => ({ start: r.start, end: r.end }));
6414
+ const spans = (type) => rows2.filter((r) => r.type === type).map((r) => ({ start: r.start, end: r.end }));
6415
+ const exons = spans("exon");
6416
+ const coding = spans("CDS");
6417
+ const transcribed = rows2.some(
6418
+ (r) => r !== top && r.type !== "exon" && r.type !== "CDS"
6419
+ );
6202
6420
  genes.push({
6203
6421
  name: a.get("Name") ?? a.get("gene_name") ?? a.get("gene") ?? a.get("gene_id") ?? key.split(" ")[1],
6204
6422
  refName: first.refName,
6205
6423
  start,
6206
6424
  end,
6207
6425
  strand: first.strand,
6208
- exons: exons.length ? mergedIntervals(exons) : [{ start, end }]
6426
+ // A range read holds only the rows over its window, so a gene with
6427
+ // transcripts but no exon or CDS there is intronic in it. Only a gene
6428
+ // with no transcripts, as a bacterium's gene and CDS, is one exon.
6429
+ exons: exons.length ? mergedIntervals(exons) : transcribed ? mergedIntervals(coding) : [{ start, end }]
6209
6430
  });
6210
6431
  }
6211
6432
  return genes;
@@ -6261,148 +6482,6 @@ function genesFromText(text) {
6261
6482
  return /^\d+$/.test(cols[1] ?? "") && /^\d+$/.test(cols[2] ?? "") ? genesFromBed(text) : genesFromGff3Lines(text.split(/\r?\n/));
6262
6483
  }
6263
6484
 
6264
- // src/reference.ts
6265
- var WELL_KNOWN_SAMPLES = /* @__PURE__ */ new Map([
6266
- ["hg38", "GRCh38"],
6267
- ["hg19", "GRCh37"],
6268
- ["hs1", "CHM13"],
6269
- ["t2t-chm13", "CHM13"],
6270
- ["chm13v2", "CHM13"]
6271
- ]);
6272
- function wellKnownSample(assemblyName) {
6273
- return WELL_KNOWN_SAMPLES.get(assemblyName.toLowerCase());
6274
- }
6275
- function sampleOf2(refName) {
6276
- const sample = refName.includes("#") ? panSNSample(refName) : void 0;
6277
- return sample === void 0 || isGenericSample(sample) ? void 0 : sample;
6278
- }
6279
- function haplotypeOf(refName) {
6280
- return panSNHaplotype(refName) ?? panSNSample(refName);
6281
- }
6282
- function graphHaplotypes(graph) {
6283
- const refNames = new Set((graph.anchorPaths ?? []).map((p) => p.name));
6284
- for (const node of graph.nodes) {
6285
- if (node.stable) {
6286
- refNames.add(node.stable.refName);
6287
- }
6288
- }
6289
- const bySample = /* @__PURE__ */ new Map();
6290
- for (const refName of refNames) {
6291
- const sample = sampleOf2(refName);
6292
- if (sample !== void 0) {
6293
- const haplotypes = bySample.get(sample) ?? bySample.set(sample, /* @__PURE__ */ new Set()).get(sample);
6294
- haplotypes.add(haplotypeOf(refName));
6295
- }
6296
- }
6297
- return bySample;
6298
- }
6299
- function backboneFrom(contigs, haplotypes) {
6300
- const samples = new Set(contigs.map((c) => sampleOf2(c.refName)));
6301
- const [sample] = samples;
6302
- if (samples.size !== 1 || sample === void 0) {
6303
- return { contigs, prefixes: [], named: false };
6304
- }
6305
- const own = new Set(contigs.map((c) => haplotypeOf(c.refName)));
6306
- const unique = [...haplotypes.get(sample) ?? []].every((h) => own.has(h));
6307
- const shared = new Set(contigs.map((c) => panSNHaplotype(c.refName)));
6308
- const [haplotype] = shared;
6309
- return {
6310
- contigs,
6311
- prefixes: [
6312
- ...unique ? [sample] : [],
6313
- ...shared.size === 1 && haplotype ? [haplotype] : []
6314
- ],
6315
- named: true
6316
- };
6317
- }
6318
- function graphBackbone(graph) {
6319
- const spans = /* @__PURE__ */ new Map();
6320
- for (const node of graph.nodes) {
6321
- if (isBackbone(node)) {
6322
- const { refName, start } = node.stable;
6323
- const end = start + node.length;
6324
- const span = spans.get(refName);
6325
- if (span) {
6326
- span.start = Math.min(span.start, start);
6327
- span.end = Math.max(span.end, end);
6328
- } else {
6329
- spans.set(refName, {
6330
- refName,
6331
- contig: panSNContig(refName),
6332
- start,
6333
- end
6334
- });
6335
- }
6336
- }
6337
- }
6338
- const contigs = [...spans.values()];
6339
- return contigs.length ? backboneFrom(contigs, graphHaplotypes(graph)) : void 0;
6340
- }
6341
- function backboneAssembly(backbone, assemblies) {
6342
- const names = (a) => [a.name, ...a.aliases ?? []];
6343
- const naming = (prefix) => (a) => names(a).some((n2) => n2.toLowerCase() === prefix.toLowerCase());
6344
- const prefixes = [...backbone?.prefixes ?? []].reverse();
6345
- const sample = backbone?.prefixes.find((p) => !p.includes("#"))?.toLowerCase();
6346
- for (const prefix of prefixes) {
6347
- const assembly = assemblies.find(naming(prefix));
6348
- if (assembly) {
6349
- return assembly;
6350
- }
6351
- }
6352
- return sample === void 0 ? void 0 : assemblies.find(
6353
- (a) => names(a).some((n2) => wellKnownSample(n2)?.toLowerCase() === sample)
6354
- );
6355
- }
6356
- function assemblyWalk(graph, assembly) {
6357
- const haplotypes = graphHaplotypes(graph);
6358
- return graph.anchorPaths?.find(
6359
- (walk) => backboneAssembly(
6360
- backboneFrom(
6361
- [
6362
- {
6363
- refName: walk.name,
6364
- contig: panSNContig(walk.name),
6365
- start: walk.start,
6366
- end: walk.start + walk.length
6367
- }
6368
- ],
6369
- haplotypes
6370
- ),
6371
- [assembly]
6372
- )
6373
- );
6374
- }
6375
- function contigNames(contig) {
6376
- const bare = contig.replace(/^chr/i, "");
6377
- const forms = !bare ? [] : /^(M|MT)$/i.test(bare) ? ["M", "MT"] : [bare];
6378
- return [.../* @__PURE__ */ new Set([contig, ...forms.flatMap((f) => [f, `chr${f}`])])];
6379
- }
6380
- function refNameBinding(refNames, aliases = {}) {
6381
- const exact = new Set(refNames);
6382
- const byName = /* @__PURE__ */ new Map();
6383
- for (const refName of exact) {
6384
- const contig = panSNContig(refName);
6385
- const alias = aliases[contig];
6386
- for (const name of /* @__PURE__ */ new Set([
6387
- ...contigNames(contig),
6388
- ...alias ? [alias] : []
6389
- ])) {
6390
- byName.set(name, byName.has(name) ? void 0 : refName);
6391
- }
6392
- }
6393
- return (name) => exact.has(name) ? name : byName.get(name);
6394
- }
6395
- function featuresOnBackbone(features, backbone, aliases) {
6396
- const bind = refNameBinding(
6397
- backbone.contigs.map((c) => c.refName),
6398
- aliases
6399
- );
6400
- return features.flatMap((f) => {
6401
- const refName = bind(f.refName);
6402
- return refName === void 0 ? [] : [refName === f.refName ? f : { ...f, refName }];
6403
- });
6404
- }
6405
-
6406
6485
  // src/gbzJoin.ts
6407
6486
  var UNKNOWN_SAMPLE = "unknown";
6408
6487
  function parseWalk(line) {
@@ -6796,6 +6875,14 @@ export {
6796
6875
  walkMarksTree,
6797
6876
  walkStripLabelsTree,
6798
6877
  stripGeneGaps,
6878
+ WELL_KNOWN_SAMPLES,
6879
+ wellKnownSample,
6880
+ graphBackbone,
6881
+ referenceLabel,
6882
+ backboneAssembly,
6883
+ assemblyWalk,
6884
+ refNameBinding,
6885
+ featuresOnBackbone,
6799
6886
  svgCanvas,
6800
6887
  version,
6801
6888
  axisScaleOf,
@@ -6809,13 +6896,6 @@ export {
6809
6896
  mergeOverlappingByName,
6810
6897
  genesFromBed,
6811
6898
  genesFromText,
6812
- WELL_KNOWN_SAMPLES,
6813
- wellKnownSample,
6814
- graphBackbone,
6815
- backboneAssembly,
6816
- assemblyWalk,
6817
- refNameBinding,
6818
- featuresOnBackbone,
6819
6899
  haplotypeWanted,
6820
6900
  referenceSamplesOf,
6821
6901
  resolveReferenceSample,
@@ -21,7 +21,7 @@ import {
21
21
  placeRowGenes,
22
22
  refNameBinding,
23
23
  walkRows
24
- } from "../chunks/chunk-WWH65AAB.js";
24
+ } from "../chunks/chunk-Y2MDXT4O.js";
25
25
 
26
26
  // src/cli/figure.ts
27
27
  import { access, open, readFile, writeFile } from "node:fs/promises";
@@ -171,7 +171,9 @@ async function renderSpec(spec, base) {
171
171
  {
172
172
  quality: spec.quality ?? 2,
173
173
  linearLayout: false,
174
- bubbleSpread: spec.bubbleSpread ?? "auto"
174
+ bubbleSpread: spec.bubbleSpread ?? "auto",
175
+ spacing: spec.spacing,
176
+ componentSeparation: spec.componentSeparation
175
177
  },
176
178
  engine
177
179
  )).result;
package/dist/index.js CHANGED
@@ -99,6 +99,7 @@ import {
99
99
  rangeText,
100
100
  readoutPlacement,
101
101
  refNameBinding,
102
+ referenceLabel,
102
103
  referenceOrder,
103
104
  referencePathQuery,
104
105
  referenceSamplesOf,
@@ -138,7 +139,7 @@ import {
138
139
  wellKnownSample,
139
140
  yToXOf,
140
141
  zoomAbout
141
- } from "./chunks/chunk-WWH65AAB.js";
142
+ } from "./chunks/chunk-Y2MDXT4O.js";
142
143
 
143
144
  // src/index.ts
144
145
  import { getDpr } from "@jbrowse/render-core/canvas2dUtils";
@@ -255,7 +256,7 @@ var SpatialIndex = class {
255
256
  }
256
257
  };
257
258
  var EdgeSpatialIndex = class {
258
- constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions, version2 = 0, routes2) {
259
+ constructor(nodePositions, graph, drawPaths, axis, cellSize, deletions, version2 = 0, routes2, stranded = false) {
259
260
  this.cells = /* @__PURE__ */ new Map();
260
261
  this.edgeCurves = baseEdgeCurves(
261
262
  nodePositions,
@@ -263,7 +264,8 @@ var EdgeSpatialIndex = class {
263
264
  axis,
264
265
  deletions,
265
266
  version2,
266
- routes2
267
+ routes2,
268
+ stranded
267
269
  );
268
270
  const boxes = [];
269
271
  let extentX = 0;
@@ -386,9 +388,9 @@ function getSpatialIndex(nodePositions, version2) {
386
388
  return index;
387
389
  }
388
390
  var edgeCache = /* @__PURE__ */ new WeakMap();
389
- function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version2, deletions, routes2) {
391
+ function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version2, deletions, routes2, stranded = false) {
390
392
  const cached = edgeCache.get(nodePositions);
391
- if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version === version2 && cached.deletions === deletions && cached.routes === routes2) {
393
+ if (cached?.graph === graph && cached.drawPaths === drawPaths && cached.scaleX === axis.scaleX && cached.scaleY === axis.scaleY && cached.version === version2 && cached.deletions === deletions && cached.routes === routes2 && cached.stranded === stranded) {
392
394
  return cached.index;
393
395
  }
394
396
  const index = new EdgeSpatialIndex(
@@ -399,7 +401,8 @@ function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version2, de
399
401
  void 0,
400
402
  deletions,
401
403
  version2,
402
- routes2
404
+ routes2,
405
+ stranded
403
406
  );
404
407
  edgeCache.set(nodePositions, {
405
408
  graph,
@@ -409,7 +412,8 @@ function getEdgeSpatialIndex(nodePositions, graph, drawPaths, axis, version2, de
409
412
  version: version2,
410
413
  index,
411
414
  deletions,
412
- routes: routes2
415
+ routes: routes2,
416
+ stranded
413
417
  });
414
418
  return index;
415
419
  }
@@ -439,7 +443,7 @@ function findHoveredNode(nodePositions, graphX, graphY, axis, version2 = 0, ink)
439
443
  }
440
444
  return hovered;
441
445
  }
442
- function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths, version2 = 0, deletions, hiddenEdges, deletionRoutes) {
446
+ function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths, version2 = 0, deletions, hiddenEdges, deletionRoutes, stranded = false) {
443
447
  const yToX = yToXOf(axis);
444
448
  const edgeThreshold = 10 / axis.scaleX;
445
449
  const edgeIndex = getEdgeSpatialIndex(
@@ -449,7 +453,8 @@ function findHoveredEdge(nodePositions, graph, graphX, graphY, axis, drawPaths,
449
453
  axis,
450
454
  version2,
451
455
  deletions,
452
- deletionRoutes
456
+ deletionRoutes,
457
+ stranded
453
458
  );
454
459
  const candidates = edgeIndex.query(
455
460
  graphX,
@@ -1899,6 +1904,7 @@ export {
1899
1904
  refNameBinding,
1900
1905
  referenceBoxes,
1901
1906
  referenceKnots,
1907
+ referenceLabel,
1902
1908
  referenceNodes,
1903
1909
  referencePathQuery,
1904
1910
  referenceSamplesOf,
@@ -11,6 +11,8 @@ export interface FigureSpec extends Omit<FigureOptions, 'walks' | 'region' | 'ge
11
11
  layout?: LayoutModeValue;
12
12
  quality?: number;
13
13
  bubbleSpread?: BubbleSpread;
14
+ spacing?: number;
15
+ componentSeparation?: number;
14
16
  walks?: (string | WalkLayer)[];
15
17
  walkStrip?: boolean;
16
18
  walkRowSamples?: string[];
@@ -16,7 +16,7 @@ export declare const COLOR_SCHEMES: readonly [{
16
16
  readonly label: "Depth";
17
17
  }, {
18
18
  readonly value: "node-length";
19
- readonly label: "Node Length";
19
+ readonly label: "Node length";
20
20
  }, {
21
21
  readonly value: "stable-rank";
22
22
  readonly label: "Stable rank";
@@ -4,6 +4,8 @@ export interface DeletionEdge {
4
4
  edgeIndex: number;
5
5
  from: string;
6
6
  to: string;
7
+ fromStrand?: '+' | '-';
8
+ toStrand?: '+' | '-';
7
9
  refName: string;
8
10
  start: number;
9
11
  end: number;
@@ -20,5 +22,5 @@ export declare function bypassedPoints(nodePositions: Record<string, {
20
22
  export declare function deletionArcCurves(nodePositions: Record<string, {
21
23
  x: number;
22
24
  y: number;
23
- }[]>, deletion: DeletionEdge, axis: AxisScale, route?: NodeSegment[]): import("./util/geometry.js").BezierCurve[] | undefined;
25
+ }[]>, deletion: DeletionEdge, axis: AxisScale, route?: NodeSegment[], stranded?: boolean): import("./util/geometry.js").BezierCurve[] | undefined;
24
26
  export declare function deletionEdges(graph: Graph): DeletionEdge[];
@@ -21,6 +21,7 @@ export interface FigureOptions {
21
21
  start: number;
22
22
  end: number;
23
23
  };
24
+ referenceName?: string;
24
25
  colorDomain?: {
25
26
  start: number;
26
27
  end: number;
@@ -25,6 +25,7 @@ interface SizeLabelArgs {
25
25
  nodeLengths: Map<string, number>;
26
26
  deletions: DeletionEdge[];
27
27
  deletionRoutes?: DeletionRoutes;
28
+ stranded?: boolean;
28
29
  alleleDeletions?: AlleleDeletion[];
29
30
  axis: AxisScale;
30
31
  translateX: number;
@@ -37,7 +38,7 @@ export interface SizeLabelCandidate {
37
38
  label: GraphLabel;
38
39
  box: Box;
39
40
  }
40
- export declare function sizeLabelCandidates({ nodePositions, nodeLengths, deletions, deletionRoutes, alleleDeletions, axis, translateX, translateY, width, height, version, }: SizeLabelArgs): {
41
+ export declare function sizeLabelCandidates({ nodePositions, nodeLengths, deletions, deletionRoutes, stranded, alleleDeletions, axis, translateX, translateY, width, height, version, }: SizeLabelArgs): {
41
42
  deletions: SizeLabelCandidate[];
42
43
  nodes: SizeLabelCandidate[];
43
44
  };
@@ -70,7 +70,7 @@ export type { LabelLayout, LabelLayoutSource } from './labelLayout.js';
70
70
  export { formatBp } from './graphLabels.js';
71
71
  export { genePins } from './genes/genePins.js';
72
72
  export type { GeneModel, GenePin } from './genes/genePins.js';
73
- export { WELL_KNOWN_SAMPLES, assemblyWalk, backboneAssembly, featuresOnBackbone, graphBackbone, refNameBinding, wellKnownSample, } from './reference.js';
73
+ export { WELL_KNOWN_SAMPLES, assemblyWalk, backboneAssembly, featuresOnBackbone, graphBackbone, refNameBinding, referenceLabel, wellKnownSample, } from './reference.js';
74
74
  export type { AssemblyNames, Backbone, BackboneContig } from './reference.js';
75
75
  export { LABEL_CHAR_PX, LABEL_PAD, LABEL_PX } from './overlayLabels.js';
76
76
  export type { Graph, GraphEdge, GraphNode, LayoutResult, NodeSegment, } from './types.js';
@@ -35,6 +35,7 @@ export interface LabelLayoutSource {
35
35
  showDeletionEdges: boolean;
36
36
  deletions: DeletionEdge[];
37
37
  deletionRoutes?: DeletionRoutes;
38
+ stranded?: boolean;
38
39
  alleleDeletions: AlleleDeletion[];
39
40
  positionsVersion: number;
40
41
  }
@@ -27,6 +27,8 @@ export interface EngineSettings {
27
27
  quality: number;
28
28
  linearLayout: boolean;
29
29
  bubbleSpread: BubbleSpread;
30
+ spacing?: number;
31
+ componentSeparation?: number;
30
32
  }
31
33
  export declare function engineRequest(graph: Graph, scaling: LayoutScaling, settings: EngineSettings): EngineRequest;
32
34
  export declare function engineKey(graph: Graph, settings: EngineSettings): string;
@@ -42,6 +44,7 @@ export declare function forceLayout(graph: Graph, settings: EngineSettings, engi
42
44
  nodePositions: {
43
45
  [x: string]: NodeSegment[];
44
46
  };
47
+ stranded: boolean;
45
48
  rowLabels?: import("./types.js").RowLabel[];
46
49
  sampleRows?: string[];
47
50
  alleleDeletions?: import("./types.js").AlleleDeletion[];
@@ -17,6 +17,9 @@ export interface AssemblyNames {
17
17
  export declare const WELL_KNOWN_SAMPLES: ReadonlyMap<string, string>;
18
18
  export declare function wellKnownSample(assemblyName: string): string | undefined;
19
19
  export declare function graphBackbone(graph: Graph): Backbone | undefined;
20
+ export declare function referenceLabel(graph: Graph, region?: {
21
+ refName: string;
22
+ }): string | undefined;
20
23
  export declare function backboneAssembly<T extends AssemblyNames>(backbone: Backbone | undefined, assemblies: T[]): T | undefined;
21
24
  export declare function assemblyWalk(graph: Graph, assembly: AssemblyNames): PathOrigin | undefined;
22
25
  export declare function contigNames(contig: string): string[];
@@ -29,6 +29,7 @@ export interface BuildOptions {
29
29
  referenceRamp?: ReferenceRamp;
30
30
  deletions?: Map<number, string[]>;
31
31
  deletionRoutes?: DeletionRoutes;
32
+ stranded?: boolean;
32
33
  hiddenEdges?: ReadonlySet<number>;
33
34
  version?: number;
34
35
  }
@@ -74,4 +74,5 @@ export interface LayoutResult {
74
74
  };
75
75
  tubeMap?: TubeMapDrawing;
76
76
  deletionRoutes?: Record<number, NodeSegment[]>;
77
+ stranded?: boolean;
77
78
  }
@@ -19,7 +19,7 @@ export declare class EdgeSpatialIndex {
19
19
  private cellSize;
20
20
  private cells;
21
21
  private edgeCurves;
22
- constructor(nodePositions: Record<string, NodeSegment[]>, graph: Graph, drawPaths: boolean, axis: AxisScale, cellSize?: number | CellSize, deletions?: Map<number, string[]>, version?: number, routes?: DeletionRoutes);
22
+ constructor(nodePositions: Record<string, NodeSegment[]>, graph: Graph, drawPaths: boolean, axis: AxisScale, cellSize?: number | CellSize, deletions?: Map<number, string[]>, version?: number, routes?: DeletionRoutes, stranded?: boolean);
23
23
  getCurves(ei: number): import("./geometry.js").BezierCurve[] | undefined;
24
24
  query(x: number, y: number, radiusX: number, radiusY?: number): number[];
25
25
  }
@@ -2,4 +2,4 @@ import type { DeletionRoutes } from '../layout/deletionRoutes.js';
2
2
  import type { Graph, NodeSegment } from '../types.js';
3
3
  import type { AxisScale, BezierCurve } from './geometry.js';
4
4
  export type EdgeCurves = Map<number, BezierCurve[]>;
5
- export declare function baseEdgeCurves(nodePositions: Record<string, NodeSegment[]>, graph: Graph, axis: AxisScale, deletions: Map<number, string[]> | undefined, version: number, routes?: DeletionRoutes): EdgeCurves;
5
+ export declare function baseEdgeCurves(nodePositions: Record<string, NodeSegment[]>, graph: Graph, axis: AxisScale, deletions: Map<number, string[]> | undefined, version: number, routes?: DeletionRoutes, stranded?: boolean): EdgeCurves;
@@ -28,6 +28,12 @@ export declare function curveBounds(curves: BezierCurve[]): {
28
28
  export declare function dashCurves(curves: BezierCurve[], dashWorld: number): BezierCurve[][];
29
29
  export declare function translateCurves(curves: BezierCurve[], dx: number, dy: number): BezierCurve[];
30
30
  type Side = 'start' | 'end';
31
+ export interface EdgeSides {
32
+ from: Side;
33
+ to: Side;
34
+ }
35
+ export declare function ownStrand(id: string): "+" | "-";
36
+ export declare function strandSides(edge: GraphEdge): EdgeSides;
31
37
  export declare function bowAround(p1x: number, p1y: number, p2x: number, p2y: number, toward: NodeSegment[], maxBow?: number): number;
32
38
  export interface AxisScale {
33
39
  scaleX: number;
@@ -42,6 +48,8 @@ export declare function pathRibbonOffsets(fromSegments: NodeSegment[], toSegment
42
48
  }[];
43
49
  export type SelfLink = boolean | Side;
44
50
  export declare function selfLinkOf(edge: GraphEdge): SelfLink;
45
- export declare function computeEdgeCurves(fromSegments: NodeSegment[], toSegments: NodeSegment[], selfLink: SelfLink, offsetX: number, offsetY: number, axis: AxisScale, bypassed?: NodeSegment[], maxBowIn?: number): BezierCurve[];
46
- export declare function routedEdgeCurves(fromSegments: NodeSegment[], toSegments: NodeSegment[], route: NodeSegment[]): BezierCurve[];
51
+ export type EdgeJoin = SelfLink | EdgeSides;
52
+ export declare function edgeJoinOf(edge: GraphEdge, stranded?: boolean): EdgeJoin;
53
+ export declare function computeEdgeCurves(fromSegments: NodeSegment[], toSegments: NodeSegment[], join: EdgeJoin, offsetX: number, offsetY: number, axis: AxisScale, bypassed?: NodeSegment[], maxBowIn?: number): BezierCurve[];
54
+ export declare function routedEdgeCurves(fromSegments: NodeSegment[], toSegments: NodeSegment[], route: NodeSegment[], sides?: EdgeSides): BezierCurve[];
47
55
  export {};
@@ -8,4 +8,4 @@ export interface NodeInk {
8
8
  maxHalfWidthPx: number;
9
9
  }
10
10
  export declare function findHoveredNode(nodePositions: Record<string, NodeSegment[]>, graphX: number, graphY: number, axis: AxisScale, version?: number, ink?: NodeInk): string | null;
11
- export declare function findHoveredEdge(nodePositions: Record<string, NodeSegment[]>, graph: Graph, graphX: number, graphY: number, axis: AxisScale, drawPaths: boolean, version?: number, deletions?: Map<number, string[]>, hiddenEdges?: ReadonlySet<number>, deletionRoutes?: DeletionRoutes): number | null;
11
+ export declare function findHoveredEdge(nodePositions: Record<string, NodeSegment[]>, graph: Graph, graphX: number, graphY: number, axis: AxisScale, drawPaths: boolean, version?: number, deletions?: Map<number, string[]>, hiddenEdges?: ReadonlySet<number>, deletionRoutes?: DeletionRoutes, stranded?: boolean): number | null;
@@ -1 +1 @@
1
- export declare const version = "5.0.0";
1
+ export declare const version = "5.1.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/bandage-core",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
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": {