@jbrowse/bandage-core 11.8.0 → 11.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/bubbles/bubbleHalos.js +6 -6
  2. package/dist/bubbles/classifyBubble.js +5 -5
  3. package/dist/bubbles/superbubbles.js +7 -7
  4. package/dist/chunks/{chunk-S4CH434Z.js → chunk-2A6Z2DHF.js} +1 -1
  5. package/dist/chunks/{chunk-6MD4VS2F.js → chunk-2MXFG3W7.js} +1 -1
  6. package/dist/chunks/{chunk-7GDNDQD7.js → chunk-3BDQQTXY.js} +163 -99
  7. package/dist/chunks/{chunk-MO4DIRHD.js → chunk-3BOREHCJ.js} +13 -13
  8. package/dist/chunks/{chunk-2RCKHCF7.js → chunk-3DHGSHC3.js} +1 -1
  9. package/dist/chunks/{chunk-3CXZUC2Y.js → chunk-3GORESBG.js} +1 -1
  10. package/dist/chunks/{chunk-R4XCTRIU.js → chunk-4BYGFDQB.js} +1 -1
  11. package/dist/chunks/{chunk-NHOXLUIL.js → chunk-4JDO7ZLN.js} +2 -2
  12. package/dist/chunks/{chunk-7PEYA3NA.js → chunk-7YSIP3O2.js} +2 -2
  13. package/dist/chunks/{chunk-3XYLEK4H.js → chunk-ARXDNQLF.js} +1 -1
  14. package/dist/chunks/{chunk-6CXLLJ2C.js → chunk-AURNOR27.js} +3 -3
  15. package/dist/chunks/{chunk-G4WQBRDS.js → chunk-BDS5VZYZ.js} +3 -3
  16. package/dist/chunks/{chunk-2H3ALI6L.js → chunk-GDZQFELP.js} +180 -159
  17. package/dist/chunks/{chunk-ZKIH2USC.js → chunk-GYJMPVOU.js} +1 -1
  18. package/dist/chunks/{chunk-X5ULFAMM.js → chunk-HEYIAMTC.js} +2 -2
  19. package/dist/chunks/{chunk-S7BCZVRK.js → chunk-HIN56TLA.js} +1 -1
  20. package/dist/chunks/{chunk-YIWN32ZX.js → chunk-HNNP5QUN.js} +1 -1
  21. package/dist/chunks/{chunk-XOIYBEU7.js → chunk-I53QYITQ.js} +1 -1
  22. package/dist/chunks/{chunk-R3NPBUKT.js → chunk-I5CHAYDZ.js} +7 -7
  23. package/dist/chunks/{chunk-LA6JHEVV.js → chunk-IXLT3JJX.js} +1 -1
  24. package/dist/chunks/{chunk-SDDMV2MM.js → chunk-IZKM4NY7.js} +1 -1
  25. package/dist/chunks/{chunk-SIXFXXRI.js → chunk-J7RNXBXA.js} +3 -3
  26. package/dist/chunks/{chunk-735MPLUL.js → chunk-JJV2GT2Z.js} +22 -14
  27. package/dist/chunks/{chunk-4OVIUCSB.js → chunk-JO2T77NW.js} +1 -1
  28. package/dist/chunks/{chunk-S3ICPPXC.js → chunk-JVT3XTKN.js} +3 -3
  29. package/dist/chunks/{chunk-J4PDHWP2.js → chunk-KKHXMJEE.js} +1 -1
  30. package/dist/chunks/{chunk-L2RQV23D.js → chunk-N3DCLT7M.js} +1 -1
  31. package/dist/chunks/{chunk-22ZMVREZ.js → chunk-NAYS472K.js} +1 -1
  32. package/dist/chunks/{chunk-OIOAK7BS.js → chunk-NWEVR2OQ.js} +1 -1
  33. package/dist/chunks/{chunk-VSRRIFRF.js → chunk-P2GJBFNA.js} +1 -1
  34. package/dist/chunks/{chunk-N2GAA7RB.js → chunk-RT4U3DH3.js} +1 -1
  35. package/dist/chunks/{chunk-AXE3RR7J.js → chunk-S6E3PMH7.js} +1 -1
  36. package/dist/chunks/{chunk-4YPCUVMC.js → chunk-SZYFZNCO.js} +2 -2
  37. package/dist/chunks/{chunk-U6JPSXJ3.js → chunk-T2UNTWU5.js} +9 -9
  38. package/dist/chunks/{chunk-G3GLGDKV.js → chunk-TQ26XEFL.js} +2 -2
  39. package/dist/chunks/{chunk-N4AJV4GH.js → chunk-UEWIGD52.js} +36 -10
  40. package/dist/chunks/{chunk-YEDZ37PH.js → chunk-VNCHWWLG.js} +1 -1
  41. package/dist/chunks/{chunk-JKIAHYAW.js → chunk-VQ353UDK.js} +1 -1
  42. package/dist/chunks/{chunk-NQWEYJDD.js → chunk-VTGQCLGI.js} +1 -1
  43. package/dist/chunks/{chunk-YF6T33PU.js → chunk-ZWBQ4RRK.js} +16 -10
  44. package/dist/cli/figure.js +28 -28
  45. package/dist/cli/fold.js +6 -6
  46. package/dist/deletionEdges.js +2 -2
  47. package/dist/figure.js +24 -24
  48. package/dist/figureData.js +22 -22
  49. package/dist/figureScene.js +15 -15
  50. package/dist/figureSettings.js +6 -6
  51. package/dist/foldVariants.js +2 -2
  52. package/dist/genes/genePins.js +3 -3
  53. package/dist/graphLabels.js +4 -4
  54. package/dist/index.js +38 -38
  55. package/dist/labelLayout.js +8 -8
  56. package/dist/layout/forceCache.js +6 -6
  57. package/dist/layout/mergeRuns.js +2 -2
  58. package/dist/layout/orientToReference.js +2 -2
  59. package/dist/layout/tubeMapLayout.js +7 -7
  60. package/dist/layout/walkRowDraw.js +9 -9
  61. package/dist/layout/walkStrip.js +10 -10
  62. package/dist/layoutModes.js +8 -8
  63. package/dist/pathColors.js +5 -5
  64. package/dist/pipeline.js +5 -5
  65. package/dist/referenceStrip.js +9 -9
  66. package/dist/renderer/GeometryBuilder.js +8 -8
  67. package/dist/renderer/walkLanes.js +4 -2
  68. package/dist/tubeMap/axis.js +5 -5
  69. package/dist/tubeMap/bundle.js +8 -8
  70. package/dist/tubeMap/coarsen.js +8 -8
  71. package/dist/tubeMap/deviations.js +9 -9
  72. package/dist/tubeMap/draw.js +3 -3
  73. package/dist/tubeMap/genes.js +6 -6
  74. package/dist/tubeMap/nodeColors.js +9 -9
  75. package/dist/tubeMap/reads.js +2 -2
  76. package/dist/types/renderer/walkLanes.d.ts +18 -2
  77. package/dist/types/util/geometry.d.ts +1 -1
  78. package/dist/types/version.d.ts +1 -1
  79. package/dist/types/walkEncoding.d.ts +4 -2
  80. package/dist/util/SpatialIndex.js +4 -4
  81. package/dist/util/edgeCurves.js +3 -3
  82. package/dist/util/geometry.js +1 -1
  83. package/dist/util/hitDetection.js +5 -5
  84. package/dist/version.js +1 -1
  85. package/dist/viewport.js +6 -6
  86. package/dist/walkEncoding.js +13 -11
  87. package/dist/walkHighlight.js +10 -10
  88. package/dist/walkKey.js +5 -5
  89. package/package.json +1 -1
@@ -1,19 +1,13 @@
1
1
  import {
2
- LANE_SMOOTHING_PX,
3
2
  THREAD_PITCH_PX,
4
3
  THREAD_PX,
5
- THREAD_REACH_PX,
6
- THREAD_TAPER_PX,
7
4
  bandScale,
8
- bandSpans,
9
5
  bandWidthPx,
10
- laneLines,
11
- laneRuns,
12
6
  laneSlideAt,
7
+ liftLanes,
13
8
  slotCentres,
14
- threadsPasses,
15
- walkThreads
16
- } from "./chunk-7GDNDQD7.js";
9
+ threadsPasses
10
+ } from "./chunk-3BDQQTXY.js";
17
11
  import {
18
12
  abgrToCssRgba,
19
13
  fadeAbgr,
@@ -26,13 +20,13 @@ import {
26
20
  } from "./chunk-GQZVR5DW.js";
27
21
  import {
28
22
  baseEdgeCurves
29
- } from "./chunk-VSRRIFRF.js";
23
+ } from "./chunk-P2GJBFNA.js";
30
24
  import {
31
25
  PATH_LIGHTNESS,
32
26
  PATH_SATURATION,
33
27
  nameHue,
34
28
  pathHueAt
35
- } from "./chunk-JKIAHYAW.js";
29
+ } from "./chunk-VQ353UDK.js";
36
30
  import {
37
31
  meanDepth,
38
32
  nodeWidthPx,
@@ -41,6 +35,9 @@ import {
41
35
  import {
42
36
  referenceMidpoints
43
37
  } from "./chunk-VX656OAZ.js";
38
+ import {
39
+ weakMemo
40
+ } from "./chunk-HMXCN6PG.js";
44
41
  import {
45
42
  dashSpans,
46
43
  pathRibbonOffsets,
@@ -49,7 +46,7 @@ import {
49
46
  strandSides,
50
47
  translateCurves,
51
48
  yToXOf
52
- } from "./chunk-735MPLUL.js";
49
+ } from "./chunk-JJV2GT2Z.js";
53
50
  import {
54
51
  pushTo
55
52
  } from "./chunk-BSOZW3FC.js";
@@ -194,6 +191,8 @@ function normal(a, b) {
194
191
  const len = Math.hypot(dx, dy);
195
192
  return len > 0 ? { nx: -dy / len, ny: dx / len } : void 0;
196
193
  }
194
+ var MITRE_LIMIT = 0.1;
195
+ var LONGEST_NORMAL = 1 / Math.sqrt(MITRE_LIMIT);
197
196
  function pointNormalsOf(rawPoints, yToX = 1) {
198
197
  const points = yToX === 1 ? rawPoints : rawPoints.map((p) => ({ x: p.x, y: p.y * yToX }));
199
198
  return points.map((p, i) => {
@@ -203,16 +202,20 @@ function pointNormalsOf(rawPoints, yToX = 1) {
203
202
  const nx = (before.nx + after.nx) / 2;
204
203
  const ny = (before.ny + after.ny) / 2;
205
204
  const dot = before.nx * nx + before.ny * ny;
206
- return dot > 0.1 ? { nx: nx / dot, ny: ny / dot } : { nx, ny };
205
+ return dot > MITRE_LIMIT ? { nx: nx / dot, ny: ny / dot } : { nx, ny };
207
206
  }
208
207
  return before ?? after ?? { nx: 0, ny: 0 };
209
208
  });
210
209
  }
211
210
  function offsetPolyline(points, normals, distance, yToX = 1) {
212
- return points.map((p, i) => ({
213
- x: p.x + normals[i].nx * distance,
214
- y: p.y + normals[i].ny * distance / yToX
215
- }));
211
+ const out = new Array(points.length);
212
+ for (let i = 0; i < points.length; i++) {
213
+ out[i] = {
214
+ x: points[i].x + normals[i].nx * distance,
215
+ y: points[i].y + normals[i].ny * distance / yToX
216
+ };
217
+ }
218
+ return out;
216
219
  }
217
220
  function hslToRgb(h, s, l) {
218
221
  const c = (1 - Math.abs(2 * l - 1)) * s;
@@ -413,13 +416,13 @@ function isPolylineInBounds(segments, bounds) {
413
416
  }
414
417
  return hit;
415
418
  }
416
- function isBezierInBounds(curves, bounds) {
419
+ function isBezierInBounds(curves, bounds, growX = 0, growY = 0) {
417
420
  for (const c of curves) {
418
421
  const cMinX = Math.min(c.x0, c.cx0, c.cx1, c.x1);
419
422
  const cMaxX = Math.max(c.x0, c.cx0, c.cx1, c.x1);
420
423
  const cMinY = Math.min(c.y0, c.cy0, c.cy1, c.y1);
421
424
  const cMaxY = Math.max(c.y0, c.cy0, c.cy1, c.y1);
422
- if (cMaxX >= bounds.minX && cMinX <= bounds.maxX && cMaxY >= bounds.minY && cMinY <= bounds.maxY) {
425
+ if (cMaxX >= bounds.minX - growX && cMinX <= bounds.maxX + growX && cMaxY >= bounds.minY - growY && cMinY <= bounds.maxY + growY) {
423
426
  return true;
424
427
  }
425
428
  }
@@ -465,6 +468,24 @@ function pathSlotsOf(graph) {
465
468
  }
466
469
  return slots;
467
470
  }
471
+ var lanesCache = weakMemo();
472
+ var linksCache = /* @__PURE__ */ new WeakMap();
473
+ function linksOf(graph) {
474
+ let links = linksCache.get(graph);
475
+ if (!links) {
476
+ links = /* @__PURE__ */ new Map();
477
+ for (const { from, to } of graph.edges) {
478
+ let out = links.get(from);
479
+ if (!out) {
480
+ out = /* @__PURE__ */ new Set();
481
+ links.set(from, out);
482
+ }
483
+ out.add(to);
484
+ }
485
+ linksCache.set(graph, links);
486
+ }
487
+ return links;
488
+ }
468
489
  function buildGeometry(options) {
469
490
  const {
470
491
  nodePositions,
@@ -520,53 +541,26 @@ function buildGeometry(options) {
520
541
  const threaded = threadsPasses(liftedWalks);
521
542
  const lanePasses = liftedWalks.map((w) => w.mostPasses);
522
543
  const thinned = threaded ? bandScale(lanePasses) : 1;
523
- const runs = liftedWalks.length > 1 ? laneRuns(
524
- liftedWalks.flatMap((w) => w.route),
525
- nodePositions,
526
- yToX,
527
- LANE_SMOOTHING_PX * worldPerScreenPx
528
- ) : void 0;
529
- const lines = runs ? laneLines(
530
- runs,
531
- nodePositions,
532
- yToX,
533
- LANE_SMOOTHING_PX * worldPerScreenPx,
534
- bandWidthPx(lanePasses) * thinned * worldPerScreenPx,
535
- threaded
536
- ) : void 0;
537
- const threads = threaded ? liftedWalks.map(
538
- (w) => walkThreads(
539
- w.route,
540
- w.passes,
541
- nodePositions,
542
- yToX,
543
- THREAD_REACH_PX * worldPerScreenPx
544
- )
545
- ) : void 0;
546
- const spanField = runs && threads ? bandSpans(
547
- runs,
548
- threads.map((t) => t.span),
549
- {
550
- reach: THREAD_REACH_PX * worldPerScreenPx,
551
- taper: THREAD_TAPER_PX * worldPerScreenPx
552
- }
544
+ const fields = highlight && liftedWalks.length > 1 ? lanesCache(
545
+ highlight,
546
+ [nodePositions, version, axis.scaleX, axis.scaleY],
547
+ () => liftLanes(liftedWalks, nodePositions, yToX, worldPerScreenPx)
553
548
  ) : void 0;
554
- const edgeKeys = new Set(
555
- threads ? graph.edges.map((e) => `${e.from}>${e.to}`) : []
556
- );
549
+ const lines = fields?.lines;
550
+ const threads = fields?.threads;
551
+ const spanField = fields?.spans;
552
+ const plainCentres = threaded ? slotCentres(lanePasses, lanePasses) : [];
557
553
  const centresByNode = /* @__PURE__ */ new Map();
558
- const laneOffset = (nodeId, laneWidth, lane, pass) => {
559
- if (!threaded) {
560
- return (lane - (liftedWalks.length - 1) / 2) * laneWidth;
561
- }
554
+ const centresOf = (nodeId) => {
562
555
  let centres = centresByNode.get(nodeId);
563
556
  if (!centres) {
564
557
  const spans = spanField?.get(nodeId);
565
- centres = slotCentres(spans?.slot ?? lanePasses, lanePasses, spans?.held);
558
+ centres = spans ? slotCentres(spans.slot, lanePasses, spans.held) : plainCentres;
566
559
  centresByNode.set(nodeId, centres);
567
560
  }
568
- return (centres[lane] + pass * THREAD_PITCH_PX) * thinned;
561
+ return centres;
569
562
  };
563
+ const laneOffset = (nodeId, laneWidth, lane, pass) => threaded ? (centresOf(nodeId)[lane] + pass * THREAD_PITCH_PX) * thinned : (lane - (liftedWalks.length - 1) / 2) * laneWidth;
570
564
  const laneHalfWidth = (laneWidth) => threaded ? THREAD_PX / 2 * thinned : laneWidth / 2;
571
565
  const laneNormals = /* @__PURE__ */ new Map();
572
566
  const laneNormalsOf = (nodeId, segments) => {
@@ -579,7 +573,54 @@ function buildGeometry(options) {
579
573
  return normals;
580
574
  };
581
575
  const laneWidths = /* @__PURE__ */ new Map();
576
+ const laneWidthOf = (id) => {
577
+ let width = laneWidths.get(id);
578
+ if (width === void 0) {
579
+ const node = nodeById.get(id);
580
+ width = walkLaneWidthPx(
581
+ node ? nodeWidthPx(node, contigThickness, nodeWidth, depthNorm) : contigThickness,
582
+ liftedWalks.length
583
+ );
584
+ laneWidths.set(id, width);
585
+ }
586
+ return width;
587
+ };
582
588
  const laneShiftOf = (nodeId, index) => lines?.shifts.get(nodeId)?.[index] ?? NO_SHIFT;
589
+ const laneEndOf = (id, segments, atStart) => {
590
+ const at = atStart ? 0 : segments.length - 1;
591
+ return {
592
+ id,
593
+ laneWidth: laneWidthOf(id),
594
+ normal: laneNormalsOf(id, segments)[at] ?? { nx: 0, ny: 0 },
595
+ shift: laneShiftOf(id, at)
596
+ };
597
+ };
598
+ const lanesMayShow = (curves, fromId) => {
599
+ if (!viewportBounds) {
600
+ return true;
601
+ }
602
+ const offset = threaded ? bandWidthPx(lanePasses) * thinned : (liftedWalks.length - 1) / 2 * laneWidthOf(fromId);
603
+ const reach = LONGEST_NORMAL * offset * worldPerScreenPx;
604
+ return isBezierInBounds(
605
+ curves,
606
+ viewportBounds,
607
+ reach + (fields?.furthestShift.dx ?? 0),
608
+ reach / yToX + (fields?.furthestShift.dy ?? 0)
609
+ );
610
+ };
611
+ const threadPairs = (lane, from, to) => {
612
+ const own = threads?.[lane]?.linkThreads;
613
+ if (!own?.size) {
614
+ return FIRST_THREADS;
615
+ }
616
+ const ahead = own.get(from)?.get(to);
617
+ const back = linksOf(graph).get(to)?.has(from) ? void 0 : own.get(to)?.get(from);
618
+ if (!back) {
619
+ return ahead ?? FIRST_THREADS;
620
+ }
621
+ const turned = back.map(([a, b]) => [b, a]);
622
+ return ahead ? [...ahead, ...turned] : turned;
623
+ };
583
624
  const lanePointsOf = (nodeId, segments) => lines?.shifts.has(nodeId) ? segments.map((p, i) => {
584
625
  const { dx, dy } = laneShiftOf(nodeId, i);
585
626
  return { ...p, x: p.x + dx, y: p.y + dy };
@@ -604,37 +645,47 @@ function buildGeometry(options) {
604
645
  const toNode = nodeById.get(edge.to);
605
646
  const intoHalfWidth = (toNode ? nodeWidthPx(toNode, contigThickness, nodeWidth, depthNorm) : contigThickness) / 2;
606
647
  const inView = (curves) => !viewportBounds || isBezierInBounds(curves, viewportBounds);
607
- const stroke = (curves, thickness, color, lane) => {
608
- const push = (pieces, from = 0, to = 1) => {
609
- if (!lane) {
610
- edgeCurves.push({ curves: pieces, thickness, color });
611
- return;
612
- }
613
- const start = from === 0 ? lane.start : laneSlideAt(lane.start, lane.end, from);
614
- const end = to === 1 ? lane.end : laneSlideAt(lane.start, lane.end, to);
648
+ const dashesOf = (curves) => dashSpans(curves, DELETION_DASH_PX / scale, yToX, viewportBounds);
649
+ const stroke = (curves, thickness, color) => {
650
+ if (!isDeletion) {
651
+ edgeCurves.push({ curves, thickness, color });
652
+ return;
653
+ }
654
+ for (const dash of dashesOf(curves)) {
655
+ edgeCurves.push({ curves: dash.curves, thickness, color });
656
+ }
657
+ };
658
+ const laneStroke = (curves, thickness, color, walk, pass, normal2, offsetX, endNormal, endOffsetX) => {
659
+ if (!isDeletion) {
615
660
  edgeCurves.push({
616
- curves: pieces,
661
+ curves,
617
662
  thickness,
618
663
  color,
619
- walk: lane.walk,
620
- pass: lane.pass,
621
- normal: start.normal,
622
- offsetX: start.offsetX,
623
- endNormal: end.normal,
624
- endOffsetX: end.offsetX
664
+ walk,
665
+ pass,
666
+ normal: normal2,
667
+ offsetX,
668
+ endNormal,
669
+ endOffsetX
625
670
  });
626
- };
627
- if (!isDeletion) {
628
- push(curves);
629
671
  return;
630
672
  }
631
- for (const dash of dashSpans(
632
- curves,
633
- DELETION_DASH_PX / scale,
634
- yToX,
635
- viewportBounds
636
- )) {
637
- push(dash.curves, dash.startAlong, dash.endAlong);
673
+ const start = { normal: normal2, offsetX };
674
+ const end = { normal: endNormal, offsetX: endOffsetX };
675
+ for (const dash of dashesOf(curves)) {
676
+ const a = dash.startAlong === 0 ? start : laneSlideAt(start, end, dash.startAlong);
677
+ const b = dash.endAlong === 1 ? end : laneSlideAt(start, end, dash.endAlong);
678
+ edgeCurves.push({
679
+ curves: dash.curves,
680
+ thickness,
681
+ color,
682
+ walk,
683
+ pass,
684
+ normal: a.normal,
685
+ offsetX: a.offsetX,
686
+ endNormal: b.normal,
687
+ endOffsetX: b.offsetX
688
+ });
638
689
  }
639
690
  };
640
691
  const arrowOn = (curves) => {
@@ -655,89 +706,57 @@ function buildGeometry(options) {
655
706
  const edgeCurveStart = edgeCurves.length;
656
707
  const arrowStart = arrows.length;
657
708
  const laneWalks = highlight?.walks.length && onWalk && !ribbons ? highlight.walks : void 0;
709
+ if (laneWalks && !lanesMayShow(baseCurves, edge.from)) {
710
+ continue;
711
+ }
658
712
  if (laneWalks) {
659
- const laneWidthOf = (id) => {
660
- let width = laneWidths.get(id);
661
- if (width === void 0) {
662
- const node = nodeById.get(id);
663
- width = walkLaneWidthPx(
664
- node ? nodeWidthPx(node, contigThickness, nodeWidth, depthNorm) : contigThickness,
665
- laneWalks.length
666
- );
667
- laneWidths.set(id, width);
668
- }
669
- return width;
670
- };
671
- const endOf = (id, segments, atStart) => {
672
- const at = atStart ? 0 : segments.length - 1;
673
- return {
674
- id,
675
- laneWidth: laneWidthOf(id),
676
- normal: laneNormalsOf(id, segments)[at] ?? { nx: 0, ny: 0 },
677
- shift: laneShiftOf(id, at)
678
- };
679
- };
680
- const from = endOf(
713
+ const from = laneEndOf(
681
714
  edge.from,
682
715
  fromSegments,
683
716
  leavesStart(edge, fromSegments, baseCurves[0], stranded, yToX)
684
717
  );
685
718
  const last = baseCurves.at(-1);
686
- const to = threaded ? endOf(
719
+ const to = threaded ? laneEndOf(
687
720
  edge.to,
688
721
  toSegments,
689
722
  nearerStart({ x: last.x1, y: last.y1 }, toSegments, yToX)
690
723
  ) : from;
691
- const slideOf = (end, offsetX) => ({
692
- dx: end.shift.dx + end.normal.nx * offsetX,
693
- dy: end.shift.dy + end.normal.ny * offsetX / yToX
694
- });
695
- const aheadKey = threads ? `${edge.from}>${edge.to}` : "";
696
- const backKey = threads ? `${edge.to}>${edge.from}` : "";
697
- const pairsOf = (lane) => {
698
- const own = threads?.[lane]?.linkThreads;
699
- if (!own?.size) {
700
- return FIRST_THREADS;
701
- }
702
- const ahead = own.get(aheadKey);
703
- const back = edgeKeys.has(backKey) ? void 0 : own.get(backKey);
704
- if (!back) {
705
- return ahead ?? FIRST_THREADS;
706
- }
707
- const turned = back.map(([a, b]) => [b, a]);
708
- return ahead ? [...ahead, ...turned] : turned;
709
- };
710
724
  const single = laneWalks.length === 1;
711
- laneWalks.forEach((walk, lane) => {
725
+ const thickness = laneHalfWidth(from.laneWidth);
726
+ for (let lane = 0; lane < laneWalks.length; lane++) {
727
+ const walk = laneWalks[lane];
712
728
  if (!walk.edgeIndexes.has(ei)) {
713
- return;
729
+ continue;
714
730
  }
715
- for (const [pass, toPass] of pairsOf(lane)) {
731
+ for (const [pass, toPass] of threadPairs(lane, edge.from, edge.to)) {
716
732
  const offsetX = single ? 0 : laneOffset(from.id, from.laneWidth, lane, pass) * worldPerScreenPx;
717
- const start = { normal: from.normal, offsetX };
718
- const end = threaded ? {
719
- normal: to.normal,
720
- offsetX: laneOffset(to.id, to.laneWidth, lane, toPass) * worldPerScreenPx
721
- } : start;
733
+ const endOffsetX = threaded ? laneOffset(to.id, to.laneWidth, lane, toPass) * worldPerScreenPx : offsetX;
722
734
  const curves = threaded ? slideCurves(
723
735
  baseCurves,
724
- slideOf(from, start.offsetX),
725
- slideOf(to, end.offsetX)
736
+ from.shift.dx + from.normal.nx * offsetX,
737
+ from.shift.dy + from.normal.ny * offsetX / yToX,
738
+ to.shift.dx + to.normal.nx * endOffsetX,
739
+ to.shift.dy + to.normal.ny * endOffsetX / yToX
726
740
  ) : offsetX === 0 ? baseCurves : translateCurves(
727
741
  baseCurves,
728
742
  from.normal.nx * offsetX,
729
743
  from.normal.ny * offsetX / yToX
730
744
  );
731
745
  if (inView(curves)) {
732
- stroke(
746
+ laneStroke(
733
747
  curves,
734
- laneHalfWidth(from.laneWidth),
748
+ thickness,
735
749
  walk.colors.get(edge.from) ?? walk.colors.get(edge.to) ?? plainEdgeColor,
736
- { walk: lane, pass, start, end }
750
+ lane,
751
+ pass,
752
+ from.normal,
753
+ offsetX,
754
+ to.normal,
755
+ endOffsetX
737
756
  );
738
757
  }
739
758
  }
740
- });
759
+ }
741
760
  if (edgeCurves.length > edgeCurveStart) {
742
761
  arrowOn(baseCurves);
743
762
  }
@@ -807,34 +826,36 @@ function buildGeometry(options) {
807
826
  const laneWidth = walkLaneWidthPx(width, lanes.length);
808
827
  const normals = lanes.length > 1 ? laneNormalsOf(nodeId, segments) : [];
809
828
  const points = lanes.length > 1 ? lanePointsOf(nodeId, segments) : segments;
810
- lanes.forEach((walk, lane) => {
811
- const laneColor = walk.colors.get(nodeId);
829
+ const thickness = laneHalfWidth(laneWidth);
830
+ for (let lane = 0; lane < lanes.length; lane++) {
831
+ const laneColor = lanes[lane].colors.get(nodeId);
812
832
  if (laneColor === void 0) {
813
- return;
833
+ continue;
814
834
  }
815
835
  const onThreads = threads?.[lane]?.nodeThreads.get(nodeId) ?? ONE_PASS;
816
- const thickness = laneHalfWidth(laneWidth);
817
- onThreads.forEach((pass) => {
818
- const offsetX = lanes.length === 1 ? 0 : laneOffset(nodeId, laneWidth, lane, pass) * worldPerScreenPx;
819
- nodeStrokes.push(
820
- lanes.length === 1 ? {
836
+ for (const pass of onThreads) {
837
+ if (lanes.length === 1) {
838
+ nodeStrokes.push({
821
839
  points: segments,
822
840
  thickness,
823
841
  color: laneColor,
824
842
  walk: lane,
825
843
  pass
826
- } : {
827
- points: offsetPolyline(points, normals, offsetX, yToX),
828
- thickness,
829
- color: laneColor,
830
- walk: lane,
831
- pass,
832
- normals,
833
- offsetX
834
- }
835
- );
836
- });
837
- });
844
+ });
845
+ continue;
846
+ }
847
+ const offsetX = laneOffset(nodeId, laneWidth, lane, pass) * worldPerScreenPx;
848
+ nodeStrokes.push({
849
+ points: offsetPolyline(points, normals, offsetX, yToX),
850
+ thickness,
851
+ color: laneColor,
852
+ walk: lane,
853
+ pass,
854
+ normals,
855
+ offsetX
856
+ });
857
+ }
858
+ }
838
859
  } else if (drawable && slots?.length && slotWidth >= MIN_PATH_STRIPE_PX) {
839
860
  const normals = pointNormalsOf(segments, yToX);
840
861
  for (const slot of slots) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  computeColorSchemeRange,
3
3
  getNodeColor
4
- } from "./chunk-2H3ALI6L.js";
4
+ } from "./chunk-GDZQFELP.js";
5
5
  import {
6
6
  abgrToCssRgba
7
7
  } from "./chunk-G2FD7S3C.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  polylineSlice
3
- } from "./chunk-YIWN32ZX.js";
3
+ } from "./chunk-HNNP5QUN.js";
4
4
  import {
5
5
  svgPath
6
- } from "./chunk-735MPLUL.js";
6
+ } from "./chunk-JJV2GT2Z.js";
7
7
  import {
8
8
  firstNodeAtOrAfter,
9
9
  isAnchored
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-MYDHU2UV.js";
7
7
  import {
8
8
  readColor
9
- } from "./chunk-J4PDHWP2.js";
9
+ } from "./chunk-KKHXMJEE.js";
10
10
  import {
11
11
  referenceWalkName
12
12
  } from "./chunk-LODKC7PJ.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ownStrand,
3
3
  strandSides
4
- } from "./chunk-735MPLUL.js";
4
+ } from "./chunk-JJV2GT2Z.js";
5
5
  import {
6
6
  pushTo
7
7
  } from "./chunk-BSOZW3FC.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  formatBp
3
- } from "./chunk-6CXLLJ2C.js";
3
+ } from "./chunk-AURNOR27.js";
4
4
  import {
5
5
  pushTo
6
6
  } from "./chunk-BSOZW3FC.js";
@@ -4,27 +4,27 @@ import {
4
4
  import {
5
5
  facetLifts,
6
6
  walkLift
7
- } from "./chunk-YF6T33PU.js";
7
+ } from "./chunk-ZWBQ4RRK.js";
8
8
  import {
9
9
  buildGeometry,
10
10
  computeReferenceRamp
11
- } from "./chunk-2H3ALI6L.js";
11
+ } from "./chunk-GDZQFELP.js";
12
12
  import {
13
13
  walkKey
14
- } from "./chunk-SDDMV2MM.js";
14
+ } from "./chunk-IZKM4NY7.js";
15
15
  import {
16
16
  walkRowsExtent
17
17
  } from "./chunk-VCUOYVLC.js";
18
18
  import {
19
19
  pathLegend,
20
20
  walkLabelsOf
21
- } from "./chunk-JKIAHYAW.js";
21
+ } from "./chunk-VQ353UDK.js";
22
22
  import {
23
23
  FIGURE_DEFAULTS
24
- } from "./chunk-6MD4VS2F.js";
24
+ } from "./chunk-2MXFG3W7.js";
25
25
  import {
26
26
  drawingBounds
27
- } from "./chunk-S3ICPPXC.js";
27
+ } from "./chunk-JVT3XTKN.js";
28
28
  import {
29
29
  referenceLabel
30
30
  } from "./chunk-5ORP7EFJ.js";
@@ -34,7 +34,7 @@ import {
34
34
  import {
35
35
  deletionDrawing,
36
36
  deletionEdges
37
- } from "./chunk-3CXZUC2Y.js";
37
+ } from "./chunk-3GORESBG.js";
38
38
  import {
39
39
  facetCells,
40
40
  facetSettingOf
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-H2H4Y3TV.js";
4
4
  import {
5
5
  REFERENCE_RAMP_ALT_CSS
6
- } from "./chunk-2H3ALI6L.js";
6
+ } from "./chunk-GDZQFELP.js";
7
7
  import {
8
8
  RAMP_GRADIENT_CSS,
9
9
  RAMP_GRADIENT_STOPS,
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  formatBp,
7
7
  groupDigits
8
- } from "./chunk-6CXLLJ2C.js";
8
+ } from "./chunk-AURNOR27.js";
9
9
 
10
10
  // src/walkKey.ts
11
11
  function rangeText(contig, start, end) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  coversGraph
3
- } from "./chunk-4YPCUVMC.js";
3
+ } from "./chunk-SZYFZNCO.js";
4
4
  import {
5
5
  bubblesFromGraph,
6
6
  derivedBubble,
@@ -10,13 +10,13 @@ import {
10
10
  } from "./chunk-RFG42VIO.js";
11
11
  import {
12
12
  bubbleSegmentIds
13
- } from "./chunk-OIOAK7BS.js";
13
+ } from "./chunk-NWEVR2OQ.js";
14
14
  import {
15
15
  bubbleSubgraph
16
16
  } from "./chunk-MHZTTLBU.js";
17
17
  import {
18
18
  strandSides
19
- } from "./chunk-735MPLUL.js";
19
+ } from "./chunk-JJV2GT2Z.js";
20
20
  import {
21
21
  pushTo
22
22
  } from "./chunk-BSOZW3FC.js";
@@ -150,21 +150,29 @@ var slideAt = (from, to, along) => ({
150
150
  dx: from.dx + (to.dx - from.dx) * along,
151
151
  dy: from.dy + (to.dy - from.dy) * along
152
152
  });
153
- function slideCurves(curves, from, to) {
154
- return curves.map((c, i) => {
155
- const a = slideAt(from, to, i / curves.length);
156
- const b = slideAt(from, to, (i + 1) / curves.length);
157
- return {
158
- x0: c.x0 + a.dx,
159
- y0: c.y0 + a.dy,
160
- cx0: c.cx0 + a.dx,
161
- cy0: c.cy0 + a.dy,
162
- cx1: c.cx1 + b.dx,
163
- cy1: c.cy1 + b.dy,
164
- x1: c.x1 + b.dx,
165
- y1: c.y1 + b.dy
153
+ function slideCurves(curves, fromDx, fromDy, toDx, toDy) {
154
+ const n = curves.length;
155
+ const out = new Array(n);
156
+ for (let i = 0; i < n; i++) {
157
+ const c = curves[i];
158
+ const a = i / n;
159
+ const b = (i + 1) / n;
160
+ const adx = fromDx + (toDx - fromDx) * a;
161
+ const ady = fromDy + (toDy - fromDy) * a;
162
+ const bdx = fromDx + (toDx - fromDx) * b;
163
+ const bdy = fromDy + (toDy - fromDy) * b;
164
+ out[i] = {
165
+ x0: c.x0 + adx,
166
+ y0: c.y0 + ady,
167
+ cx0: c.cx0 + adx,
168
+ cy0: c.cy0 + ady,
169
+ cx1: c.cx1 + bdx,
170
+ cy1: c.cy1 + bdy,
171
+ x1: c.x1 + bdx,
172
+ y1: c.y1 + bdy
166
173
  };
167
- });
174
+ }
175
+ return out;
168
176
  }
169
177
  function translateCurves(curves, dx, dy) {
170
178
  const out = new Array(curves.length);