jbrowse-plugin-graphgenomeviewer 4.2.0 → 4.4.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.
- package/dist/chunks/{GraphGenomeView-QOBHGMLE.js → GraphGenomeView-NHYVE7GZ.js} +2 -2
- package/dist/chunks/{GraphTrackSettingsDialog-RTSJTM6A.js → GraphTrackSettingsDialog-LXYUYPZS.js} +2 -2
- package/dist/chunks/{LinearGraphDisplay-TPXW6POY.js → LinearGraphDisplay-742CW5KL.js} +2 -2
- package/dist/chunks/chunk-5GUQPO7Y.js +3 -0
- package/dist/chunks/chunk-5GUQPO7Y.js.map +7 -0
- package/dist/chunks/{chunk-4C4CMI6K.js → chunk-CNFN6EVE.js} +2 -2
- package/dist/chunks/chunk-QHMGWDDH.js +2 -0
- package/dist/chunks/chunk-QHMGWDDH.js.map +7 -0
- package/dist/chunks/chunk-UTWFU4X6.js +2 -0
- package/dist/chunks/chunk-UTWFU4X6.js.map +7 -0
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +5 -5
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +4 -4
- package/package.json +2 -2
- package/src/GraphGenomeView/components/GraphCanvas.tsx +6 -53
- package/src/GraphGenomeView/components/WalkRowContextMenu.tsx +63 -0
- package/src/GraphGenomeView/components/WalkRowsOverlay.tsx +1 -1
- package/src/GraphGenomeView/components/WalkStrip.tsx +210 -0
- package/src/GraphGenomeView/components/WalkStripLocator.tsx +49 -0
- package/src/GraphGenomeView/components/usePaneGestures.ts +1 -0
- package/src/GraphGenomeView/model.test.ts +42 -3
- package/src/GraphGenomeView/model.ts +226 -105
- package/src/GraphGenomeView/viewModel.ts +23 -7
- package/src/version.ts +1 -1
- package/dist/chunks/chunk-ECKLYJWP.js +0 -2
- package/dist/chunks/chunk-ECKLYJWP.js.map +0 -7
- package/dist/chunks/chunk-HW55QCJG.js +0 -3
- package/dist/chunks/chunk-HW55QCJG.js.map +0 -7
- package/dist/chunks/chunk-SIP5IHWK.js +0 -2
- package/dist/chunks/chunk-SIP5IHWK.js.map +0 -7
- /package/dist/chunks/{GraphGenomeView-QOBHGMLE.js.map → GraphGenomeView-NHYVE7GZ.js.map} +0 -0
- /package/dist/chunks/{GraphTrackSettingsDialog-RTSJTM6A.js.map → GraphTrackSettingsDialog-LXYUYPZS.js.map} +0 -0
- /package/dist/chunks/{LinearGraphDisplay-TPXW6POY.js.map → LinearGraphDisplay-742CW5KL.js.map} +0 -0
- /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,
|
|
@@ -298,6 +303,22 @@ const VIEWPORT_PANES_BUILT = 1
|
|
|
298
303
|
// How many walk rows read their haplotype's genes, the first rows down
|
|
299
304
|
const WALK_GENE_ROWS = 40
|
|
300
305
|
|
|
306
|
+
// The rows a sample filter keeps, in the order it names them
|
|
307
|
+
function filterSamples<R extends { sample: string; label: string }>(
|
|
308
|
+
rows: R[],
|
|
309
|
+
samples: string[] | undefined,
|
|
310
|
+
) {
|
|
311
|
+
return samples
|
|
312
|
+
? rows
|
|
313
|
+
.filter(r => samples.includes(r.sample))
|
|
314
|
+
.sort(
|
|
315
|
+
(a, b) =>
|
|
316
|
+
samples.indexOf(a.sample) - samples.indexOf(b.sample) ||
|
|
317
|
+
a.label.localeCompare(b.label),
|
|
318
|
+
)
|
|
319
|
+
: rows
|
|
320
|
+
}
|
|
321
|
+
|
|
301
322
|
// What the canvas draws under the tube map, whose ink is all TubeMapOverlay's
|
|
302
323
|
const EMPTY_BATCH: RenderBatch = {
|
|
303
324
|
nodeStrokes: [],
|
|
@@ -494,7 +515,7 @@ export function GraphPaneMixin() {
|
|
|
494
515
|
// with a label that opens it. Off by default: on a base-level cut
|
|
495
516
|
// every SNP's halo is a blob.
|
|
496
517
|
showBubbles: types.optional(types.boolean, false),
|
|
497
|
-
showDeletionEdges: types.optional(types.boolean,
|
|
518
|
+
showDeletionEdges: types.optional(types.boolean, true),
|
|
498
519
|
// The session's genes drawn onto the backbone: exons along the nodes
|
|
499
520
|
// that carry them, names pinned at their midpoints. See genes/.
|
|
500
521
|
showGenes: types.optional(types.boolean, true),
|
|
@@ -517,6 +538,9 @@ export function GraphPaneMixin() {
|
|
|
517
538
|
// Samples whose walks the walk rows show, by the name before the
|
|
518
539
|
// haplotype number; undefined shows every walk the cut holds.
|
|
519
540
|
walkRowSamples: types.maybe(types.frozen<string[]>()),
|
|
541
|
+
// Walk rows in a strip under a layout that draws nodes, linked to the
|
|
542
|
+
// drawing: a node's passes tick each bar, a bar's point lights its node
|
|
543
|
+
walkStrip: types.optional(types.boolean, false),
|
|
520
544
|
// Walks lifted out of the drawing, each a layer with a lane of its own
|
|
521
545
|
// and the rest fading, coloured by the encoding it states or by the
|
|
522
546
|
// default one. See walkEncoding.ts. Empty lifts none.
|
|
@@ -622,6 +646,8 @@ export function GraphPaneMixin() {
|
|
|
622
646
|
// whether the pointer is over this pane, whose own hit test then says
|
|
623
647
|
// what it is on
|
|
624
648
|
pointerInPane: false,
|
|
649
|
+
// the strip point the pointer is over, which lights its node above
|
|
650
|
+
stripHover: null as { row: string; offset: number } | null,
|
|
625
651
|
hoveredEdge: null as number | null,
|
|
626
652
|
selectedNode: null as string | null,
|
|
627
653
|
viewportDirty: 0,
|
|
@@ -966,6 +992,9 @@ export function GraphPaneMixin() {
|
|
|
966
992
|
get alleleDeletions() {
|
|
967
993
|
return self.layoutResult?.alleleDeletions ?? []
|
|
968
994
|
},
|
|
995
|
+
get deletionRoutes() {
|
|
996
|
+
return self.layoutResult?.deletionRoutes
|
|
997
|
+
},
|
|
969
998
|
// Whether the current layout states y in screen px rather than in the same
|
|
970
999
|
// units as x (LayoutResult.pixelRows). Everything that has to put the two
|
|
971
1000
|
// axes in one expression reads this through scaleX/scaleY.
|
|
@@ -1157,16 +1186,7 @@ export function GraphPaneMixin() {
|
|
|
1157
1186
|
if (!bars) {
|
|
1158
1187
|
return undefined
|
|
1159
1188
|
}
|
|
1160
|
-
const
|
|
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
|
|
1189
|
+
const rows = filterSamples(bars.rows, self.walkRowSamples)
|
|
1170
1190
|
const [reference, ...paired] = withCalls(
|
|
1171
1191
|
[bars.reference, ...rows],
|
|
1172
1192
|
repeat?.calls,
|
|
@@ -1527,12 +1547,163 @@ export function GraphPaneMixin() {
|
|
|
1527
1547
|
},
|
|
1528
1548
|
}))
|
|
1529
1549
|
.views(self => ({
|
|
1530
|
-
// The
|
|
1550
|
+
// The contributors a view can actually be opened on: those naming an
|
|
1551
|
+
// assembly this session has loaded. Every strain of an E. coli pangenome
|
|
1552
|
+
// demo is its own assembly, so all of them resolve; an HPRC graph names
|
|
1553
|
+
// hundreds of haplotypes that no session loads, so only the reference
|
|
1554
|
+
// does.
|
|
1555
|
+
get launchableAssemblies() {
|
|
1556
|
+
return resolveContributors(
|
|
1557
|
+
withReferenceRegion(self.contributingAssemblies, self.graphRegion),
|
|
1558
|
+
self.assemblyResolver,
|
|
1559
|
+
)
|
|
1560
|
+
},
|
|
1561
|
+
// Whether there is a linear view this graph may draw a highlight into —
|
|
1562
|
+
// the paired one, or the session's only one on the reference assembly.
|
|
1563
|
+
// Read by the node menu, which offers the item only when it would land
|
|
1564
|
+
// somewhere.
|
|
1565
|
+
get canHighlightInLinearView() {
|
|
1566
|
+
const region = self.graphRegion
|
|
1567
|
+
return (
|
|
1568
|
+
region !== undefined &&
|
|
1569
|
+
linearViewTarget({
|
|
1570
|
+
views: [...getSession(self).views],
|
|
1571
|
+
connectedViewId: self.connectedViewId,
|
|
1572
|
+
assemblyName: region.assemblyName,
|
|
1573
|
+
}) !== undefined
|
|
1574
|
+
)
|
|
1575
|
+
},
|
|
1576
|
+
// Where one node can be opened: on its own assembly, and on the reference
|
|
1577
|
+
// the graph was cut against. Both are padded to a readable window — a
|
|
1578
|
+
// base-level allele is a few bp, and a linear view framed on exactly that
|
|
1579
|
+
// shows no context at all.
|
|
1580
|
+
nodeLaunchTargets(nodeId: string) {
|
|
1581
|
+
const node = self.nodeById?.get(nodeId)
|
|
1582
|
+
const own = node ? nodeOwnLocation(node) : undefined
|
|
1583
|
+
const ownAssembly = own
|
|
1584
|
+
? resolveLocationAssembly(self.assemblyResolver, own)
|
|
1585
|
+
: undefined
|
|
1586
|
+
const region = self.graphRegion
|
|
1587
|
+
const nodeById = self.nodeById
|
|
1588
|
+
const neighbors = self.nodeNeighbors
|
|
1589
|
+
const span =
|
|
1590
|
+
region && nodeById && neighbors
|
|
1591
|
+
? nodeReferenceSpan({ nodeId, nodeById, neighbors })
|
|
1592
|
+
: undefined
|
|
1593
|
+
const onReference =
|
|
1594
|
+
region && span
|
|
1595
|
+
? {
|
|
1596
|
+
sample: region.assemblyName,
|
|
1597
|
+
haplotype: undefined,
|
|
1598
|
+
refName: region.refName,
|
|
1599
|
+
...span,
|
|
1600
|
+
}
|
|
1601
|
+
: undefined
|
|
1602
|
+
return {
|
|
1603
|
+
own:
|
|
1604
|
+
own && ownAssembly
|
|
1605
|
+
? { location: paddedLocation(own), assembly: ownAssembly }
|
|
1606
|
+
: undefined,
|
|
1607
|
+
reference:
|
|
1608
|
+
region && onReference
|
|
1609
|
+
? {
|
|
1610
|
+
location: paddedLocation(onReference),
|
|
1611
|
+
assembly: region.assemblyName,
|
|
1612
|
+
}
|
|
1613
|
+
: undefined,
|
|
1614
|
+
// Unpadded: a mark is of the node, the same span its hover band
|
|
1615
|
+
// draws. The padding above is room to read a view opened on it.
|
|
1616
|
+
highlight:
|
|
1617
|
+
region && onReference
|
|
1618
|
+
? { location: onReference, assembly: region.assemblyName }
|
|
1619
|
+
: undefined,
|
|
1620
|
+
}
|
|
1621
|
+
},
|
|
1622
|
+
}))
|
|
1623
|
+
.views(self => ({
|
|
1624
|
+
// The linear view this pane draws inside, when a display hosts it: x is
|
|
1625
|
+
// that view's window and the cut is re-made when the window leaves it.
|
|
1626
|
+
// A pane that is a view of its own has none.
|
|
1627
|
+
get host(): LinearHost | undefined {
|
|
1628
|
+
let view: unknown
|
|
1629
|
+
try {
|
|
1630
|
+
view = getContainingView(self)
|
|
1631
|
+
} catch {
|
|
1632
|
+
return undefined
|
|
1633
|
+
}
|
|
1634
|
+
return isLinearHost(view) ? view : undefined
|
|
1635
|
+
},
|
|
1636
|
+
}))
|
|
1637
|
+
.views(self => ({
|
|
1638
|
+
// Whether the strip of walk rows sits under the drawing: asked for, on a
|
|
1639
|
+
// standalone view whose layout draws nodes, for a graph with walks, and
|
|
1640
|
+
// not while a bubble is popped
|
|
1641
|
+
get walkStripShown() {
|
|
1642
|
+
return (
|
|
1643
|
+
self.walkStrip &&
|
|
1644
|
+
!self.host &&
|
|
1645
|
+
self.modeDrawsNodes &&
|
|
1646
|
+
!self.layoutResult?.tubeMap &&
|
|
1647
|
+
!self.walkRowBars &&
|
|
1648
|
+
self.popStack.length === 0 &&
|
|
1649
|
+
(self.graph?.paths?.length ?? 0) > 1
|
|
1650
|
+
)
|
|
1651
|
+
},
|
|
1652
|
+
// The strip's rows: the cut's, with the sample filter and no repeat
|
|
1653
|
+
// pick, which the Repeat menu only offers in walk rows
|
|
1654
|
+
get walkStripRows() {
|
|
1655
|
+
const graph = self.graph
|
|
1656
|
+
const bars =
|
|
1657
|
+
graph && this.walkStripShown
|
|
1658
|
+
? walkRows(graph, self.graphRegion)
|
|
1659
|
+
: undefined
|
|
1660
|
+
return bars
|
|
1661
|
+
? { ...bars, rows: filterSamples(bars.rows, self.walkRowSamples) }
|
|
1662
|
+
: undefined
|
|
1663
|
+
},
|
|
1664
|
+
get walkStripFrame() {
|
|
1665
|
+
const bars = this.walkStripRows
|
|
1666
|
+
if (!bars) {
|
|
1667
|
+
return undefined
|
|
1668
|
+
}
|
|
1669
|
+
return walkStripFrame(bars, { width: self.paneWidth })
|
|
1670
|
+
},
|
|
1671
|
+
// Whether a cut must follow every snarl a walk leaves the window by, so
|
|
1672
|
+
// walks come back whole: walk rows measure them, and so does the strip
|
|
1673
|
+
get cutsWholeWalks() {
|
|
1674
|
+
return (
|
|
1675
|
+
layoutModeByValue(self.chosenLayoutMode).wholeWalks ||
|
|
1676
|
+
(self.walkStrip && !self.host)
|
|
1677
|
+
)
|
|
1678
|
+
},
|
|
1679
|
+
// Where each walk passes the hovered node, or the selected one
|
|
1680
|
+
get walkStripMarks() {
|
|
1681
|
+
const bars = this.walkStripRows
|
|
1682
|
+
const id = self.hoveredNode ?? self.selectedNode
|
|
1683
|
+
const node = id === null ? undefined : self.nodeById?.get(id)
|
|
1684
|
+
return bars && node && self.graph
|
|
1685
|
+
? stripMarks(self.graph, [bars.reference, ...bars.rows], node)
|
|
1686
|
+
: []
|
|
1687
|
+
},
|
|
1688
|
+
// Where the drawing has the node a strip point lights, for the ring
|
|
1689
|
+
// that finds it in a hairball
|
|
1690
|
+
get walkStripLocator() {
|
|
1691
|
+
const id = self.stripHover ? self.hoveredNode : null
|
|
1692
|
+
return id === null
|
|
1693
|
+
? undefined
|
|
1694
|
+
: nodeAnchor(self.nodePositions?.[id], p => ({
|
|
1695
|
+
x: p.x * self.scaleX + self.translateX,
|
|
1696
|
+
y: p.y * self.scaleY + self.translateY,
|
|
1697
|
+
}))
|
|
1698
|
+
},
|
|
1699
|
+
}))
|
|
1700
|
+
.views(self => ({
|
|
1701
|
+
// The gene reads walk rows need, as the layout or the strip: for each row whose haplotype
|
|
1531
1702
|
// (`HG00097#1`) names an assembly with a gene track, that track over the
|
|
1532
1703
|
// span of its own contig the row's bar covers. The first WALK_GENE_ROWS
|
|
1533
1704
|
// such rows are read, and the rest counted for the key.
|
|
1534
1705
|
get walkGeneReads() {
|
|
1535
|
-
const bars = self.walkRowBars
|
|
1706
|
+
const bars = self.walkRowBars ?? self.walkStripRows
|
|
1536
1707
|
if (!bars || !self.showGenes) {
|
|
1537
1708
|
return undefined
|
|
1538
1709
|
}
|
|
@@ -1608,8 +1779,7 @@ export function GraphPaneMixin() {
|
|
|
1608
1779
|
// open: the span of its contig the bar covers, on the session assembly
|
|
1609
1780
|
// its haplotype names (the cut's own assembly for the reference row).
|
|
1610
1781
|
// The label names the haplotype when no assembly is loaded for it.
|
|
1611
|
-
walkRowLaunchTarget(index: number) {
|
|
1612
|
-
const bars = self.walkRowBars
|
|
1782
|
+
walkRowLaunchTarget(index: number, bars = self.walkRowBars) {
|
|
1613
1783
|
const row = bars ? [bars.reference, ...bars.rows][index] : undefined
|
|
1614
1784
|
if (!row?.axis) {
|
|
1615
1785
|
return undefined
|
|
@@ -1639,7 +1809,7 @@ export function GraphPaneMixin() {
|
|
|
1639
1809
|
// Each row's genes as offsets along its bar: the reference row's from
|
|
1640
1810
|
// the backbone's gene track, every other row's from its own assembly
|
|
1641
1811
|
get walkRowGenes() {
|
|
1642
|
-
const bars = self.walkRowBars
|
|
1812
|
+
const bars = self.walkRowBars ?? self.walkStripRows
|
|
1643
1813
|
if (!bars || !self.showGenes) {
|
|
1644
1814
|
return undefined
|
|
1645
1815
|
}
|
|
@@ -1652,94 +1822,6 @@ export function GraphPaneMixin() {
|
|
|
1652
1822
|
: undefined
|
|
1653
1823
|
},
|
|
1654
1824
|
}))
|
|
1655
|
-
.views(self => ({
|
|
1656
|
-
// The contributors a view can actually be opened on: those naming an
|
|
1657
|
-
// assembly this session has loaded. Every strain of an E. coli pangenome
|
|
1658
|
-
// demo is its own assembly, so all of them resolve; an HPRC graph names
|
|
1659
|
-
// hundreds of haplotypes that no session loads, so only the reference
|
|
1660
|
-
// does.
|
|
1661
|
-
get launchableAssemblies() {
|
|
1662
|
-
return resolveContributors(
|
|
1663
|
-
withReferenceRegion(self.contributingAssemblies, self.graphRegion),
|
|
1664
|
-
self.assemblyResolver,
|
|
1665
|
-
)
|
|
1666
|
-
},
|
|
1667
|
-
// Whether there is a linear view this graph may draw a highlight into —
|
|
1668
|
-
// the paired one, or the session's only one on the reference assembly.
|
|
1669
|
-
// Read by the node menu, which offers the item only when it would land
|
|
1670
|
-
// somewhere.
|
|
1671
|
-
get canHighlightInLinearView() {
|
|
1672
|
-
const region = self.graphRegion
|
|
1673
|
-
return (
|
|
1674
|
-
region !== undefined &&
|
|
1675
|
-
linearViewTarget({
|
|
1676
|
-
views: [...getSession(self).views],
|
|
1677
|
-
connectedViewId: self.connectedViewId,
|
|
1678
|
-
assemblyName: region.assemblyName,
|
|
1679
|
-
}) !== undefined
|
|
1680
|
-
)
|
|
1681
|
-
},
|
|
1682
|
-
// Where one node can be opened: on its own assembly, and on the reference
|
|
1683
|
-
// the graph was cut against. Both are padded to a readable window — a
|
|
1684
|
-
// base-level allele is a few bp, and a linear view framed on exactly that
|
|
1685
|
-
// shows no context at all.
|
|
1686
|
-
nodeLaunchTargets(nodeId: string) {
|
|
1687
|
-
const node = self.nodeById?.get(nodeId)
|
|
1688
|
-
const own = node ? nodeOwnLocation(node) : undefined
|
|
1689
|
-
const ownAssembly = own
|
|
1690
|
-
? resolveLocationAssembly(self.assemblyResolver, own)
|
|
1691
|
-
: undefined
|
|
1692
|
-
const region = self.graphRegion
|
|
1693
|
-
const nodeById = self.nodeById
|
|
1694
|
-
const neighbors = self.nodeNeighbors
|
|
1695
|
-
const span =
|
|
1696
|
-
region && nodeById && neighbors
|
|
1697
|
-
? nodeReferenceSpan({ nodeId, nodeById, neighbors })
|
|
1698
|
-
: undefined
|
|
1699
|
-
const onReference =
|
|
1700
|
-
region && span
|
|
1701
|
-
? {
|
|
1702
|
-
sample: region.assemblyName,
|
|
1703
|
-
haplotype: undefined,
|
|
1704
|
-
refName: region.refName,
|
|
1705
|
-
...span,
|
|
1706
|
-
}
|
|
1707
|
-
: undefined
|
|
1708
|
-
return {
|
|
1709
|
-
own:
|
|
1710
|
-
own && ownAssembly
|
|
1711
|
-
? { location: paddedLocation(own), assembly: ownAssembly }
|
|
1712
|
-
: undefined,
|
|
1713
|
-
reference:
|
|
1714
|
-
region && onReference
|
|
1715
|
-
? {
|
|
1716
|
-
location: paddedLocation(onReference),
|
|
1717
|
-
assembly: region.assemblyName,
|
|
1718
|
-
}
|
|
1719
|
-
: undefined,
|
|
1720
|
-
// Unpadded: a mark is of the node, the same span its hover band
|
|
1721
|
-
// draws. The padding above is room to read a view opened on it.
|
|
1722
|
-
highlight:
|
|
1723
|
-
region && onReference
|
|
1724
|
-
? { location: onReference, assembly: region.assemblyName }
|
|
1725
|
-
: undefined,
|
|
1726
|
-
}
|
|
1727
|
-
},
|
|
1728
|
-
}))
|
|
1729
|
-
.views(self => ({
|
|
1730
|
-
// The linear view this pane draws inside, when a display hosts it: x is
|
|
1731
|
-
// that view's window and the cut is re-made when the window leaves it.
|
|
1732
|
-
// A pane that is a view of its own has none.
|
|
1733
|
-
get host(): LinearHost | undefined {
|
|
1734
|
-
let view: unknown
|
|
1735
|
-
try {
|
|
1736
|
-
view = getContainingView(self)
|
|
1737
|
-
} catch {
|
|
1738
|
-
return undefined
|
|
1739
|
-
}
|
|
1740
|
-
return isLinearHost(view) ? view : undefined
|
|
1741
|
-
},
|
|
1742
|
-
}))
|
|
1743
1825
|
.views(self => ({
|
|
1744
1826
|
// Whether the drawing's x is reference bp a host can place. Walk rows
|
|
1745
1827
|
// put the backbone at its bp too, but their bars are lengths, so the
|
|
@@ -2212,6 +2294,7 @@ export function GraphPaneMixin() {
|
|
|
2212
2294
|
// `deletions` is and for the same reason — see `referenceRamp`.
|
|
2213
2295
|
referenceRamp: self.referenceRamp,
|
|
2214
2296
|
deletions: self.deletionEdgeIndexes,
|
|
2297
|
+
deletionRoutes: self.deletionRoutes,
|
|
2215
2298
|
hiddenEdges: self.hiddenEdgeIndexes,
|
|
2216
2299
|
// passed so the shared edge-curve cache can tell a drag from a pan
|
|
2217
2300
|
version: self.positionsVersion,
|
|
@@ -2301,6 +2384,7 @@ export function GraphPaneMixin() {
|
|
|
2301
2384
|
? self.walkLayers.map(l => (l.color ? l : l.walk))
|
|
2302
2385
|
: undefined,
|
|
2303
2386
|
facet: self.walkLayers.length > 1 ? self.facetSpec : undefined,
|
|
2387
|
+
walkStrip: self.walkStripShown || undefined,
|
|
2304
2388
|
width: self.paneWidth,
|
|
2305
2389
|
height: self.paneCeiling,
|
|
2306
2390
|
colorScheme: self.chosenColorScheme,
|
|
@@ -2319,7 +2403,7 @@ export function GraphPaneMixin() {
|
|
|
2319
2403
|
: `${layoutModeByValue(self.chosenLayoutMode).label} draws a picture of its own, which the SVG export does not`
|
|
2320
2404
|
},
|
|
2321
2405
|
// The drawing as a standalone SVG, fitted, with its genes, its lifted
|
|
2322
|
-
// walks' keys and
|
|
2406
|
+
// walks' keys, facet panels and the strip of walk rows; see figureSvg
|
|
2323
2407
|
figure() {
|
|
2324
2408
|
const { graph, layoutResult } = self
|
|
2325
2409
|
return graph && layoutResult && !this.figureUnavailable
|
|
@@ -2340,6 +2424,11 @@ export function GraphPaneMixin() {
|
|
|
2340
2424
|
walkRows: self.walkRowBars,
|
|
2341
2425
|
rowGenes: self.walkRowGenes,
|
|
2342
2426
|
rowGeneGaps: self.walkRowGeneGaps,
|
|
2427
|
+
walkStrip: self.walkStripRows && {
|
|
2428
|
+
rows: self.walkStripRows,
|
|
2429
|
+
rowGenes: self.walkRowGenes,
|
|
2430
|
+
rowGeneGaps: self.walkRowGeneGaps,
|
|
2431
|
+
},
|
|
2343
2432
|
spec: this.figureSpec(),
|
|
2344
2433
|
})
|
|
2345
2434
|
: undefined
|
|
@@ -2598,6 +2687,26 @@ export function GraphPaneMixin() {
|
|
|
2598
2687
|
setPointerInPane(inside: boolean) {
|
|
2599
2688
|
self.pointerInPane = inside
|
|
2600
2689
|
},
|
|
2690
|
+
setWalkStrip(show: boolean) {
|
|
2691
|
+
self.walkStrip = show
|
|
2692
|
+
self.stripHover = null
|
|
2693
|
+
},
|
|
2694
|
+
// The pointer over the strip: the node under it becomes the hovered
|
|
2695
|
+
// node, so the drawing, the tooltip and a linked linear view follow
|
|
2696
|
+
setStripHover(hover: { row: string; offset: number } | null) {
|
|
2697
|
+
self.stripHover = hover
|
|
2698
|
+
const bars = self.walkStripRows
|
|
2699
|
+
const frame = self.walkStripFrame
|
|
2700
|
+
const row =
|
|
2701
|
+
hover && bars
|
|
2702
|
+
? [bars.reference, ...bars.rows].find(r => r.name === hover.row)
|
|
2703
|
+
: undefined
|
|
2704
|
+
const node =
|
|
2705
|
+
row && frame && self.graph
|
|
2706
|
+
? segmentAt(self.graph, row, hover!.offset, frame.scaleX)
|
|
2707
|
+
: undefined
|
|
2708
|
+
self.hoveredNode = node ?? null
|
|
2709
|
+
},
|
|
2601
2710
|
setLegendSize(size: { width: number; height: number }) {
|
|
2602
2711
|
self.legendSize = size
|
|
2603
2712
|
},
|
|
@@ -4039,6 +4148,18 @@ export function GraphPaneMixin() {
|
|
|
4039
4148
|
self.setShowGenes(!self.showGenes)
|
|
4040
4149
|
},
|
|
4041
4150
|
},
|
|
4151
|
+
...(self.modeDrawsNodes && (self.graph?.paths?.length ?? 0) > 1
|
|
4152
|
+
? [
|
|
4153
|
+
{
|
|
4154
|
+
type: 'checkbox' as const,
|
|
4155
|
+
label: 'Walk rows under the graph',
|
|
4156
|
+
checked: self.walkStrip,
|
|
4157
|
+
onClick: () => {
|
|
4158
|
+
self.setWalkStrip(!self.walkStrip)
|
|
4159
|
+
},
|
|
4160
|
+
},
|
|
4161
|
+
]
|
|
4162
|
+
: []),
|
|
4042
4163
|
...(self.referenceStripApplies
|
|
4043
4164
|
? [
|
|
4044
4165
|
{
|
|
@@ -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
|
-
...(
|
|
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
|
|
151
|
-
//
|
|
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
|
|
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.
|
|
1
|
+
export const version = '4.4.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
|