jbrowse-plugin-graphgenomeviewer 4.2.0 → 4.3.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 (33) hide show
  1. package/dist/chunks/{GraphGenomeView-QOBHGMLE.js → GraphGenomeView-ZB4N6Q3H.js} +2 -2
  2. package/dist/chunks/{GraphTrackSettingsDialog-RTSJTM6A.js → GraphTrackSettingsDialog-LXYUYPZS.js} +2 -2
  3. package/dist/chunks/{LinearGraphDisplay-TPXW6POY.js → LinearGraphDisplay-BCTYOYQ5.js} +2 -2
  4. package/dist/chunks/{chunk-4C4CMI6K.js → chunk-CNFN6EVE.js} +2 -2
  5. package/dist/chunks/chunk-PPYB6N2Q.js +2 -0
  6. package/dist/chunks/chunk-PPYB6N2Q.js.map +7 -0
  7. package/dist/chunks/chunk-PSOLXUFN.js +3 -0
  8. package/dist/chunks/chunk-PSOLXUFN.js.map +7 -0
  9. package/dist/chunks/chunk-UTWFU4X6.js +2 -0
  10. package/dist/chunks/chunk-UTWFU4X6.js.map +7 -0
  11. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +5 -5
  12. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +4 -4
  13. package/package.json +2 -2
  14. package/src/GraphGenomeView/components/GraphCanvas.tsx +6 -53
  15. package/src/GraphGenomeView/components/WalkRowContextMenu.tsx +63 -0
  16. package/src/GraphGenomeView/components/WalkRowsOverlay.tsx +1 -1
  17. package/src/GraphGenomeView/components/WalkStrip.tsx +207 -0
  18. package/src/GraphGenomeView/components/WalkStripLocator.tsx +49 -0
  19. package/src/GraphGenomeView/components/usePaneGestures.ts +1 -0
  20. package/src/GraphGenomeView/model.test.ts +42 -3
  21. package/src/GraphGenomeView/model.ts +139 -13
  22. package/src/GraphGenomeView/viewModel.ts +23 -7
  23. package/src/version.ts +1 -1
  24. package/dist/chunks/chunk-ECKLYJWP.js +0 -2
  25. package/dist/chunks/chunk-ECKLYJWP.js.map +0 -7
  26. package/dist/chunks/chunk-HW55QCJG.js +0 -3
  27. package/dist/chunks/chunk-HW55QCJG.js.map +0 -7
  28. package/dist/chunks/chunk-SIP5IHWK.js +0 -2
  29. package/dist/chunks/chunk-SIP5IHWK.js.map +0 -7
  30. /package/dist/chunks/{GraphGenomeView-QOBHGMLE.js.map → GraphGenomeView-ZB4N6Q3H.js.map} +0 -0
  31. /package/dist/chunks/{GraphTrackSettingsDialog-RTSJTM6A.js.map → GraphTrackSettingsDialog-LXYUYPZS.js.map} +0 -0
  32. /package/dist/chunks/{LinearGraphDisplay-TPXW6POY.js.map → LinearGraphDisplay-BCTYOYQ5.js.map} +0 -0
  33. /package/dist/chunks/{chunk-4C4CMI6K.js.map → chunk-CNFN6EVE.js.map} +0 -0
@@ -40,6 +40,11 @@ import {
40
40
  } from '@jbrowse/bandage-core/layout/walkRowDraw'
41
41
  import { walkRowsExtent } from '@jbrowse/bandage-core/layout/walkRowLayout'
42
42
  import { walkRows } from '@jbrowse/bandage-core/layout/walkRows'
43
+ import {
44
+ segmentAt,
45
+ stripMarks,
46
+ walkStripFrame,
47
+ } from '@jbrowse/bandage-core/layout/walkStrip'
43
48
  import {
44
49
  LAYOUT_MODES,
45
50
  LAYOUT_MODE_VALUES,
@@ -47,6 +52,7 @@ import {
47
52
  modeUsesLayoutEngine,
48
53
  } from '@jbrowse/bandage-core/layoutModes'
49
54
  import { NODE_WIDTH_VALUES, nodeInk } from '@jbrowse/bandage-core/nodeWidths'
55
+ import { LABEL_CHAR_PX } from '@jbrowse/bandage-core/overlayLabels'
50
56
  import {
51
57
  anchorFromPaths,
52
58
  chooseReferencePath,
@@ -297,6 +303,25 @@ const VIEWPORT_DEBOUNCE_MS = 150
297
303
  const VIEWPORT_PANES_BUILT = 1
298
304
  // How many walk rows read their haplotype's genes, the first rows down
299
305
  const WALK_GENE_ROWS = 40
306
+ // The most of the pane's height the strip of walk rows under a node layout
307
+ // takes; past it rows shrink, down to a dense overview
308
+ const WALK_STRIP_CEILING = 260
309
+
310
+ // The rows a sample filter keeps, in the order it names them
311
+ function filterSamples<R extends { sample: string; label: string }>(
312
+ rows: R[],
313
+ samples: string[] | undefined,
314
+ ) {
315
+ return samples
316
+ ? rows
317
+ .filter(r => samples.includes(r.sample))
318
+ .sort(
319
+ (a, b) =>
320
+ samples.indexOf(a.sample) - samples.indexOf(b.sample) ||
321
+ a.label.localeCompare(b.label),
322
+ )
323
+ : rows
324
+ }
300
325
 
301
326
  // What the canvas draws under the tube map, whose ink is all TubeMapOverlay's
302
327
  const EMPTY_BATCH: RenderBatch = {
@@ -494,7 +519,7 @@ export function GraphPaneMixin() {
494
519
  // with a label that opens it. Off by default: on a base-level cut
495
520
  // every SNP's halo is a blob.
496
521
  showBubbles: types.optional(types.boolean, false),
497
- showDeletionEdges: types.optional(types.boolean, false),
522
+ showDeletionEdges: types.optional(types.boolean, true),
498
523
  // The session's genes drawn onto the backbone: exons along the nodes
499
524
  // that carry them, names pinned at their midpoints. See genes/.
500
525
  showGenes: types.optional(types.boolean, true),
@@ -517,6 +542,9 @@ export function GraphPaneMixin() {
517
542
  // Samples whose walks the walk rows show, by the name before the
518
543
  // haplotype number; undefined shows every walk the cut holds.
519
544
  walkRowSamples: types.maybe(types.frozen<string[]>()),
545
+ // Walk rows in a strip under a layout that draws nodes, linked to the
546
+ // drawing: a node's passes tick each bar, a bar's point lights its node
547
+ walkStrip: types.optional(types.boolean, false),
520
548
  // Walks lifted out of the drawing, each a layer with a lane of its own
521
549
  // and the rest fading, coloured by the encoding it states or by the
522
550
  // default one. See walkEncoding.ts. Empty lifts none.
@@ -622,6 +650,8 @@ export function GraphPaneMixin() {
622
650
  // whether the pointer is over this pane, whose own hit test then says
623
651
  // what it is on
624
652
  pointerInPane: false,
653
+ // the strip point the pointer is over, which lights its node above
654
+ stripHover: null as { row: string; offset: number } | null,
625
655
  hoveredEdge: null as number | null,
626
656
  selectedNode: null as string | null,
627
657
  viewportDirty: 0,
@@ -966,6 +996,9 @@ export function GraphPaneMixin() {
966
996
  get alleleDeletions() {
967
997
  return self.layoutResult?.alleleDeletions ?? []
968
998
  },
999
+ get deletionRoutes() {
1000
+ return self.layoutResult?.deletionRoutes
1001
+ },
969
1002
  // Whether the current layout states y in screen px rather than in the same
970
1003
  // units as x (LayoutResult.pixelRows). Everything that has to put the two
971
1004
  // axes in one expression reads this through scaleX/scaleY.
@@ -1157,16 +1190,7 @@ export function GraphPaneMixin() {
1157
1190
  if (!bars) {
1158
1191
  return undefined
1159
1192
  }
1160
- const samples = self.walkRowSamples
1161
- const rows = samples
1162
- ? bars.rows
1163
- .filter(r => samples.includes(r.sample))
1164
- .sort(
1165
- (a, b) =>
1166
- samples.indexOf(a.sample) - samples.indexOf(b.sample) ||
1167
- a.label.localeCompare(b.label),
1168
- )
1169
- : bars.rows
1193
+ const rows = filterSamples(bars.rows, self.walkRowSamples)
1170
1194
  const [reference, ...paired] = withCalls(
1171
1195
  [bars.reference, ...rows],
1172
1196
  repeat?.calls,
@@ -1608,8 +1632,7 @@ export function GraphPaneMixin() {
1608
1632
  // open: the span of its contig the bar covers, on the session assembly
1609
1633
  // its haplotype names (the cut's own assembly for the reference row).
1610
1634
  // The label names the haplotype when no assembly is loaded for it.
1611
- walkRowLaunchTarget(index: number) {
1612
- const bars = self.walkRowBars
1635
+ walkRowLaunchTarget(index: number, bars = self.walkRowBars) {
1613
1636
  const row = bars ? [bars.reference, ...bars.rows][index] : undefined
1614
1637
  if (!row?.axis) {
1615
1638
  return undefined
@@ -1740,6 +1763,76 @@ export function GraphPaneMixin() {
1740
1763
  return isLinearHost(view) ? view : undefined
1741
1764
  },
1742
1765
  }))
1766
+ .views(self => ({
1767
+ // Whether the strip of walk rows sits under the drawing: asked for, on a
1768
+ // standalone view whose layout draws nodes, for a graph with walks, and
1769
+ // not while a bubble is popped
1770
+ get walkStripShown() {
1771
+ return (
1772
+ self.walkStrip &&
1773
+ !self.host &&
1774
+ self.modeDrawsNodes &&
1775
+ !self.layoutResult?.tubeMap &&
1776
+ !self.walkRowBars &&
1777
+ self.popStack.length === 0 &&
1778
+ (self.graph?.paths?.length ?? 0) > 1
1779
+ )
1780
+ },
1781
+ // The strip's rows: the cut's, with the sample filter and no repeat
1782
+ // pick, which the Repeat menu only offers in walk rows
1783
+ get walkStripRows() {
1784
+ const graph = self.graph
1785
+ const bars =
1786
+ graph && this.walkStripShown
1787
+ ? walkRows(graph, self.graphRegion)
1788
+ : undefined
1789
+ return bars
1790
+ ? { ...bars, rows: filterSamples(bars.rows, self.walkRowSamples) }
1791
+ : undefined
1792
+ },
1793
+ get walkStripFrame() {
1794
+ const bars = this.walkStripRows
1795
+ if (!bars) {
1796
+ return undefined
1797
+ }
1798
+ const longest = Math.max(
1799
+ ...[bars.reference, ...bars.rows].map(r => r.label.length),
1800
+ )
1801
+ return walkStripFrame(bars, {
1802
+ width: self.paneWidth,
1803
+ maxHeight: WALK_STRIP_CEILING,
1804
+ labelPx: longest * LABEL_CHAR_PX + 16,
1805
+ })
1806
+ },
1807
+ // Whether a cut must follow every snarl a walk leaves the window by, so
1808
+ // walks come back whole: walk rows measure them, and so does the strip
1809
+ get cutsWholeWalks() {
1810
+ return (
1811
+ layoutModeByValue(self.chosenLayoutMode).wholeWalks ||
1812
+ (self.walkStrip && !self.host)
1813
+ )
1814
+ },
1815
+ // Where each walk passes the hovered node, or the selected one
1816
+ get walkStripMarks() {
1817
+ const bars = this.walkStripRows
1818
+ const id = self.hoveredNode ?? self.selectedNode
1819
+ const node = id === null ? undefined : self.nodeById?.get(id)
1820
+ return bars && node && self.graph
1821
+ ? stripMarks(self.graph, [bars.reference, ...bars.rows], node)
1822
+ : []
1823
+ },
1824
+ // Where the drawing has the node a strip point lights, for the ring
1825
+ // that finds it in a hairball
1826
+ get walkStripLocator() {
1827
+ const id = self.stripHover ? self.hoveredNode : null
1828
+ return id === null
1829
+ ? undefined
1830
+ : nodeAnchor(self.nodePositions?.[id], p => ({
1831
+ x: p.x * self.scaleX + self.translateX,
1832
+ y: p.y * self.scaleY + self.translateY,
1833
+ }))
1834
+ },
1835
+ }))
1743
1836
  .views(self => ({
1744
1837
  // Whether the drawing's x is reference bp a host can place. Walk rows
1745
1838
  // put the backbone at its bp too, but their bars are lengths, so the
@@ -2212,6 +2305,7 @@ export function GraphPaneMixin() {
2212
2305
  // `deletions` is and for the same reason — see `referenceRamp`.
2213
2306
  referenceRamp: self.referenceRamp,
2214
2307
  deletions: self.deletionEdgeIndexes,
2308
+ deletionRoutes: self.deletionRoutes,
2215
2309
  hiddenEdges: self.hiddenEdgeIndexes,
2216
2310
  // passed so the shared edge-curve cache can tell a drag from a pan
2217
2311
  version: self.positionsVersion,
@@ -2598,6 +2692,26 @@ export function GraphPaneMixin() {
2598
2692
  setPointerInPane(inside: boolean) {
2599
2693
  self.pointerInPane = inside
2600
2694
  },
2695
+ setWalkStrip(show: boolean) {
2696
+ self.walkStrip = show
2697
+ self.stripHover = null
2698
+ },
2699
+ // The pointer over the strip: the node under it becomes the hovered
2700
+ // node, so the drawing, the tooltip and a linked linear view follow
2701
+ setStripHover(hover: { row: string; offset: number } | null) {
2702
+ self.stripHover = hover
2703
+ const bars = self.walkStripRows
2704
+ const frame = self.walkStripFrame
2705
+ const row =
2706
+ hover && bars
2707
+ ? [bars.reference, ...bars.rows].find(r => r.name === hover.row)
2708
+ : undefined
2709
+ const node =
2710
+ row && frame && self.graph
2711
+ ? segmentAt(self.graph, row, hover!.offset, frame.scaleX)
2712
+ : undefined
2713
+ self.hoveredNode = node ?? null
2714
+ },
2601
2715
  setLegendSize(size: { width: number; height: number }) {
2602
2716
  self.legendSize = size
2603
2717
  },
@@ -4039,6 +4153,18 @@ export function GraphPaneMixin() {
4039
4153
  self.setShowGenes(!self.showGenes)
4040
4154
  },
4041
4155
  },
4156
+ ...(self.modeDrawsNodes && (self.graph?.paths?.length ?? 0) > 1
4157
+ ? [
4158
+ {
4159
+ type: 'checkbox' as const,
4160
+ label: 'Walk rows under the graph',
4161
+ checked: self.walkStrip,
4162
+ onClick: () => {
4163
+ self.setWalkStrip(!self.walkStrip)
4164
+ },
4165
+ },
4166
+ ]
4167
+ : []),
4042
4168
  ...(self.referenceStripApplies
4043
4169
  ? [
4044
4170
  {
@@ -2,7 +2,8 @@ import { layoutModeByValue } from '@jbrowse/bandage-core/layoutModes'
2
2
  import { readConfObject } from '@jbrowse/core/configuration'
3
3
  import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'
4
4
  import { getSession } from '@jbrowse/core/util'
5
- import { types } from '@jbrowse/mobx-state-tree'
5
+ import { addDisposer, types } from '@jbrowse/mobx-state-tree'
6
+ import { reaction } from 'mobx'
6
7
 
7
8
  import { GraphPaneMixin, MAX_GRAPH_REGION_BP, formatSpanBp } from './model'
8
9
  import {
@@ -134,7 +135,7 @@ export default function stateModelFactory() {
134
135
  return self.cutSubgraph(readConfObject(track, 'adapter'), region, {
135
136
  hops: self.subgraphContext,
136
137
  haplotypes: self.subgraphHaplotypes,
137
- ...(layoutModeByValue(self.chosenLayoutMode).wholeWalks
138
+ ...(self.cutsWholeWalks
138
139
  ? { snarls: 'overlapping' as const }
139
140
  : {}),
140
141
  })
@@ -147,13 +148,14 @@ export default function stateModelFactory() {
147
148
  retryLoad() {
148
149
  void self.load()
149
150
  },
150
- // walk rows measures whole walks, which a GBZ cut only follows when
151
- // asked, so entering or leaving it cuts the track again
151
+ // walk rows, and the strip of them under a node layout, measure whole
152
+ // walks, which a GBZ cut only follows when asked, so a change in whether
153
+ // the drawing needs them cuts the track again; the strip's toggle does
154
+ // the same (afterAttach)
152
155
  switchLayout(mode: LayoutModeValue) {
153
- const from = layoutModeByValue(self.chosenLayoutMode)
156
+ const before = self.cutsWholeWalks
154
157
  self.setLayoutMode(mode)
155
- return self.loadedTrackId &&
156
- layoutModeByValue(mode).wholeWalks !== from.wholeWalks
158
+ return self.loadedTrackId && self.cutsWholeWalks !== before
157
159
  ? self.load()
158
160
  : self.recomputeLayout()
159
161
  },
@@ -164,6 +166,20 @@ export default function stateModelFactory() {
164
166
  if (!self.graph) {
165
167
  void self.load()
166
168
  }
169
+ addDisposer(
170
+ self,
171
+ reaction(
172
+ () => self.walkStrip,
173
+ () => {
174
+ if (
175
+ self.loadedTrackId &&
176
+ !layoutModeByValue(self.chosenLayoutMode).wholeWalks
177
+ ) {
178
+ void self.load()
179
+ }
180
+ },
181
+ ),
182
+ )
167
183
  },
168
184
  }))
169
185
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '4.2.0'
1
+ export const version = '4.3.0'
@@ -1,2 +0,0 @@
1
- import{b as R,g as ie}from"./chunk-7BKUWQE5.js";import{a as se,b as ce}from"./chunk-KULNKXPT.js";import{a as Re}from"./chunk-FY3BTKY4.js";var jt=Re((Kn,Oe)=>{Oe.exports=JBrowseExports["tss-react/mui"]});function Kt(e){let t=n=>Math.round(n*100)/100;return e.map((n,r)=>`${r?"L":"M"}${t(n.x)},${t(n.y)}`).join("")}function ue(e,t,n,r,a){let o=Math.hypot(r-t,n-e);if(o===0)return[n,r];let s=(n-e)/o,c=(r-t)/o;return[n+a*s,r+a*c]}function j(e,t){let n=1-t,r=n*n*n,a=3*n*n*t,o=3*n*t*t,s=t*t*t;return{x:r*e.x0+a*e.cx0+o*e.cx1+s*e.x1,y:r*e.y0+a*e.cy0+o*e.cy1+s*e.y1}}function he(e){let t=e[Math.floor(e.length/2)];return t?j(t,.5):void 0}function xe(e){let t=1/0,n=1/0,r=-1/0,a=-1/0;for(let o of e)t=Math.min(t,o.x0,o.cx0,o.cx1,o.x1),r=Math.max(r,o.x0,o.cx0,o.cx1,o.x1),n=Math.min(n,o.y0,o.cy0,o.cy1,o.y1),a=Math.max(a,o.y0,o.cy0,o.cy1,o.y1);return{minX:t,minY:n,maxX:r,maxY:a}}function De(e,t,n){let r=j(e,t),a=j(e,n),o=(n-t)/3,s=de(e,t),c=de(e,n);return{x0:r.x,y0:r.y,cx0:r.x+s.x*o,cy0:r.y+s.y*o,cx1:a.x-c.x*o,cy1:a.y-c.y*o,x1:a.x,y1:a.y}}function de(e,t){let n=1-t,r=3*n*n,a=6*n*t,o=3*t*t;return{x:r*(e.cx0-e.x0)+a*(e.cx1-e.cx0)+o*(e.x1-e.cx1),y:r*(e.cy0-e.y0)+a*(e.cy1-e.cy0)+o*(e.y1-e.cy1)}}function Jt(e,t){let n=[];for(let r of e){let a=Math.hypot(r.cx0-r.x0,r.cy0-r.y0)+Math.hypot(r.cx1-r.cx0,r.cy1-r.cy0)+Math.hypot(r.x1-r.cx1,r.y1-r.cy1),o=Math.max(3,Math.round(a/t)|1);for(let s=0;s<o;s+=2)n.push([De(r,s/o,(s+1)/o)])}return n}function Qt(e,t,n){let r=new Array(e.length);for(let a=0;a<e.length;a++){let o=e[a];r[a]={x0:o.x0+t,y0:o.y0+n,cx0:o.cx0+t,cy0:o.cy0+n,cx1:o.cx1+t,cy1:o.cy1+n,x1:o.x1+t,y1:o.y1+n}}return r}function W(e,t){let n=e.length-1;return t==="end"?{at:e[n],inward:e[n-1]}:{at:e[0],inward:e[1]}}var He=.5,Xe=80;function me(e){let t=1/0,n=-1/0;for(let r of e)t=Math.min(t,r.x),n=Math.max(n,r.x);return{min:t,max:n}}function le(e,t){return Math.max(0,t.min-e,e-t.max)}function pe(e,t,n){let r=le(e[0].x,t),a=le(e[e.length-1].x,t);return r===a?n:r<a?"start":"end"}function ke(e,t){return{from:pe(e,me(t),"end"),to:pe(t,me(e),"start")}}var Fe=.75,ze=1.15,$e=.35,We=60;function Ye(e,t,n,r,a,o){let s=Math.hypot(n-e,r-t),c=s===0?1:(n-e)/s,u=s===0?0:(r-t)/s,d=0,f=1/0,b=-1/0;for(let l of a){let p=l.x-e,x=l.y-t,y=p*-u+x*c;Math.abs(y)>Math.abs(d)&&(d=y);let A=p*c+x*u;f=Math.min(f,A),b=Math.max(b,A)}let i=a.length?b-f:0,h=Math.max(Math.abs(d)*ze/Fe,i*$e),m=o===void 0?h:Math.min(h,o);return d<0?-m:m}function be({scaleX:e,scaleY:t}){return t/e}var Ue=3;function Y(e,t,n){let r=t.x-e.x,a=(t.y-e.y)*n,o=Math.hypot(r,a);return o===0?void 0:{x:r/o,y:a/o}}function Zt(e,t,n,r,a=Ue){let o=be(r),s=Y(e[e.length-1],t[0],o)??Y(e[0],e.at(-1),o)??Y(t[0],t.at(-1),o)??{x:1,y:0},c=[];for(let u=0;u<n;u++){let d=(u-(n-1)/2)*a;c.push({x:-s.y*d,y:s.x*d/o})}return c}function U(e,t){return e.map(n=>({x:n.x,y:n.y*t}))}function je(e,t){for(let n of e)n.y0/=t,n.cy0/=t,n.cy1/=t,n.y1/=t;return e}var Ve=12,qe=40;function fe(e,t){let n=0;for(let o=1;o<e.length;o++)n+=Math.hypot(e[o].x-e[o-1].x,e[o].y-e[o-1].y);let r=n*t;return Math.min(qe,Math.max(Ve,r/2))/Math.max(t,Number.EPSILON)}function en(e){return e.from!==e.to?!1:e.fromStrand&&e.toStrand&&e.fromStrand!==e.toStrand?e.fromStrand==="+"?"end":"start":!0}function Ke(e,t,n,r){let{at:a,inward:o}=e,s=o?a.x-o.x:1,c=o?a.y-o.y:0,u=Math.hypot(s,c)||1,d=s/u,f=c/u,b=a.x+n,i=a.y+r,h=b+d*t/3,m=i+f*t/3,l=b+d*t,p=i+f*t,x=-f*t/2,y=d*t/2;return[{x0:b,y0:i,cx0:h,cy0:m,cx1:l+x,cy1:p+y,x1:l,y1:p},{x0:l,y0:p,cx0:l-x,cy0:p-y,cx1:h,cy1:m,x1:b,y1:i}]}function V(e,t,n,r,a,o,s=[],c){let u=o.scaleX,d=be(o),f=c??(o.pixelRows?We:void 0);if(d!==1)return je(V(U(e,d),U(t,d),n,r,a*d,{scaleX:u,scaleY:u},U(s,d),f===void 0?void 0:f*d),d);if(typeof n=="string")return Ke(W(e,n),fe(e,u),r,a);let b=n?{from:"end",to:"start"}:ke(e,t),i=W(e,b.from),h=W(t,b.to),m=i.at,l=h.at,p=m.x+r,x=m.y+a,y=l.x+r,A=l.y+a;if(n){let L=1,N=0;if(i.inward){let I=m.x-i.inward.x,O=m.y-i.inward.y,C=Math.hypot(I,O);C>0&&(L=I/C,N=O/C)}let g=fe(e,u),E=-N,M=L,S=(m.x+l.x)/2+r+E*g,_=(m.y+l.y)/2+a+M*g,G=p+L*g,w=x+N*g,P=y-L*g,B=A-N*g;return[{x0:p,y0:x,cx0:G,cy0:w,cx1:G+E*g,cy1:w+M*g,x1:S,y1:_},{x0:S,y0:_,cx0:P+E*g,cy0:B+M*g,cx1:P,cy1:B,x1:y,y1:A}]}else{let L=i.inward??{x:m.x-(l.x-m.x),y:m.y-(l.y-m.y)},N=h.inward??{x:l.x-(m.x-l.x),y:l.y-(m.y-l.y)},g=Math.hypot(y-p,A-x),E=(te,ne,Ce,Ie)=>{let re=ne.x-te.x,oe=ne.y-te.y,ae=Math.hypot(re,oe),Te=ae===0?0:(Ce*re+Ie*oe)/ae;return Math.max(0,Math.min(g*He,Xe,Te*.5))},M=l.x-m.x,S=l.y-m.y,[_,G]=ue(L.x,L.y,m.x,m.y,E(L,m,M,S)),[w,P]=ue(N.x,N.y,l.x,l.y,E(N,l,-M,-S)),B=s.length?Ye(p,x,y,A,s,f):0,I=g===0?1:g,O=(y-p)/I,C=(A-x)/I,J=-C*B,Q=O*B,Z=O*Math.abs(B),ee=C*Math.abs(B);return[{x0:p,y0:x,cx0:_+r+J-Z,cy0:G+a+Q-ee,cx1:w+r+J+Z,cy1:P+a+Q+ee,x1:y,y1:A}]}}var Je=1;function Qe(e,t){return t.flatMap(n=>e[n]??[])}function ye(e,t,n){let r=e[t.from],a=e[t.to];return r?.length&&a?.length?V(r,a,t.from===t.to,0,0,n,Qe(e,t.bypassed)):void 0}function Ze(e){let t=new Map;for(let n of e.nodes)if(R(n)){let r=t.get(n.stable.refName);r?r.push(n):t.set(n.stable.refName,[n])}for(let n of t.values())n.sort((r,a)=>r.stable.start-a.stable.start);return t}function et(e,t,n){let r=[];if(e)for(let a=ie(e,t);a<e.length;a++){let o=e[a];if(o.stable.start>=n)break;o.stable.start+o.length<=n&&r.push(o.id)}return r}function tt(e,t,n,r){let{fromStrand:a,toStrand:o}=e;if(a===void 0||o===void 0)return!0;let s=a===(t.stable.strand??"+"),c=o===(n.stable.strand??"+");return r?s&&c:!s&&!c}function on(e){let t=new Map(e.nodes.map(a=>[a.id,a])),n=Ze(e),r=[];for(let a=0;a<e.edges.length;a++){let o=e.edges[a],s=t.get(o.from),c=t.get(o.to);if(s!==void 0&&c!==void 0&&R(s)&&R(c)&&s.stable.refName===c.stable.refName){let u=s.stable.start<=c.stable.start,[d,f]=u?[s,c]:[c,s],b=d.stable.start+d.length,i=f.stable.start,h=i-b;if(h>=Je&&tt(o,s,c,u)){let m=d.stable.refName;r.push({edgeIndex:a,from:o.from,to:o.to,refName:m,start:b,end:i,bp:h,bypassed:et(n.get(m),b,i)})}}}return r}function v(e){return e.endsWith("-")?"-":"+"}function ge(e){return e==="+"?"-":"+"}function nt(e){let t=(e.fromStrand??v(e.from))===v(e.from)?"end":"start",n=(e.toStrand??v(e.to))===v(e.to)?"start":"end";return{from:t,to:n}}function cn(e){let t=e.edges.map(nt),n=new Map,r=(i,h,m)=>{let l=`${i}|${h}`,p=n.get(l);p?p.push(m):n.set(l,[m])};e.edges.forEach((i,h)=>{r(i.from,t[h].from,h),r(i.to,t[h].to,h)});let a=i=>{let h=n.get(`${i}|end`);if(h?.length!==1)return;let m=h[0],l=e.edges[m];return l.from===i&&l.to!==i&&t[m].from==="end"&&t[m].to==="start"&&n.get(`${l.to}|start`)?.length===1?m:void 0},o=new Set,s=new Map,c=new Map;for(let i of e.nodes){let h=n.get(`${i.id}|start`),m=h?.length===1?e.edges[h[0]].from:void 0;if(s.has(i.id)||m!==void 0&&a(m)===h[0])continue;let l=[i.id];for(;;){let p=a(l.at(-1)),x=p===void 0?void 0:e.edges[p].to;if(x===void 0||s.has(x))break;o.add(p),l.push(x)}for(let p of l)s.set(p,i.id);l.length>1&&c.set(i.id,l)}for(let i of e.nodes)s.has(i.id)||s.set(i.id,i.id);let u=new Map(e.nodes.map(i=>[i.id,i])),d=[];for(let i of e.nodes){if(s.get(i.id)!==i.id)continue;let h=c.get(i.id);if(!h){d.push(i);continue}let m=h.map(x=>u.get(x)),l=m.filter(R),p=l.length?l.reduce((x,y)=>x.stable.start<=y.stable.start?x:y):m[0];d.push({id:i.id,name:i.name,length:m.reduce((x,y)=>x+y.length,0),depth:i.depth,stable:p.stable})}let f=new Set,b=[];return e.edges.forEach((i,h)=>{let m=s.get(i.from),l=s.get(i.to),p=t[h],x=`${m}|${p.from}>${l}|${p.to}`;o.has(h)||f.has(x)||(f.add(x),b.push({from:m,to:l,...p.from==="start"?{fromStrand:ge(v(m))}:{},...p.to==="end"?{toStrand:ge(v(l))}:{}}))}),{graph:{...e,nodes:d,edges:b,paths:void 0,pathVisits:void 0},runs:c}}function un(e,t,n){let r={...e};for(let[a,o]of t){let s=e[a];if(!s?.length)continue;delete r[a];let c=K(s),u=c.at(-1),d=o.map(i=>Math.max(n(i),0)),f=d.reduce((i,h)=>i+h,0)||o.length,b=0;o.forEach((i,h)=>{let m=f?d[h]/f*u:u/o.length,l=h===o.length-1?u:b+m;r[i]=Le(s,c,b,l),b=l})}return r}function dn(e,t,n){if(e.length===0)return[];let r=K(e),a=r.at(-1);return Le(e,r,t*a,n*a)}function Ne(e){let t=K(e);return q(e,t,t.at(-1)/2)}function K(e){let t=[0];for(let n=1;n<e.length;n++)t.push(t[n-1]+Math.hypot(e[n].x-e[n-1].x,e[n].y-e[n-1].y));return t}function q(e,t,n){if(n<=0)return{...e[0]};let r=1;for(;r<t.length&&t[r]<n;)r++;if(r>=t.length)return{...e.at(-1)};let a=t[r]-t[r-1],o=a>0?(n-t[r-1])/a:0,s=e[r-1],c=e[r];return{x:s.x+(c.x-s.x)*o,y:s.y+(c.y-s.y)*o}}function Le(e,t,n,r){let a=[q(e,t,n)];for(let o=0;o<e.length;o++)t[o]>n&&t[o]<r&&a.push({...e[o]});return a.push(q(e,t,r)),a}function rt(e){return e.length*6.2+8}function ot(e,t){return e.x0<t.x1&&e.x1>t.x0&&e.y0<t.y1&&e.y1>t.y0}function Me(e,t){return e.x1<=0||e.x0>=t.width||e.y1<=0||e.y0>=t.height}function at(e,t){return e.x0>=0&&e.x1<=t.width&&e.y0>=0&&e.y1<=t.height}function st(e,t=[]){let n=[...t];return r=>Me(r,e)||n.some(a=>ot(a,r))?!1:(n.push(r),!0)}function ln(e,t,n){let r=[],o=(s,c)=>{let u=rt(c),d=s.y>t.height/2?-1:1;for(let f=0;f<=(s.stack??0);f++){let b=s.y+d*f*19,i={x0:s.x-u/2,x1:s.x+u/2,y0:b-11-4,y1:b+4};if(Me(i,t))return;if(at(i,t)&&n(i))return{...s,text:c,y:b,w:u}}};for(let s of e){let c=o(s,s.text)??(s.fallback===void 0?void 0:o(s,s.fallback));c&&r.push(c)}return r}var ct=26,it=4e4,ut=8,dt=8,mt=5.7,lt=6,pt=13,H=3,X=2;function z(e){return(e.length*mt+lt+H)/2}var T=(pt+H)/2,ft=z("1 bp");function k(e,t,n){return{x0:t-e,x1:t+e,y0:n-T,y1:n+T}}function yn(e,t){return{x0:6,x1:6+e.length*6.2+8+H,y0:t-8-H/2,y1:t+8+H/2}}function F(e,t,n){return n<t?e:Math.min(Math.max(e,t),n)}function _e(e){return String(e).replace(/\B(?=(\d{3})+$)/g,",")}function Be(e){return e>=1e6?`${+(e/1e6).toFixed(1)} Mb`:e>=1e3?`${+(e/1e3).toFixed(1)} kb`:`${e} bp`}function Ae(e){return`${Be(e)} del`}function ht(e,t,n,r){let a=e[0],o=e[e.length-1],s=(t.x-(a.x0+o.x1)/2)*n,c=(t.y-(a.y0+o.y1)/2)*r,u=Math.hypot(s,c);return u===0?{x:0,y:-1}:{x:s/u,y:c/u}}function xt(e,t){return Math.abs(t.x)*(e.x1-e.x0)/2+Math.abs(t.y)*(e.y1-e.y0)/2}function bt(e,t){let n=(e.x1-e.x0)/2,r=(e.y1-e.y0)/2;return Math.min(t.x===0?1/0:n/Math.abs(t.x),t.y===0?1/0:r/Math.abs(t.y))}function yt(e,t,n){let r=0;for(let a=1;a<e.length;a++){let o=e[a-1],s=e[a];r+=Math.hypot((s.x-o.x)*t,(s.y-o.y)*n)}return r}function gt(e,t){let n=1/0,r=1/0,a=-1/0,o=-1/0;for(let s of e)for(let c of t[s]??[])n=Math.min(n,c.x),a=Math.max(a,c.x),r=Math.min(r,c.y),o=Math.max(o,c.y);return n===1/0?void 0:{minX:n,minY:r,maxX:a,maxY:o}}var Ee=new WeakMap;function Nt(e,t){let n=Ee.get(e);if(n?.nodeLengths===t)return n.order;let r=Object.entries(e).filter(([a,o])=>o.length>0&&t.has(a)).sort(([a],[o])=>t.get(o)-t.get(a));return Ee.set(e,{nodeLengths:t,order:r}),r}var Se=new WeakMap;function Lt(e,t,n,r){let{scaleX:a,scaleY:o}=n,s=Se.get(e);if(s?.deletions===t&&s.scaleX===a&&s.scaleY===o&&s.version===r)return s.arcs;let c=[];for(let u of[...t].sort((d,f)=>f.bp-d.bp)){let d=ye(e,u,n),f=d?he(d):void 0;if(d&&f){let{minX:b,minY:i,maxX:h,maxY:m}=xe(d);c.push({deletion:u,curves:d,apex:f,extent:Math.max((h-b)*a,(m-i)*o)})}}return Se.set(e,{deletions:t,scaleX:a,scaleY:o,version:r,arcs:c}),c}function gn({nodePositions:e,nodeLengths:t,deletions:n,alleleDeletions:r,axis:a,translateX:o,translateY:s,width:c,height:u,version:d=0}){let{scaleX:f,scaleY:b}=a,i=[],h=[];for(let{deletion:p,curves:x,apex:y,extent:A}of Lt(e,n,a,d))if(A>=ct){let L=Ae(p.bp),N=z(L),g=k(N,0,0),E=y.x*f+o,M=y.y*b+s,S=A>=N*2/X,_=ht(x,y,f,b),G=xt(g,_)+dt,w=S?E:F(E+_.x*G,N,c-N),P=S?M:F(M+_.y*G,T,u-T),B=Math.hypot(w-E,P-M),I;if(B>0){let O={x:(w-E)/B,y:(P-M)/B},C=bt(g,O);I={arcX:E,arcY:M,labelX:w-O.x*C,labelY:P-O.y*C}}i.push({label:{key:`del:${p.edgeIndex}`,text:L,x:w,y:P,kind:"deletion",leader:I},box:k(N,w,P)})}let m=new Set,l=[...r??[]].sort((p,x)=>x.bp-p.bp);for(let p of l){let x=gt(p.nodeIds,e);if(!x)continue;let y=Math.max(x.minX*f+o,0),A=Math.min(x.maxX*f+o,c),L=Math.max(x.minY*b+s,0),N=Math.min(x.maxY*b+s,u);if(A<y||N<L)continue;let g=Math.max(A-y,N-L),E=Ae(p.bp),M=z(E);if(g>=M*2/X){for(let G of p.nodeIds)m.add(G);let S=F((y+A)/2,M,c-M),_=F((L+N)/2,T,u-T);i.push({label:{key:`alleledel:${p.nodeIds.join(",")}`,text:E,x:S,y:_,kind:"deletion"},box:k(M,S,_)})}}for(let[p,x]of Nt(e,t)){if(m.has(p))continue;let{x:y,y:A}=Ne(x),L=A*b+s;if(L+T<=0||L-T>=u)continue;let N=yt(x,f,b);if(N<ft*2/X)continue;let g=Be(t.get(p)),E=z(g);if(N>=E*2/X){let M=y*f+o;h.push({label:{key:`node:${p}`,text:g,x:M,y:L,kind:"node"},box:k(E,M,L)})}}return{deletions:i,nodes:h}}function Nn(e,t){return Math.max(ut,Math.floor(e*t/it))}function Ln(e,t,n=1/0){let r=[];for(let{label:a,box:o}of e){if(r.length>=n)break;t(o)&&r.push(a)}return r}var At=/:(\d+)-\d+$/;function $(e){let t=At.exec(e);return t?{name:e.slice(0,t.index),start:+t[1]}:{name:e,start:0}}function Sn(e,t){let n=[],r=new Map;for(let a of e){let o=se(a.name),s=a.start;for(let c of a.steps){let u={path:a.name,sample:o,start:s,strand:c.strand},d=r.get(c.id);d?d.push(u):r.set(c.id,[u]),s+=t(c.id)}n.push({name:a.name,sample:o,start:a.start,length:s-a.start})}return{anchorPaths:n,pathVisits:r}}function Et(e,t){return e.find(r=>r.sample===t||r.name===t)??e[0]}var St=1;function _t(e,t,n){let r=e;if(t&&t.length>0){let a=t.find(o=>o.path===n)??t[0];r={...e,stable:{refName:a.path,start:a.start,rank:a.path===n?0:St,strand:a.strand},samples:[...new Set(t.map(o=>o.sample))].sort()}}return r}function Bt(e,t){let{anchorPaths:n,pathVisits:r}=e,a=n&&r?Et(n,t):void 0;return a&&r?{...e,nodes:e.nodes.map(o=>_t(o,r.get(o.name),a.name)),anchoredBy:"paths",referencePath:a.name}:e}function _n(e,t){return e.anchoredBy==="tags"?e:Bt(e,t)}function Bn(e,t){if(!e.anchorPaths)return;let n=new Map(e.nodes.map(o=>[o.id,o.length])),r=(o,s=o.length)=>{let c=0;for(let u=0;u<s;u++)c+=n.get(o[u])??0;return c},a=new Map;for(let o of e.paths??[]){let s=$(o.name).name;a.set(s,[...a.get(s)??[],o])}return e.anchorPaths.map(o=>{let s=a.get(o.name),c=s?.length===1?s[0]:void 0,u=c&&t.get(c.name);return c&&u?{...o,start:o.start+r(c.nodeIds,u.first),length:r(u.nodeIds)}:o})}function Cn(e){let t=0;for(let n=0;n<e.length;n++)t=e.charCodeAt(n)+((t<<5)-t);return Math.abs(t%360)}function wt(e,t){return e*360/t}var Pt=.7,Gt=.5,Ot=16;function In(e){return e>0&&e<=Ot}function Ct(e,t){return`hsl(${wt(e,t)}, ${Pt*100}%, ${Gt*100}%)`}function Tn(e,t){return`hsl(0, 0%, ${t>1?25+45*e/(t-1):45}%)`}function It(e){let t=$(e).name.split("#"),n=t.join("#");if(t.length>=3&&ce(t[0])){let r=t.slice(2).join("#");return[r,r,n]}return[t[0],t.slice(0,2).join("#"),n]}function Tt(e,t){let{start:n}=$(e);return t.map(r=>`${r} @${_e(n)}`)}function Rn(e,t){let n=new Map;e.forEach((u,d)=>{n.has(u.name)||n.set(u.name,d)});let r=[...n].map(([u,d])=>({name:u,i:d})),a=r.map(u=>It(u.name)),o=[0,1,2].flatMap(u=>[r.map((d,f)=>a[f][u]),r.map((d,f)=>Tt(d.name,a[f])[u])]),c=o.find(u=>new Set(u).size===r.length)??o.at(-1);return r.map(({name:u,i:d},f)=>({name:u,label:c[f],color:t?.[d]??Ct(d,e.length)}))}function Dn(e,t){return Math.max(e.nodeLengthPerMegabase*t/1e6,e.minimumNodeLength)}function Rt(e,t=5){let r=e.nodes.reduce((o,s)=>o+s.length,0)/1e6,a=Math.max(e.nodes.length*40,500);return{nodeLengthPerMegabase:r>0?a/r:1e4,minimumNodeLength:Math.max(5,t),edgeLength:5,nodeSegmentLength:20}}var D={power:1,effect:1};function vt(e){return e.power===1&&e.effect===1}function Dt(e,t){let n=e.nodes.length,r=e.nodes.reduce((s,c)=>s+c.length,0),a=n>0?r/n:0,o=n>0?Math.max(n*40,500)/n:40;return s=>{if(a<=0)return o;let c=(s/a)**t.power;return Math.max(o*((c-1)*t.effect+1),5)}}var we=1e3,Ht={law:D,minNodeLength:0};function Hn(e,t=Ht){let{law:n,minNodeLength:r}=t,a=Rt(e,r);if(vt(n))return{nodes:e.nodes,opts:a};let o=Dt(e,n);return{nodes:e.nodes.map(s=>({...s,length:Math.round(o(s.length)*we)})),opts:{...a,nodeLengthPerMegabase:1e6/we,minimumNodeLength:5}}}var Pe=[{value:"auto",label:"Proportional",description:"Bandage's own scale: a node draws proportional to its length, so a 1 bp allele is a speck.",law:D,minNodeFactor:0},{value:"open",label:"Open bubbles",description:"Give every allele a drawn length, so a bubble reads as two arms rather than a knot.",law:D,minNodeFactor:2.5},{value:"wide",label:"Wide bubbles",description:"As open, spread further. Best on a window of a dozen bubbles.",law:D,minNodeFactor:10},{value:"compress",label:"Compress lengths",description:"Pull the longest and shortest nodes towards the graph\u2019s mean, so a cut spanning kb and bp fits one pane.",law:{power:.5,effect:.5},minNodeFactor:0}],Fn=Pe.map(e=>e.value);function zn(e){let t=Pe.find(n=>n.value===e);return{law:t?.law??D,minNodeLength:(t?.minNodeFactor??0)*40}}var kt=[{value:"auto",label:"Auto"},{value:"uniform",label:"Uniform"},{value:"random",label:"Random"},{value:"rainbow",label:"Rainbow"},{value:"depth",label:"Depth"},{value:"node-length",label:"Node Length"},{value:"stable-rank",label:"Stable rank"},{value:"reference-position",label:"Reference position"},{value:"grey",label:"Grey"}],Wn=kt.map(e=>e.value);function Yn(e,t){return e!=="auto"?e:t?.anchoredBy?"reference-position":"uniform"}var Ft=[{value:"depth",label:"By depth",description:"Thicker the more paths carry a node, on the square root of its depth against the mean. A graph with one depth everywhere draws uniform."},{value:"uniform",label:"Uniform",description:"Every node the same thickness."}],jn=Ft.map(e=>e.value),zt=.3,Ge=3;function $t(e){let t=0,n=0;for(let r of e.nodes)t+=r.depth*r.length,n+=r.length;return n>0?t/n:0}function Wt(e,t){return!(t>0)||!(e.depth>0)?1:Math.min(Ge,Math.max(zt,Math.sqrt(e.depth/t)))}function Yt(e,t,n,r){return t*(n==="depth"?Wt(e,r):1)}function Ut(e,t){return e*(t==="depth"?Ge:1)}function Vn(e,t,n,r){let a=e?$t(e):0;return{maxHalfWidthPx:Ut(n,r)/2,halfWidthPx:o=>{let s=t?.get(o);return s?Yt(s,n,r,a)/2:n/2}}}export{$ as a,Sn as b,Et as c,Bt as d,_n as e,Bn as f,Kt as g,xe as h,Jt as i,Qt as j,be as k,Zt as l,en as m,V as n,Qe as o,on as p,cn as q,un as r,dn as s,st as t,ln as u,yn as v,_e as w,Be as x,gn as y,Nn as z,Ln as A,Cn as B,wt as C,Pt as D,Gt as E,Ot as F,In as G,Ct as H,Tn as I,Rn as J,Dn as K,Hn as L,Pe as M,Fn as N,zn as O,kt as P,Wn as Q,Yn as R,Ft as S,jn as T,$t as U,Yt as V,Vn as W,jt as X};
2
- //# sourceMappingURL=chunk-ECKLYJWP.js.map