jbrowse-plugin-graphgenomeviewer 4.0.21 → 4.0.22
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/README.md +5 -2
- package/dist/chunks/AddTrackWorkflow-NKKGI6SM.js +2 -0
- package/dist/chunks/{AddTrackWorkflow-WYRVQYH2.js.map → AddTrackWorkflow-NKKGI6SM.js.map} +1 -1
- package/dist/chunks/{GbzBaseSyntenyAdapter-7ZKFAEJG.js → GbzBaseSyntenyAdapter-HNETLUW4.js} +2 -2
- package/dist/chunks/{GraphGenomeView-CLUMXH4K.js → GraphGenomeView-GMHRO7E4.js} +2 -2
- package/dist/chunks/{GraphGenomeView-CLUMXH4K.js.map → GraphGenomeView-GMHRO7E4.js.map} +1 -1
- package/dist/chunks/LinearGraphDisplay-I2OOPQKB.js +2 -0
- package/dist/chunks/{LinearGraphDisplay-4CMVCEM5.js.map → LinearGraphDisplay-I2OOPQKB.js.map} +1 -1
- package/dist/chunks/{MinigraphBubbleAdapter-NRDD66AK.js → MinigraphBubbleAdapter-FIFZYJ27.js} +2 -2
- package/dist/chunks/chunk-2PYVZZ3R.js +3 -0
- package/dist/chunks/chunk-2PYVZZ3R.js.map +7 -0
- package/dist/chunks/chunk-D7XFR2IV.js +2 -0
- package/dist/chunks/chunk-D7XFR2IV.js.map +7 -0
- package/dist/chunks/chunk-DMYUVGGI.js +3 -0
- package/dist/chunks/{chunk-QSH263Y6.js.map → chunk-DMYUVGGI.js.map} +4 -4
- package/dist/chunks/chunk-JXSDDOKD.js +3 -0
- package/dist/chunks/chunk-JXSDDOKD.js.map +7 -0
- package/dist/chunks/chunk-OSTJHAAK.js +2 -0
- package/dist/chunks/chunk-OSTJHAAK.js.map +7 -0
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +1 -2
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +4 -4
- package/package.json +2 -2
- package/src/GraphGenomeView/components/FacetPanels.tsx +204 -0
- package/src/GraphGenomeView/components/GenePins.tsx +12 -5
- package/src/GraphGenomeView/components/GraphCanvas.tsx +86 -42
- package/src/GraphGenomeView/components/ReferenceStripOverlay.tsx +19 -7
- package/src/GraphGenomeView/model.test.ts +36 -10
- package/src/GraphGenomeView/model.ts +130 -9
- package/src/version.ts +1 -1
- package/dist/chunks/AddTrackWorkflow-WYRVQYH2.js +0 -2
- package/dist/chunks/LinearGraphDisplay-4CMVCEM5.js +0 -2
- package/dist/chunks/chunk-3FGJLRG2.js +0 -2
- package/dist/chunks/chunk-3FGJLRG2.js.map +0 -7
- package/dist/chunks/chunk-3SZJ4AFI.js +0 -3
- package/dist/chunks/chunk-3SZJ4AFI.js.map +0 -7
- package/dist/chunks/chunk-QSH263Y6.js +0 -2
- package/dist/chunks/chunk-SCMIJAAX.js +0 -3
- package/dist/chunks/chunk-SCMIJAAX.js.map +0 -7
- package/dist/chunks/chunk-XK7OPCRQ.js +0 -2
- package/dist/chunks/chunk-XK7OPCRQ.js.map +0 -7
- /package/dist/chunks/{GbzBaseSyntenyAdapter-7ZKFAEJG.js.map → GbzBaseSyntenyAdapter-HNETLUW4.js.map} +0 -0
- /package/dist/chunks/{MinigraphBubbleAdapter-NRDD66AK.js.map → MinigraphBubbleAdapter-FIFZYJ27.js.map} +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["global-externals:@jbrowse/core/data_adapters/dataAdapterCache", "global-externals:@jbrowse/core/pluggableElementTypes", "global-externals:@mui/material/SvgIcon", "global-externals:@jbrowse/core/configuration", "global-externals:@jbrowse/core/util/colorBits", "global-externals:mobx", "global-externals:@jbrowse/render-core/canvas2dUtils", "global-externals:@jbrowse/render-core/RenderLifecycleMixin", "../../src/GetGraphReads.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/utils/createSvgIcon.mjs", "../../packages/core/src/util/geometry.ts", "../../packages/core/src/deletionEdges.ts", "../../packages/core/src/layout/mergeRuns.ts", "../../packages/core/src/overlayLabels.ts", "../../packages/core/src/graphLabels.ts", "../../packages/core/src/labelLayout.ts", "../../packages/core/src/walkEncoding.ts", "../../packages/core/src/renderer/GeometryBuilder.ts", "../../packages/core/src/renderer/colorBits.ts", "../../packages/core/src/referenceSpan.ts", "../../packages/core/src/util/edgeCurves.ts", "../../src/launchFromGraph/contributors.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/CompareArrows.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/HighlightAlt.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/LineStyle.mjs", "../../src/launchFromGraph/graphMenuItems.ts", "../../packages/core/src/tubeMap/reads.ts", "../../packages/core/src/alleleProjection/projectAlleles.ts", "../../packages/core/src/layout/placeOffReference.ts", "../../packages/core/src/layout/anchoredLayout.ts", "../../packages/core/src/layout/orderedLayout.ts", "../../packages/core/src/layout/sampleRowLayout.ts", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/types.js", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/layout.js", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/geometry.js", "../../packages/core/src/layout/tubeMapLayout.ts", "../../packages/core/src/layout/walkRows.ts", "../../packages/core/src/layout/walkRowLayout.ts", "../../packages/core/src/layoutModes.ts", "../../packages/core/src/bubbles/classifyBubble.ts", "../../packages/core/src/gfa-core/gfaParser.ts", "../../packages/core/src/gfa/gfaConverter.ts", "../../packages/core/src/layout/orientToReference.ts", "../../packages/core/src/layout/referenceSeeds.ts", "../../packages/core/src/pipeline.ts", "../../packages/core/src/referenceStrip.ts", "../../packages/core/src/tubeMap/axis.ts", "../../packages/core/src/tubeMap/connectors.ts", "../../packages/core/src/tubeMap/deviations.ts", "../../packages/core/src/tubeMap/mismatches.ts", "../../packages/core/src/tubeMap/draw.ts", "../../packages/core/src/tubeMap/genes.ts", "../../packages/core/src/viewport.ts", "../../src/GraphGenomeView/host.ts", "../../src/GraphGenomeView/repeats/walkCalls.ts", "../../packages/core/src/bubbles/bubbleHalos.ts", "../../packages/core/src/bubbles/bubblesFromGraph.ts", "../../packages/core/src/bubbles/popBubble.ts", "../../packages/core/src/genes/genePins.ts", "../../packages/core/src/layout/trimToWindow.ts", "../../packages/core/src/tubeMap/coarsen.ts", "../../packages/core/src/tubeMap/warp.ts", "../../packages/core/src/tubeMap/frame.ts", "../../packages/core/src/walkHighlight.ts", "../../src/GraphGenomeView/model.ts", "../../src/GraphGenomeView/genes/geneFeatures.ts", "../../src/GraphGenomeView/lenientEnum.ts", "../../src/GraphGenomeView/repeats/repeatFeatures.ts", "../../src/hoverSync/lgvHover.ts", "../../src/launchFromGraph/launchFromGraph.ts", "../../src/launchFromGraph/linearViewTarget.ts", "../../src/launchFromGraph/launchTracks.ts", "../../src/launchFromGraph/syntenyTracks.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/Settings.mjs"],
|
|
4
|
+
"sourcesContent": ["module.exports = JBrowseExports[\"@jbrowse/core/data_adapters/dataAdapterCache\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes\"];", "module.exports = JBrowseExports[\"@mui/material/SvgIcon\"];", "module.exports = JBrowseExports[\"@jbrowse/core/configuration\"];", "module.exports = JBrowseExports[\"@jbrowse/core/util/colorBits\"];", "module.exports = JBrowseExports[\"mobx\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/canvas2dUtils\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/RenderLifecycleMixin\"];", "import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'\nimport { RpcMethodType } from '@jbrowse/core/pluggableElementTypes'\n\nimport type { GafReads } from './gaf/gafFile'\nimport type { RpcExecuteArgs } from '@jbrowse/core/rpc/RpcRegistry'\n\n// The reads aligned over a cut's segments, from an adapter whose config names\n// a GAF (GbzBaseSyntenyAdapter's `readsLocation`). Asked after the cut is\n// parsed, because the segment names are what a GAF index is keyed by.\n\nexport interface GetGraphReadsArgs {\n adapterConfig: Record<string, unknown>\n nodeNames: string[]\n}\n\ndeclare module '@jbrowse/core/rpc/RpcRegistry' {\n interface RpcRegistry {\n GetGraphReads: {\n args: GetGraphReadsArgs\n return: GafReads\n }\n }\n}\n\ninterface ReadsAdapter {\n getReads(\n nodeNames: string[],\n opts: { signal?: AbortSignal },\n ): Promise<GafReads>\n}\n\nfunction isReadsAdapter(adapter: object): adapter is ReadsAdapter {\n return (\n 'getReads' in adapter &&\n typeof (adapter as ReadsAdapter).getReads === 'function'\n )\n}\n\nconst isNamed = (location: unknown) =>\n typeof location === 'object' &&\n location !== null &&\n Object.entries(location).some(\n ([key, value]) =>\n ['uri', 'localPath', 'blobId'].includes(key) &&\n typeof value === 'string' &&\n value !== '',\n )\n\n// Whether an adapter config as written names reads, so a graph without them\n// makes no round trip to ask\nexport function namesReads(adapterConfig: Record<string, unknown>) {\n return (\n (typeof adapterConfig.reads === 'string' && adapterConfig.reads !== '') ||\n isNamed(adapterConfig.readsLocation)\n )\n}\n\nexport default class GetGraphReads extends RpcMethodType<'GetGraphReads'> {\n name = 'GetGraphReads' as const\n\n async execute(args: RpcExecuteArgs<'GetGraphReads'>) {\n const { adapterConfig, nodeNames, sessionId, signal } = args\n const { dataAdapter } = await getAdapter(\n this.pluginManager,\n sessionId,\n adapterConfig,\n )\n return isReadsAdapter(dataAdapter)\n ? dataAdapter.getReads(nodeNames, { signal })\n : { records: [], total: 0 }\n }\n}\n", "'use client';\n\nexport { createSvgIcon as default } from '@mui/material/SvgIcon';", "import { ROW_HEIGHT_PX } from '../layout/rowSpacing'\n\nimport type { GraphEdge, NodeSegment } from '../types'\n\n// An SVG path along a polyline, rounded to two decimals.\nexport function svgPath(points: NodeSegment[]) {\n const round = (v: number) => Math.round(v * 100) / 100\n return points\n .map((p, i) => `${i ? 'L' : 'M'}${round(p.x)},${round(p.y)}`)\n .join('')\n}\n\nexport function projectLine(\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n distance: number,\n): [number, number] {\n const d = Math.hypot(y2 - y1, x2 - x1)\n if (d === 0) {\n return [x2, y2]\n }\n const vx = (x2 - x1) / d\n const vy = (y2 - y1) / d\n return [x2 + distance * vx, y2 + distance * vy]\n}\n\nexport interface BezierCurve {\n x0: number\n y0: number\n cx0: number\n cy0: number\n cx1: number\n cy1: number\n x1: number\n y1: number\n}\n\n// The cubic's own point at t, by de Casteljau. Used to place a label ON a curve\n// rather than at a position derived from what the curve was supposed to be: an\n// apex recomputed from the bulge and the endpoints agrees with the drawing only\n// as long as both derive it the same way, and they did not.\nexport function curvePointAt(c: BezierCurve, t: number) {\n const u = 1 - t\n const a = u * u * u\n const b = 3 * u * u * t\n const d = 3 * u * t * t\n const e = t * t * t\n return {\n x: a * c.x0 + b * c.cx0 + d * c.cx1 + e * c.x1,\n y: a * c.y0 + b * c.cy0 + d * c.cy1 + e * c.y1,\n }\n}\n\n// Halfway along a run of curves, measured in curves rather than in arc length: a\n// run is one cubic for every edge this is used on (computeEdgeCurves returns a\n// single curve except for self-loops), so the two agree where it matters and the\n// cheap version cannot be wrong by more than a curve.\nexport function curveMidpoint(curves: BezierCurve[]) {\n const mid = curves[Math.floor(curves.length / 2)]\n return mid ? curvePointAt(mid, 0.5) : undefined\n}\n\n// Control-polygon bounds, which contain the curve. Used to ask how big a drawn\n// edge is on screen, where the answer has to hold under any layout: a bulge\n// alone is in whatever units the layout works in.\nexport function curveBounds(curves: BezierCurve[]) {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const c of curves) {\n minX = Math.min(minX, c.x0, c.cx0, c.cx1, c.x1)\n maxX = Math.max(maxX, c.x0, c.cx0, c.cx1, c.x1)\n minY = Math.min(minY, c.y0, c.cy0, c.cy1, c.y1)\n maxY = Math.max(maxY, c.y0, c.cy0, c.cy1, c.y1)\n }\n return { minX, minY, maxX, maxY }\n}\n\n// The sub-curve over [t0, t1], exact rather than sampled: de Casteljau twice,\n// which is what keeps a dashed curve on the same path as the solid one.\nfunction subCurve(c: BezierCurve, t0: number, t1: number): BezierCurve {\n const p0 = curvePointAt(c, t0)\n const p1 = curvePointAt(c, t1)\n // The Hermite tangents of the whole cubic at t0/t1, scaled by the sub-span,\n // are the sub-curve's control-point offsets.\n const dt = (t1 - t0) / 3\n const d0 = curveTangentAt(c, t0)\n const d1 = curveTangentAt(c, t1)\n return {\n x0: p0.x,\n y0: p0.y,\n cx0: p0.x + d0.x * dt,\n cy0: p0.y + d0.y * dt,\n cx1: p1.x - d1.x * dt,\n cy1: p1.y - d1.y * dt,\n x1: p1.x,\n y1: p1.y,\n }\n}\n\nfunction curveTangentAt(c: BezierCurve, t: number) {\n const u = 1 - t\n const a = 3 * u * u\n const b = 6 * u * t\n const d = 3 * t * t\n return {\n x: a * (c.cx0 - c.x0) + b * (c.cx1 - c.cx0) + d * (c.x1 - c.cx1),\n y: a * (c.cy0 - c.y0) + b * (c.cy1 - c.cy0) + d * (c.y1 - c.cy1),\n }\n}\n\n// A curve chopped into dashes, each its own run so a renderer strokes them as\n// separate paths. Dashing is geometry here rather than a stroke style because\n// the two backends do not share one: Canvas2D has setLineDash, a GPU backend\n// tessellating these curves does not, and a dash pattern that only exists in one\n// of them is a figure that changes with the renderer.\n//\n// `dashWorld` is the period in layout units, so a caller passing screen px over\n// the current scale gets dashes of a constant on-screen size.\nexport function dashCurves(\n curves: BezierCurve[],\n dashWorld: number,\n): BezierCurve[][] {\n const out: BezierCurve[][] = []\n for (const c of curves) {\n // Control-polygon length overestimates the arc, which errs toward more\n // dashes rather than toward a curve that reads as solid.\n const polyLen =\n Math.hypot(c.cx0 - c.x0, c.cy0 - c.y0) +\n Math.hypot(c.cx1 - c.cx0, c.cy1 - c.cy0) +\n Math.hypot(c.x1 - c.cx1, c.y1 - c.cy1)\n // Odd, so a dash sits at each end of the curve and the arc reads as\n // attached to both nodes rather than floating between them.\n const spans = Math.max(3, Math.round(polyLen / dashWorld) | 1)\n for (let i = 0; i < spans; i += 2) {\n out.push([subCurve(c, i / spans, (i + 1) / spans)])\n }\n }\n return out\n}\n\nexport function translateCurves(\n curves: BezierCurve[],\n dx: number,\n dy: number,\n): BezierCurve[] {\n const out: BezierCurve[] = new Array(curves.length)\n for (let i = 0; i < curves.length; i++) {\n const c = curves[i]!\n out[i] = {\n x0: c.x0 + dx,\n y0: c.y0 + dy,\n cx0: c.cx0 + dx,\n cy0: c.cy0 + dy,\n cx1: c.cx1 + dx,\n cy1: c.cy1 + dy,\n x1: c.x1 + dx,\n y1: c.y1 + dy,\n }\n }\n return out\n}\n\ntype Side = 'start' | 'end'\n\n// The attachment point, and the point just inside the node from it \u2014 which is\n// what gives the outward tangent, so a curve leaves along the node it leaves\n// rather than straight at its partner.\nfunction attachment(segments: NodeSegment[], side: Side) {\n const last = segments.length - 1\n return side === 'end'\n ? { at: segments[last]!, inward: segments[last - 1] }\n : { at: segments[0]!, inward: segments[1] }\n}\n\n// Ceilings on how far a control point runs along its own node's direction: a\n// fraction of the separation the curve has to cover, so endpoints that nearly\n// touch cannot fold the cubic back over itself, and an absolute cap so a\n// graph-wide edge does not sprout a huge hook.\nconst TANGENT_SPAN_FACTOR = 0.5\nconst MAX_TANGENT = 80\n\n// Which drawn end of each node the edge joins.\n//\n// A link states the orientation it reads each segment in, but a node is drawn\n// in whatever direction its layout placed it \u2014 reference-forward on the\n// anchored rows, run-sweep order off them, arbitrary under FMMM \u2014 and nothing\n// records that direction, so the strands cannot be turned into drawn ends. What\n// the drawing itself says is which ends face each other, so that is what is\n// read: each node attaches at whichever of its two ends is nearer the OTHER\n// node's drawn span, with the forward reading (leave the from-node's end, enter\n// the to-node's start) winning a tie.\n//\n// Nearer to the span, not to the other attachment point. A bubble's two nodes\n// usually overlap in x, and there the distances to a point differ by a hair\n// that says nothing \u2014 enough to hang a node's entry and exit edges off the same\n// end, which draws them crossed. Overlapping spans give both ends distance 0,\n// i.e. a tie, and the tie is what keeps such a bubble forward. What survives is\n// the case the rule is for: `L s381 - s2087 +` then `L s2087 + s378 -` is\n// NCTC86 crossing its locus right to left, and joining last-point to\n// first-point regardless drew the second link 7 kb backwards across the segment\n// it rejoins \u2014 the long X in pangenome/rgfa_subgraph_launch. There the spans do\n// not overlap and the far end loses by 7 kb. Pinned by bubbleCrossing.test.ts.\nfunction spanOf(segments: NodeSegment[]) {\n let min = Infinity\n let max = -Infinity\n for (const s of segments) {\n min = Math.min(min, s.x)\n max = Math.max(max, s.x)\n }\n return { min, max }\n}\n\nfunction distanceToSpan(x: number, span: { min: number; max: number }) {\n return Math.max(0, span.min - x, x - span.max)\n}\n\nfunction facingSide(\n segments: NodeSegment[],\n other: { min: number; max: number },\n tieBreak: Side,\n): Side {\n const startGap = distanceToSpan(segments[0]!.x, other)\n const endGap = distanceToSpan(segments[segments.length - 1]!.x, other)\n return startGap === endGap ? tieBreak : startGap < endGap ? 'start' : 'end'\n}\n\nfunction facingSides(fromSegments: NodeSegment[], toSegments: NodeSegment[]) {\n return {\n from: facingSide(fromSegments, spanOf(toSegments), 'end'),\n to: facingSide(toSegments, spanOf(fromSegments), 'start'),\n }\n}\n\n// A cubic whose two control points both sit `b` off the chord reaches 3b/4 at\n// its midpoint: (P0 + 3C0 + 3C1 + P1)/8 with the endpoints on the chord.\nconst APEX_FRACTION = 0.75\n// How far past the run the apex goes, so the arc reads as passing AROUND the\n// reference it skips rather than through it.\nconst BOW_CLEARANCE = 1.15\n\n// How far, and which way, to bow an arc so that it encloses the run of nodes it\n// bypasses -- the deletion case. Both come from where that run actually lies\n// relative to this edge's own chord, which is the only thing that can answer\n// either question.\n//\n// It used to be a magnitude alone, `0.35 * the summed drawn length of the\n// bypassed nodes`, bowed to a hardcoded side. Both halves were wrong, and the\n// amylase 94.2 kb arc showed each: summed path length is not spatial reach, so a\n// chain that snakes (as an FMMM chain does) sizes an arc far past where its own\n// nodes are; and the fixed sign is a coin flip on which side those nodes fell.\n// Measured on that figure, the bypassed run reached 739 units to one side of a\n// 16-unit chord while the arc bowed 719 units to the other -- a balloon in empty\n// space, enclosing nothing, beside the run it was supposed to name. The two arcs\n// in the same figure that read correctly were the two whose runs happened to\n// fall on the hardcoded side.\n//\n// Sign comes from the FARTHEST point rather than the mean, because the arc has\n// to clear the whole run, not most of it.\n//\n// The magnitude needs two terms, and the perpendicular one alone is not enough:\n// in an anchored layout the backbone is a straight line, so a bypassed run lies\n// exactly ON the chord, reaches nowhere off it, and an arc sized by clearance\n// alone collapses back into the stub at a joint this exists to prevent. That is\n// every anchored figure, and it is what the unit tests caught. So the run's\n// extent ALONG the chord carries the collinear case, at the same 0.35 the old\n// rule used, which is what keeps those figures where they are; clearance takes\n// over exactly when the run has gone somewhere the arc would otherwise miss.\nconst ALONG_FRACTION = 0.35\n// The most bow a deletion gets in a ROW layout, in screen px, which is what y\n// already is there.\n//\n// A cap rather than a smaller fraction, because the fraction is the part that is\n// wrong: `span * ALONG_FRACTION` scales the bow with the amount of reference\n// skipped, and in a row layout the arc's own SPAN states that exactly \u2014 its two\n// feet are the deletion's two coordinates on the backbone \u2014 while the label\n// prints it in bp. So the bow carries no information there, and letting it grow\n// with the event turns a big deletion into a loop enclosing a dozen unrelated\n// nodes and edges while a small one stays the flat tie under the backbone that\n// reads correctly. Reported as \"the dashed lines frankly look weird for the\n// 'deletion'\" on `pangenome/hprc_cfhr_deletion`, whose 84.7 kb arc dived 5.5\n// rows into the rank rows while its 28.6 kb and 11.2 kb neighbours sat just\n// under the reference row: four deletions in one frame, drawn as four different\n// kinds of mark.\n//\n// Three rows: enough to clear the reference row's own tube and to read as an arc\n// across several hundred px of backbone, not enough to reach the second rank\n// row. The isotropic layout is NOT capped \u2014 there the run the arc bows around is\n// scattered rather than collinear, so the bow is what makes the bubble's two\n// arms comparable, which is the clearance term's whole job.\nconst MAX_ROW_BOW_PX = 3 * ROW_HEIGHT_PX\n\nexport function bowAround(\n p1x: number,\n p1y: number,\n p2x: number,\n p2y: number,\n toward: NodeSegment[],\n // Ceiling on the returned magnitude, in the units this function is working in.\n // Undefined leaves the bow uncapped; see MAX_ROW_BOW_PX.\n maxBow?: number,\n) {\n const chord = Math.hypot(p2x - p1x, p2y - p1y)\n const ux = chord === 0 ? 1 : (p2x - p1x) / chord\n const uy = chord === 0 ? 0 : (p2y - p1y) / chord\n let reach = 0\n let minAlong = Infinity\n let maxAlong = -Infinity\n for (const p of toward) {\n const dx = p.x - p1x\n const dy = p.y - p1y\n const perp = dx * -uy + dy * ux\n if (Math.abs(perp) > Math.abs(reach)) {\n reach = perp\n }\n const along = dx * ux + dy * uy\n minAlong = Math.min(minAlong, along)\n maxAlong = Math.max(maxAlong, along)\n }\n const span = toward.length ? maxAlong - minAlong : 0\n const uncapped = Math.max(\n (Math.abs(reach) * BOW_CLEARANCE) / APEX_FRACTION,\n span * ALONG_FRACTION,\n )\n const size = maxBow === undefined ? uncapped : Math.min(uncapped, maxBow)\n // A run that sits on the chord has no side of its own; the arc keeps the one\n // it has always taken there, so a collinear layout draws exactly as before.\n return reach < 0 ? -size : size\n}\n\n// How the drawing maps layout units to screen pixels, both axes together.\n//\n// **One object rather than two arguments, and that is the point of it.** Every\n// measurement in this file \u2014 a chord length, a tangent projection, a bow \u2014 mixes\n// x and y in one `hypot`, so it needs both. On a row layout they differ: x is\n// reference bp and y is a row pitch in screen px (LayoutResult.pixelRows), and\n// the same drop of 20 is a fifth of a pane one way and twenty bases the other.\n// Passing them separately meant a caller could pass one and default the other,\n// which compiles, draws a wrong picture, and reports nothing. Three callers\n// build these curves \u2014 the drawing, the hit index, and the label that rides them\n// \u2014 and they have to agree; a pair that cannot be half-passed is what makes that\n// structural instead of remembered.\n//\n// The two numbers are the model's own `scaleX`/`scaleY`, handed over unchanged\n// rather than pre-divided, so nothing downstream can combine an x scale from one\n// moment with a y scale from another.\nexport interface AxisScale {\n scaleX: number\n scaleY: number\n // Whether the layout's y is already SCREEN PIXELS \u2014 true for the two row\n // layouts, false for the isotropic simulation (see rowSpacing.ts and the\n // model's axisScaleOf). It rides here rather than being threaded to each\n // caller because the three functions that build a deletion's curve \u2014 the\n // drawing, the hit index and the label that rides it \u2014 all take an `axis` and\n // all three have to agree about the bow; a fourth parameter only some of them\n // passed is how a label and its arc end up in different corners of the\n // drawing, which has happened here before.\n //\n // NOT derivable as `scaleY !== scaleX`: on a row layout scaleY is pinned to 1\n // and scaleX is the zoom, so the two coincide at one zoom level and anything\n // keyed on the difference would silently stop applying there.\n pixelRows?: boolean\n}\n\n// y in x units: what makes the two axes comparable. 1 on an isotropic layout\n// (FMMM), where every number below is exactly what it was.\nexport function yToXOf({ scaleX, scaleY }: AxisScale) {\n return scaleY / scaleX\n}\n\n// How far apart two adjacent path ribbons run, in world x units.\nexport const PATH_RIBBON_SPACING = 3\n\n// A polyline's overall direction in DRAWN space, or undefined when it has none.\nfunction drawnDirection(\n a: { x: number; y: number },\n b: { x: number; y: number },\n yToX: number,\n) {\n const dx = b.x - a.x\n const dy = (b.y - a.y) * yToX\n const len = Math.hypot(dx, dy)\n return len === 0 ? undefined : { x: dx / len, y: dy / len }\n}\n\n// Where each of an edge's path ribbons is drawn, as an offset from the edge\n// itself, in world units \u2014 the one derivation of it, because three callers need\n// the same answer: the geometry that draws the ribbons, the spatial index that\n// bounds them and the hit test that measures against them. Two of those already\n// held their own copy of these four lines.\n//\n// The fan runs perpendicular to the edge, and the perpendicular is taken in\n// DRAWN space: on a row layout x is bp and y is screen px, so a perpendicular\n// taken from the raw coordinates is perpendicular to nothing on screen. `yToX`\n// converts before the direction is measured and the y component is converted\n// back, the same round trip `pointNormalsOf` makes for a node's own outline. On\n// an isotropic layout `yToX` is 1 and this is the arithmetic it always did.\n//\n// **Two abutting nodes have no chord to be perpendicular to, and in an anchored\n// layout that is every backbone link rather than a corner case**: consecutive\n// segments meet at exactly one point, so the chord is zero-length. Both callers\n// used to give up there, which left the whole backbone chain with no edge\n// geometry at all whenever `drawPaths` was on \u2014 no curve, no arrowhead, nothing\n// to hover \u2014 and the abutting nodes hid it. A node's own drawn direction answers\n// what the chord cannot, and for a backbone running along x it fans the ribbons\n// across the row, which is where they belong.\nexport function pathRibbonOffsets(\n fromSegments: NodeSegment[],\n toSegments: NodeSegment[],\n count: number,\n axis: AxisScale,\n spacing = PATH_RIBBON_SPACING,\n) {\n const yToX = yToXOf(axis)\n const dir = drawnDirection(\n fromSegments[fromSegments.length - 1]!,\n toSegments[0]!,\n yToX,\n ) ??\n drawnDirection(fromSegments[0]!, fromSegments.at(-1)!, yToX) ??\n drawnDirection(toSegments[0]!, toSegments.at(-1)!, yToX) ?? { x: 1, y: 0 }\n const offsets: { x: number; y: number }[] = []\n for (let i = 0; i < count; i++) {\n const distance = (i - (count - 1) / 2) * spacing\n offsets.push({ x: -dir.y * distance, y: (dir.x * distance) / yToX })\n }\n return offsets\n}\n\nfunction scaleYOf(points: NodeSegment[], yToX: number) {\n return points.map(p => ({ x: p.x, y: p.y * yToX }))\n}\n\nfunction unscaleYOf(curves: BezierCurve[], yToX: number) {\n for (const c of curves) {\n c.y0 /= yToX\n c.cy0 /= yToX\n c.cy1 /= yToX\n c.y1 /= yToX\n }\n return curves\n}\n\n// How far a self-loop or hairpin reaches off its node, in layout units: half\n// the node's drawn length, held between two screen sizes. Bandage uses a fixed\n// `edgeLength` in scene units, which fits one layout's units and no other's: a\n// fixed 50 units was 50 kb of drawing under the engine (1 unit per kb) and 50\n// bp on an anchored row, and under the engine it drew a 100 px oval around a\n// node a tenth that long.\nconst SELF_LOOP_MIN_PX = 12\nconst SELF_LOOP_MAX_PX = 40\n\nfunction selfLoopReach(segments: NodeSegment[], scale: number) {\n let length = 0\n for (let i = 1; i < segments.length; i++) {\n length += Math.hypot(\n segments[i]!.x - segments[i - 1]!.x,\n segments[i]!.y - segments[i - 1]!.y,\n )\n }\n const nodePx = length * scale\n const reachPx = Math.min(\n SELF_LOOP_MAX_PX,\n Math.max(SELF_LOOP_MIN_PX, nodePx / 2),\n )\n return reachPx / Math.max(scale, Number.EPSILON)\n}\n\n// How an edge joins a node to itself: `true` is a loop from the end back\n// round to the start, a Side is a hairpin that leaves that end and returns to\n// it, and `false` is an edge between two nodes.\nexport type SelfLink = boolean | Side\n\n// `L a + a -` leaves a's end and arrives at the start of a read backwards,\n// which is a's end again, and `L a - a +` is the same at its start. Drawn end\n// to start like a cycle it named the wrong ends, which is the difference\n// between an inverted repeat and a tandem one. Bandage draws the same case\n// with its own path (makeSpecialPathConnectingNodeToReverseComplement).\nexport function selfLinkOf(edge: GraphEdge): SelfLink {\n if (edge.from !== edge.to) {\n return false\n }\n return edge.fromStrand && edge.toStrand && edge.fromStrand !== edge.toStrand\n ? edge.fromStrand === '+'\n ? 'end'\n : 'start'\n : true\n}\n\n// A teardrop off one end, after Bandage's: it leaves along the node, turns at\n// the apex and comes back in on itself.\nfunction hairpinCurves(\n attach: { at: NodeSegment; inward: NodeSegment | undefined },\n reach: number,\n offsetX: number,\n offsetY: number,\n): BezierCurve[] {\n const { at, inward } = attach\n const dx = inward ? at.x - inward.x : 1\n const dy = inward ? at.y - inward.y : 0\n const len = Math.hypot(dx, dy) || 1\n const ux = dx / len\n const uy = dy / len\n const px = at.x + offsetX\n const py = at.y + offsetY\n const nearX = px + (ux * reach) / 3\n const nearY = py + (uy * reach) / 3\n const apexX = px + ux * reach\n const apexY = py + uy * reach\n const sideX = (-uy * reach) / 2\n const sideY = (ux * reach) / 2\n return [\n {\n x0: px,\n y0: py,\n cx0: nearX,\n cy0: nearY,\n cx1: apexX + sideX,\n cy1: apexY + sideY,\n x1: apexX,\n y1: apexY,\n },\n {\n x0: apexX,\n y0: apexY,\n cx0: apexX - sideX,\n cy0: apexY - sideY,\n cx1: nearX,\n cy1: nearY,\n x1: px,\n y1: py,\n },\n ]\n}\n\nexport function computeEdgeCurves(\n fromSegments: NodeSegment[],\n toSegments: NodeSegment[],\n selfLink: SelfLink,\n offsetX: number,\n offsetY: number,\n axis: AxisScale,\n // The nodes this edge bypasses, for a deletion; empty for every other edge.\n // Given them, the curve bows around them.\n //\n // Topologically a deletion is a bubble whose reference arm is a run of long\n // nodes and whose own arm is a bare link, so at the engine's 5-unit edge\n // length it collapses into a stub at a joint and the one event a graph shows\n // better than a linear view is invisible. Bowing it around that run is what\n // makes the two arms comparable, which is what a reader has to see to read it\n // as an alternative route.\n //\n // Taken as nodes rather than as a precomputed number because the chord is\n // needed to turn them into one, and only this function knows the chord -- it\n // picks the attachment ends. Three callers build this curve (the drawing, the\n // hit index and the label that rides it) and a second derivation of it is a\n // second thing to keep in step; that is exactly how the label and the arc once\n // came out in different corners of the drawing.\n bypassed: NodeSegment[] = [],\n // Only the recursion below passes this: the cap belongs to the layout, so it\n // is read off `axis` at the top level and handed down already converted into\n // the isotropic body's units.\n maxBowIn?: number,\n): BezierCurve[] {\n const scale = axis.scaleX\n const yToX = yToXOf(axis)\n const maxBow = maxBowIn ?? (axis.pixelRows ? MAX_ROW_BOW_PX : undefined)\n if (yToX !== 1) {\n // Recur once with y already in x units, then put the answer back. The\n // isotropic body below is the only implementation, so an anisotropic\n // drawing cannot drift from the one every force-directed figure uses.\n //\n // It costs three small arrays per edge, which was worth measuring before\n // being clever about: over 8,000 deletion-bearing edges \u2014 the top of the\n // range this view draws at all \u2014 the whole pass is 1.9 ms isotropic against\n // 2.8 ms here, once per debounced rebuild, against the ~9 ms of geometry the\n // toolbar already reports. Inlining the conversion would buy 0.9 ms and cost\n // the single implementation, so it is not done.\n return unscaleYOf(\n computeEdgeCurves(\n scaleYOf(fromSegments, yToX),\n scaleYOf(toSegments, yToX),\n selfLink,\n offsetX,\n offsetY * yToX,\n { scaleX: scale, scaleY: scale },\n scaleYOf(bypassed, yToX),\n // the cap is a y measure and the body works in x units, so it converts\n // with everything else that crosses this boundary\n maxBow === undefined ? undefined : maxBow * yToX,\n ),\n yToX,\n )\n }\n if (typeof selfLink === 'string') {\n return hairpinCurves(\n attachment(fromSegments, selfLink),\n selfLoopReach(fromSegments, scale),\n offsetX,\n offsetY,\n )\n }\n const sides = selfLink\n ? { from: 'end' as Side, to: 'start' as Side }\n : facingSides(fromSegments, toSegments)\n const fromAttach = attachment(fromSegments, sides.from)\n const toAttach = attachment(toSegments, sides.to)\n const fromEnd = fromAttach.at\n const toStart = toAttach.at\n\n const p1x = fromEnd.x + offsetX\n const p1y = fromEnd.y + offsetY\n const p2x = toStart.x + offsetX\n const p2y = toStart.y + offsetY\n\n if (selfLink) {\n let segDirX = 1\n let segDirY = 0\n if (fromAttach.inward) {\n const dx = fromEnd.x - fromAttach.inward.x\n const dy = fromEnd.y - fromAttach.inward.y\n const len = Math.hypot(dx, dy)\n if (len > 0) {\n segDirX = dx / len\n segDirY = dy / len\n }\n }\n\n const ext = selfLoopReach(fromSegments, scale)\n const perpX = -segDirY\n const perpY = segDirX\n const midX = (fromEnd.x + toStart.x) / 2 + offsetX + perpX * ext\n const midY = (fromEnd.y + toStart.y) / 2 + offsetY + perpY * ext\n const cp1x = p1x + segDirX * ext\n const cp1y = p1y + segDirY * ext\n const cp2x = p2x - segDirX * ext\n const cp2y = p2y - segDirY * ext\n\n return [\n {\n x0: p1x,\n y0: p1y,\n cx0: cp1x,\n cy0: cp1y,\n cx1: cp1x + perpX * ext,\n cy1: cp1y + perpY * ext,\n x1: midX,\n y1: midY,\n },\n {\n x0: midX,\n y0: midY,\n cx0: cp2x + perpX * ext,\n cy0: cp2y + perpY * ext,\n cx1: cp2x,\n cy1: cp2y,\n x1: p2x,\n y1: p2y,\n },\n ]\n } else {\n // The point just inside each node from where the edge attaches, so\n // projectLine extends OUTWARD from the node. A node drawn as a single point\n // has no direction of its own; mirroring the chord through the attachment\n // leaves it pointing at its partner, i.e. a straight leader.\n const fromPrev = fromAttach.inward ?? {\n x: fromEnd.x - (toStart.x - fromEnd.x),\n y: fromEnd.y - (toStart.y - fromEnd.y),\n }\n const toNext = toAttach.inward ?? {\n x: toStart.x - (fromEnd.x - toStart.x),\n y: toStart.y - (fromEnd.y - toStart.y),\n }\n\n const dist = Math.hypot(p2x - p1x, p2y - p1y)\n // Each control point leaves along its OWN node's direction, so the tangent\n // extension may only run as far as the other endpoint actually lies in that\n // direction. Sizing it by the whole separation instead cannot work on an\n // anchored layout, where the separation is mostly the row drop: both edges\n // of a bubble then leave sideways and come back, and the pair draws as a\n // crossed bowtie rather than as a bubble (pinned by bubbleCrossing.test.ts,\n // which fails on 17 of the pggb subgraph's alleles under a flat\n // `dist * 0.35`). Projecting the separation onto the tangent is rotation\n // invariant, so a force-directed layout, whose edges do run along their\n // nodes, is unchanged; and it is clamped at 0, so an edge that doubles back\n // gets a straight leader instead of a loop.\n const tangentProj = (\n prev: { x: number; y: number },\n at: { x: number; y: number },\n towardX: number,\n towardY: number,\n ) => {\n const dx = at.x - prev.x\n const dy = at.y - prev.y\n const len = Math.hypot(dx, dy)\n const along = len === 0 ? 0 : (towardX * dx + towardY * dy) / len\n return Math.max(\n 0,\n Math.min(dist * TANGENT_SPAN_FACTOR, MAX_TANGENT, along * 0.5),\n )\n }\n const towardToX = toStart.x - fromEnd.x\n const towardToY = toStart.y - fromEnd.y\n const [cx1, cy1] = projectLine(\n fromPrev.x,\n fromPrev.y,\n fromEnd.x,\n fromEnd.y,\n tangentProj(fromPrev, fromEnd, towardToX, towardToY),\n )\n const [cx2, cy2] = projectLine(\n toNext.x,\n toNext.y,\n toStart.x,\n toStart.y,\n tangentProj(toNext, toStart, -towardToX, -towardToY),\n )\n\n // Perpendicular to the chord, so the bow is symmetric about it, and spread\n // the two control points APART along it. Perpendicular alone draws a hairpin\n // whenever the bulge exceeds the chord \u2014 which is the normal case for a\n // deletion, whose endpoints the simulation leaves adjacent however much\n // reference it skips \u2014 and a hairpin reads as a stray line rather than as a\n // route. The along-chord spread opens it into an arch.\n //\n // The spread takes the MAGNITUDE, not the signed bulge: it decides how open\n // the arch is, not which way it faces, and letting it flip swaps the two\n // control points past each other and folds the cubic into a cusp.\n const bulge = bypassed.length\n ? bowAround(p1x, p1y, p2x, p2y, bypassed, maxBow)\n : 0\n const chordLen = dist === 0 ? 1 : dist\n const ux = (p2x - p1x) / chordLen\n const uy = (p2y - p1y) / chordLen\n const bulgeX = -uy * bulge\n const bulgeY = ux * bulge\n const spreadX = ux * Math.abs(bulge)\n const spreadY = uy * Math.abs(bulge)\n\n return [\n {\n x0: p1x,\n y0: p1y,\n cx0: cx1 + offsetX + bulgeX - spreadX,\n cy0: cy1 + offsetY + bulgeY - spreadY,\n cx1: cx2 + offsetX + bulgeX + spreadX,\n cy1: cy2 + offsetY + bulgeY + spreadY,\n x1: p2x,\n y1: p2y,\n },\n ]\n }\n}\n", "import { firstNodeAtOrAfter, isBackbone } from './anchoredNodes'\nimport { computeEdgeCurves } from './util/geometry'\n\nimport type { AnchoredNode } from './anchoredNodes'\nimport type { Graph, GraphEdge } from './types'\nimport type { AxisScale } from './util/geometry'\n\n// A deletion is the one kind of variation this view could not draw, and the\n// reason is structural rather than cosmetic: extra sequence is a *node*, so it\n// gets a tube, a colour and a hover. Missing sequence is an **edge** \u2014 a link\n// from one backbone segment to another that is not its neighbour \u2014 and every\n// edge in the drawing was painted the same grey, so the graph said nothing about\n// the events a linear view is worst at.\n//\n// It needs no new data. Both endpoints of such an edge carry rGFA coordinates on\n// the same stable sequence, so the bp the haplotype skips is the gap between\n// them, and the whole classification is arithmetic on SN/SO plus the segment\n// lengths already parsed. `agent-docs/reference/PANGENOME_GRAPHS.md` in\n// jbrowse-components measured what this finds on the hosted HPRC graph: 8\n// deletions in the 200 kb MHC class II window against 78 attributed alleles, 1\n// in the 70 kb C4 window.\n//\n// Two things this deliberately does not do:\n//\n// - it does not attribute the deletion to a haplotype. A backbone-to-backbone\n// skip has GRCh38 at both ends, so there is no `SN` naming a donor, and the\n// same reference note records why: a deletion only carries a donor when it\n// also carries novel sequence, and then it is not a clean deletion any more.\n// - it does not treat a same-position link as a deletion. Adjacent backbone\n// segments abut (gap 0) and a substitution's flanks can overlap by the\n// aligned bases, so only a positive gap counts.\n\n// A skip has to clear this to be drawn as a deletion. minigraph's SV-resolution\n// graph does not record anything smaller than tens of bp, so this is not a\n// biological threshold but a guard against calling a rounding artifact a\n// deletion \u2014 abutting segments are the common case and they must not light up.\nconst MIN_DELETION_BP = 1\n\nexport interface DeletionEdge {\n // index into graph.edges, which is what the renderer keys its curve ranges by\n edgeIndex: number\n // the edge's own endpoints, so anything that needs the arc as it is DRAWN can\n // rebuild it (deletionArcCurves) instead of approximating it from `bypassed`\n from: string\n to: string\n refName: string\n start: number\n end: number\n bp: number\n // Backbone node ids whose reference interval lies inside the skipped span:\n // the route a haplotype taking this edge does NOT take. A drawing sizes the\n // deletion's arc off their drawn length so the two arms of the bubble are\n // comparable; nothing else can supply that, because in a force layout the\n // edge's own endpoints are wherever the simulation put them.\n bypassed: string[]\n}\n\n// Where the backbone this deletion bypasses is drawn, as points. `bowAround`\n// turns them into how far and which way the arc bows; this only has to say which\n// points they are, and is shared so the geometry that draws the arc, the index\n// that hit-tests it and the overlay that labels it all bow around the same run.\nexport function bypassedPoints(\n nodePositions: Record<string, { x: number; y: number }[]>,\n bypassed: string[],\n) {\n return bypassed.flatMap(id => nodePositions[id] ?? [])\n}\n\n// The deletion's arc exactly as the renderer draws it, for anything that has to\n// agree with the drawing: the label that rides it, and hit detection.\n//\n// This exists because the label used to be placed at an apex derived from the\n// BYPASSED nodes while the curve was drawn between the EDGE's endpoints. In an\n// anchored layout the bypassed run lies between the endpoints, so the two agreed\n// and the divergence was invisible; under FMMM the endpoints are wherever the\n// simulation left them and the bypassed run is elsewhere, so the words and the\n// arc they named came out in different corners of the drawing\n// (`pangenome/hprc_cfhr_deletion`, reviewed 2026-07-31). One function is the fix:\n// a second derivation of the same curve is a second thing to keep in step.\nexport function deletionArcCurves(\n nodePositions: Record<string, { x: number; y: number }[]>,\n deletion: DeletionEdge,\n axis: AxisScale,\n) {\n const from = nodePositions[deletion.from]\n const to = nodePositions[deletion.to]\n return from?.length && to?.length\n ? computeEdgeCurves(\n from,\n to,\n deletion.from === deletion.to,\n 0,\n 0,\n axis,\n bypassedPoints(nodePositions, deletion.bypassed),\n )\n : undefined\n}\n\n// The backbone by stable sequence, each sorted by start, so the run a deletion\n// bypasses is a slice rather than a scan. Filtering the whole backbone per\n// candidate edge is O(edges x backbone), which at the node budget this view\n// accepts (20,000) is hundreds of millions of comparisons for a pass that runs\n// once per graph.\nfunction backboneBySequence(graph: Graph) {\n const bySequence = new Map<string, AnchoredNode[]>()\n for (const node of graph.nodes) {\n if (isBackbone(node)) {\n const list = bySequence.get(node.stable.refName)\n if (list) {\n list.push(node)\n } else {\n bySequence.set(node.stable.refName, [node])\n }\n }\n }\n for (const list of bySequence.values()) {\n list.sort((a, b) => a.stable.start - b.stable.start)\n }\n return bySequence\n}\n\n// Backbone nodes lying wholly inside [start, end) on `refName`. Sorted by\n// start, so the candidates are one contiguous run and the walk stops at the\n// first node starting past the end; only the upper bound still needs testing\n// per node, since a long node can start inside and finish outside.\nfunction bypassedNodes(\n nodes: AnchoredNode[] | undefined,\n start: number,\n end: number,\n) {\n const bypassed: string[] = []\n if (nodes) {\n for (let i = firstNodeAtOrAfter(nodes, start); i < nodes.length; i++) {\n const node = nodes[i]!\n if (node.stable.start >= end) {\n break\n }\n if (node.stable.start + node.length <= end) {\n bypassed.push(node.id)\n }\n }\n }\n return bypassed\n}\n\n// A link leaves the END of `from` as it reads it and arrives at the START of\n// `to`. Read the way the reference reads a node, its end is its right side, so\n// a link leaves `from` on the right when the two readings agree and arrives at\n// `to` on the left when they do.\n//\n// A deletion joins the right side of the left node to the left side of the\n// right one, from whichever strand the file states it: `L 1 + 3 +`, or the\n// same link as `L 3 - 1 -`. `L 3 + 1 +` joins the right side of the RIGHT node\n// to the left side of the left one, sequence read again, a duplication. A link\n// with one reading against the reference lands on a node's far side, an\n// inversion. Neither skips anything. Coordinates alone could not tell them\n// from a deletion: each was drawn as one, and hidden with them by default.\n//\n// A link stating no strands is judged by its coordinates, as all once were.\nfunction skipsBetween(\n edge: GraphEdge,\n from: AnchoredNode,\n to: AnchoredNode,\n forwards: boolean,\n) {\n const { fromStrand, toStrand } = edge\n if (fromStrand === undefined || toStrand === undefined) {\n return true\n }\n const leavesRight = fromStrand === (from.stable.strand ?? '+')\n const arrivesLeft = toStrand === (to.stable.strand ?? '+')\n return forwards ? leavesRight && arrivesLeft : !leavesRight && !arrivesLeft\n}\n\n// Every edge that skips reference sequence, in graph.edges order.\nexport function deletionEdges(graph: Graph): DeletionEdge[] {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const backbone = backboneBySequence(graph)\n const found: DeletionEdge[] = []\n for (let edgeIndex = 0; edgeIndex < graph.edges.length; edgeIndex++) {\n const edge = graph.edges[edgeIndex]!\n const from = byId.get(edge.from)\n const to = byId.get(edge.to)\n if (\n from !== undefined &&\n to !== undefined &&\n isBackbone(from) &&\n isBackbone(to) &&\n from.stable.refName === to.stable.refName\n ) {\n // Which endpoint is upstream comes from the coordinates, since a file\n // may state a link from either strand. Whether it SKIPS what lies\n // between comes from its sides: a deletion leaves the right side of the\n // left node and arrives at the left side of the right one.\n const forwards = from.stable.start <= to.stable.start\n const [left, right] = forwards ? [from, to] : [to, from]\n const start = left.stable.start + left.length\n const end = right.stable.start\n const bp = end - start\n if (bp >= MIN_DELETION_BP && skipsBetween(edge, from, to, forwards)) {\n const refName = left.stable.refName\n found.push({\n edgeIndex,\n from: edge.from,\n to: edge.to,\n refName,\n start,\n end,\n bp,\n bypassed: bypassedNodes(backbone.get(refName), start, end),\n })\n }\n }\n }\n return found\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Graph, GraphEdge, GraphNode, NodeSegment } from '../types'\n\n// A base-level graph chops its sequence into short nodes at every variant and\n// every kilobase, so a cut of it is thousands of nodes strung in unbranching\n// runs. FMMM only needs the runs: each becomes one node, laid out as one\n// chain, and its positions are split back onto the members afterwards, so the\n// drawing, the hit index and the labels never see the merged graph.\n//\n// A node joins the run before it when the run's end side and the node's start\n// side each hold exactly one link, and it is that link read forward. Sides, not\n// in- and out-edges: a graph holds one node per segment, so `L a + b -` joins\n// b's END, and counting it as an in-edge of b both merged across it and handed\n// the engine a link attached at the wrong end. A self loop keeps its node alone.\n\nexport interface MergedRuns {\n graph: Graph\n // merged node id to the member ids in chain order; a run of one is absent\n runs: Map<string, string[]>\n}\n\ntype Side = 'start' | 'end'\n\n// The strand a node's id carries, which is the one its drawn chain reads.\nfunction ownStrand(id: string) {\n return id.endsWith('-') ? '-' : '+'\n}\n\nfunction flip(strand: '+' | '-') {\n return strand === '+' ? '-' : '+'\n}\n\nfunction sidesOf(edge: GraphEdge) {\n const from: Side =\n (edge.fromStrand ?? ownStrand(edge.from)) === ownStrand(edge.from)\n ? 'end'\n : 'start'\n const to: Side =\n (edge.toStrand ?? ownStrand(edge.to)) === ownStrand(edge.to)\n ? 'start'\n : 'end'\n return { from, to }\n}\n\nexport function mergeRuns(graph: Graph): MergedRuns {\n const sides = graph.edges.map(sidesOf)\n // node side -> indexes of the edges attached there\n const at = new Map<string, number[]>()\n const attach = (id: string, side: Side, ei: number) => {\n const key = `${id}|${side}`\n const list = at.get(key)\n if (list) {\n list.push(ei)\n } else {\n at.set(key, [ei])\n }\n }\n graph.edges.forEach((edge, ei) => {\n attach(edge.from, sides[ei]!.from, ei)\n attach(edge.to, sides[ei]!.to, ei)\n })\n // the edge that carries node `a`'s run on into the next node, if one does\n const joinAfter = (a: string) => {\n const out = at.get(`${a}|end`)\n if (out?.length !== 1) {\n return undefined\n }\n const ei = out[0]!\n const edge = graph.edges[ei]!\n return edge.from === a &&\n edge.to !== a &&\n sides[ei]!.from === 'end' &&\n sides[ei]!.to === 'start' &&\n at.get(`${edge.to}|start`)?.length === 1\n ? ei\n : undefined\n }\n const joined = new Set<number>()\n const runOf = new Map<string, string>()\n const runs = new Map<string, string[]>()\n for (const node of graph.nodes) {\n const into = at.get(`${node.id}|start`)\n const pred = into?.length === 1 ? graph.edges[into[0]!]!.from : undefined\n if (\n runOf.has(node.id) ||\n (pred !== undefined && joinAfter(pred) === into![0])\n ) {\n continue\n }\n const run = [node.id]\n for (;;) {\n const ei = joinAfter(run.at(-1)!)\n const next = ei === undefined ? undefined : graph.edges[ei]!.to\n if (next === undefined || runOf.has(next)) {\n break\n }\n joined.add(ei!)\n run.push(next)\n }\n for (const id of run) {\n runOf.set(id, node.id)\n }\n if (run.length > 1) {\n runs.set(node.id, run)\n }\n }\n // a cycle with no branch has no run start; the loop above never reached it\n for (const node of graph.nodes) {\n if (!runOf.has(node.id)) {\n runOf.set(node.id, node.id)\n }\n }\n\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const nodes: GraphNode[] = []\n for (const node of graph.nodes) {\n if (runOf.get(node.id) !== node.id) {\n continue\n }\n const run = runs.get(node.id)\n if (!run) {\n nodes.push(node)\n continue\n }\n const members = run.map(id => byId.get(id)!)\n const backbone = members.filter(isBackbone)\n const first = backbone.length\n ? backbone.reduce((a, b) => (a.stable.start <= b.stable.start ? a : b))\n : members[0]!\n nodes.push({\n id: node.id,\n name: node.name,\n length: members.reduce((sum, n) => sum + n.length, 0),\n depth: node.depth,\n stable: first.stable,\n })\n }\n // A link can only reach a run at its free ends, the first member's start and\n // the last's end, since every inner side holds exactly the join. So a\n // member's side is the run's side, and a flipped one crosses over as the\n // strand opposite the run's own.\n const seen = new Set<string>()\n const edges: GraphEdge[] = []\n graph.edges.forEach((edge, ei) => {\n const from = runOf.get(edge.from)!\n const to = runOf.get(edge.to)!\n const side = sides[ei]!\n const key = `${from}|${side.from}>${to}|${side.to}`\n if (joined.has(ei) || seen.has(key)) {\n return\n }\n seen.add(key)\n edges.push({\n from,\n to,\n ...(side.from === 'start' ? { fromStrand: flip(ownStrand(from)) } : {}),\n ...(side.to === 'end' ? { toStrand: flip(ownStrand(to)) } : {}),\n })\n })\n return {\n graph: { ...graph, nodes, edges, paths: undefined, pathVisits: undefined },\n runs,\n }\n}\n\n// Each member takes the stretch of its run's polyline its weight entitles it\n// to, in chain order, with the shared points interpolated so the members abut.\nexport function splitRuns(\n positions: Record<string, NodeSegment[]>,\n runs: Map<string, string[]>,\n weightOf: (id: string) => number,\n) {\n const out: Record<string, NodeSegment[]> = { ...positions }\n for (const [id, members] of runs) {\n const line = positions[id]\n if (!line?.length) {\n continue\n }\n delete out[id]\n const arc = arcPrefix(line)\n const total = arc.at(-1)!\n const weights = members.map(m => Math.max(weightOf(m), 0))\n const weightSum = weights.reduce((a, b) => a + b, 0) || members.length\n let from = 0\n members.forEach((member, k) => {\n const share = weightSum\n ? (weights[k]! / weightSum) * total\n : total / members.length\n const to = k === members.length - 1 ? total : from + share\n out[member] = slice(line, arc, from, to)\n from = to\n })\n }\n return out\n}\n\n// The stretch of a polyline between two fractions of its arc length, with the\n// ends interpolated. What splitRuns cuts a run with, and what the gene pins cut\n// a backbone node with to place an exon.\nexport function polylineSlice(line: NodeSegment[], from: number, to: number) {\n if (line.length === 0) {\n return []\n }\n const arc = arcPrefix(line)\n const total = arc.at(-1)!\n return slice(line, arc, from * total, to * total)\n}\n\n// The point halfway along a polyline by arc length, where a node's label goes.\nexport function polylineMidpoint(line: NodeSegment[]): NodeSegment {\n const arc = arcPrefix(line)\n return pointAt(line, arc, arc.at(-1)! / 2)\n}\n\n// Cumulative distance to each point of a polyline, starting at 0.\nfunction arcPrefix(line: NodeSegment[]) {\n const arc = [0]\n for (let i = 1; i < line.length; i++) {\n arc.push(\n arc[i - 1]! +\n Math.hypot(line[i]!.x - line[i - 1]!.x, line[i]!.y - line[i - 1]!.y),\n )\n }\n return arc\n}\n\nfunction pointAt(line: NodeSegment[], arc: number[], s: number): NodeSegment {\n if (s <= 0) {\n return { ...line[0]! }\n }\n let i = 1\n while (i < arc.length && arc[i]! < s) {\n i++\n }\n if (i >= arc.length) {\n return { ...line.at(-1)! }\n }\n const span = arc[i]! - arc[i - 1]!\n const t = span > 0 ? (s - arc[i - 1]!) / span : 0\n const a = line[i - 1]!\n const b = line[i]!\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t }\n}\n\nfunction slice(line: NodeSegment[], arc: number[], from: number, to: number) {\n const points = [pointAt(line, arc, from)]\n for (let i = 0; i < line.length; i++) {\n if (arc[i]! > from && arc[i]! < to) {\n points.push({ ...line[i]! })\n }\n }\n points.push(pointAt(line, arc, to))\n return points\n}\n", "// Every label drawn over the graph claims a box in screen px from one\n// occupancy, so a label that would land on one already placed, or off the pane,\n// is dropped whichever overlay it belongs to. Callers take in priority order.\n// A chip is drawn whole or not at all.\n\nexport interface Box {\n x0: number\n x1: number\n y0: number\n y1: number\n}\n\nexport const LABEL_PX = 11\nexport const LABEL_CHAR_PX = 6.2\nexport const LABEL_PAD = 4\n\nexport interface LabelCandidate<T> {\n item: T\n x: number\n y: number\n text: string\n // how many rows down to try when the spot is taken, for a label that is\n // worth a column beside its neighbours rather than dropping\n stack?: number\n}\n\nexport interface PlacedLabel<T> extends LabelCandidate<T> {\n w: number\n}\n\nexport type TakeBox = (box: Box) => boolean\n\nexport function labelWidth(text: string) {\n return text.length * LABEL_CHAR_PX + LABEL_PAD * 2\n}\n\nexport function overlaps(a: Box, b: Box) {\n return a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0\n}\n\ninterface Frame {\n width: number\n height: number\n}\n\nfunction offFrame(box: Box, frame: Frame) {\n return (\n box.x1 <= 0 ||\n box.x0 >= frame.width ||\n box.y1 <= 0 ||\n box.y0 >= frame.height\n )\n}\n\nfunction insideFrame(box: Box, frame: Frame) {\n return (\n box.x0 >= 0 &&\n box.x1 <= frame.width &&\n box.y0 >= 0 &&\n box.y1 <= frame.height\n )\n}\n\nexport function occupancy(frame: Frame, reserved: Box[] = []): TakeBox {\n const placed = [...reserved]\n return box => {\n if (offFrame(box, frame) || placed.some(p => overlaps(p, box))) {\n return false\n }\n placed.push(box)\n return true\n }\n}\n\nexport function placeLabels<T>(\n candidates: LabelCandidate<T>[],\n frame: Frame,\n take: TakeBox,\n): PlacedLabel<T>[] {\n const out: PlacedLabel<T>[] = []\n const row = LABEL_PX + LABEL_PAD * 2\n for (const c of candidates) {\n const w = labelWidth(c.text)\n // a stack grows away from the nearer edge of the pane\n const direction = c.y > frame.height / 2 ? -1 : 1\n for (let tries = 0; tries <= (c.stack ?? 0); tries++) {\n const y = c.y + direction * tries * row\n const box = {\n x0: c.x - w / 2,\n x1: c.x + w / 2,\n y0: y - LABEL_PX - LABEL_PAD,\n y1: y + LABEL_PAD,\n }\n if (offFrame(box, frame)) {\n break\n }\n if (insideFrame(box, frame) && take(box)) {\n out.push({ ...c, y, w })\n break\n }\n }\n }\n return out\n}\n", "import { deletionArcCurves } from './deletionEdges'\nimport { polylineMidpoint } from './layout/mergeRuns'\nimport { occupancy } from './overlayLabels'\nimport { curveBounds, curveMidpoint } from './util/geometry'\n\nimport type { DeletionEdge } from './deletionEdges'\nimport type { Box, TakeBox } from './overlayLabels'\nimport type { AlleleDeletion, NodeSegment } from './types'\nimport type { AxisScale, BezierCurve } from './util/geometry'\n\n// What to write on the drawing, and where. Bandage labels a node with its name,\n// its length or its depth; a segment id means nothing to a reader of a pangenome\n// (\"s462766\"), so the label here is the one fact that carries the story \u2014 how\n// much sequence the alternative is worth. A node says its length, a deletion arc\n// says what it skips.\n//\n// **Every length written here is positive, and a deletion says it is one.**\n// A deletion arc used to be labelled \"\u221294.2 kb\", and a review read that as the\n// node's sequence length being negative, which is the only thing a bare signed\n// number beside a graph can mean. \"skips 94.2 kb\" replaced it and left a reader\n// asking 94.2 kb of what; \"skips 94.2 kb of reference\" answered that and still\n// drew \"it is sort of unclear what 'skips X bp of reference' means. is there any\n// better wording?\" on a third round. So the label now names the event rather\n// than describing the arc's mechanics: \"94.2 kb del\" abbreviates the term a\n// reader already has for reference sequence a haplotype does not carry, and\n// against the reference is what a deletion is measured from by default.\n//\n// **Two rules decide which labels there are, and both are about the drawing\n// rather than about a threshold on the graph.**\n//\n// A label has to fit the node it names, within a factor of two: past that it is\n// a label with a node attached rather than a node with a label. The rule used to\n// be a flat minimum drawn length (46 screen px) regardless of how long the text\n// was, which reads as arbitrary from the outside \u2014 review asked why some nodes\n// state a length and others do not, and nothing in the picture could answer it.\n// Measured against the text, the answer is in the picture: the node is shorter\n// than the words.\n//\n// And a label is dropped when its box would overlap one already placed. Labels\n// go biggest-sequence-first, so what survives a crowd is the allele that\n// matters; this is also what fixes labels printing on top of each other (\"\u221210\n// kb3.2 kb\" in the amylase figure).\n//\n// Between them they hold at both ends of the scale a pangenome graph spans: 63\n// kb-scale nodes at the amylase locus nearly all carry a length, and a 154-node\n// base-level pggb graph of 1-100 bp segments carries almost none \u2014 where every\n// label would have said \"1 bp\" and covered the segment it named.\n//\n// Collision is measured in SCREEN pixels, because that is where the text has its\n// size: the same two labels clear each other zoomed in and collide zoomed out,\n// so a wide view sheds labels on its own. This is why there is no \"show labels\"\n// toggle \u2014 a control whose only job is to undo a bad default is a bad default.\n\n// A deletion is worth saying even when its arc is small, because the arc is the\n// only thing on screen that represents it; but not when it is a dot.\n//\n// Measured on the arc's DRAWN EXTENT (the larger side of its bounds, in screen\n// px), not on its bulge. Bulge is in whatever units the layout works in: under\n// FMMM it is a fraction of the bypassed nodes' drawn length at the engine's own\n// scale, and in an anchored layout it is a fraction of their length in bp. The\n// same deletion therefore cleared this in one layout and not the other, which is\n// how `pangenome/hprc_cfhr_deletion` lost the labels on its 2.2 kb and 9.3 kb\n// arcs by changing nothing but the layout. Extent is what a reader sees either\n// way, and it is the same quantity the node rule below uses.\nconst MIN_DELETION_LABEL_PX = 26\n\n// One node label per this much pane, and never fewer than a handful: at a\n// 1400 by 600 pane that is about twenty.\nconst PX_PER_NODE_LABEL = 40_000\nconst MIN_NODE_LABELS = 8\n\n// A deletion clearing that floor still need not be able to CARRY its label: the\n// text is a fixed 26 characters however small the event's arc is, so at the LPA\n// KIV-2 locus `skips 27.7 kb of reference` came out four times the width of the\n// arc it was centred on, reading as a caption dropped on the bubble beside it\n// rather than as that arc's name (`pangenome/hprc_lpa_kiv2`, reviewed\n// 2026-07-31).\n//\n// A node in the same position simply loses its label, and that is right for a\n// node \u2014 the tube is still drawn, still coloured, still hoverable, and the graph\n// is full of them. It is wrong for a deletion, which is one dashed curve\n// standing for sequence that is not in the picture at all: dropped, nothing\n// tells a reader the arc is an event rather than another link.\n//\n// So the label moves off the arc instead and takes a leader line with it. It is\n// displaced along the direction the arc bows \u2014 which is the side the bulge was\n// drawn INTO, so it is the open side by construction \u2014 far enough that its box\n// clears the curve, and the leader states the association the proximity no\n// longer does.\nconst LEADER_GAP_PX = 8\n\n// The label's own box, for collision. Mirrors GraphCanvas's nodeLabelStyle:\n// 10px text in 3px of horizontal padding, on a 13px line. Character width is an\n// average rather than a measurement \u2014 placement runs on every pan and zoom, and\n// a canvas text metric per candidate costs more than the occasional\n// half-character of slack is worth.\nconst LABEL_CHAR_PX = 5.7\nconst LABEL_PAD_PX = 6\nconst LABEL_HEIGHT_PX = 13\n// Breathing room, so two labels that merely touch still read as two.\nconst LABEL_GAP_PX = 3\n// How much wider than its node a label may be before it is the label that is\n// being drawn rather than the node.\nconst MAX_LABEL_OVERHANG = 2\n\n// Half the box a piece of label text occupies. Split out of `labelBox` because\n// the width is wanted before there is anywhere to put the box: a displaced\n// deletion label is clamped by its own half-width, and the node pass below\n// rejects on size before it has a position at all.\nfunction labelHalfWidth(text: string) {\n return (text.length * LABEL_CHAR_PX + LABEL_PAD_PX + LABEL_GAP_PX) / 2\n}\n\n// Constant whatever the label says, so a candidate's vertical cull costs\n// nothing and can be taken before its text exists.\nconst LABEL_HALF_HEIGHT = (LABEL_HEIGHT_PX + LABEL_GAP_PX) / 2\n\n// The narrowest box `formatBp` can ask for, `1 bp`. A node drawn shorter than\n// this cannot carry ANY label, so the check happens before the length is\n// formatted: on a 7,499-node cut nearly every node fails it, and building a\n// string and a box per node only to discard both was most of this pass\n// (measured 37 ms per frame, and this pass runs on every pan).\nconst MIN_LABEL_HALF_WIDTH = labelHalfWidth('1 bp')\n\n// translate(-50%, -50%) in the style, so the anchor is the box's centre.\nfunction boxAt(halfW: number, x: number, y: number): Box {\n return {\n x0: x - halfW,\n x1: x + halfW,\n y0: y - LABEL_HALF_HEIGHT,\n y1: y + LABEL_HALF_HEIGHT,\n }\n}\n\n// The box a row label occupies, from the same metrics RowLabels renders with\n// (11px text, 4px of horizontal padding, a 16px line, centred on its row). Lives\n// here rather than in the component so the two halves of the collision test\n// cannot drift apart.\nexport function rowLabelBox(text: string, screenY: number): Box {\n return {\n x0: 6,\n x1: 6 + text.length * 6.2 + 8 + LABEL_GAP_PX,\n y0: screenY - 8 - LABEL_GAP_PX / 2,\n y1: screenY + 8 + LABEL_GAP_PX / 2,\n }\n}\n\n// Slide a value into [lo, hi], leaving it alone when the range is empty \u2014 a box\n// wider than the canvas has nowhere to fit, and pinning it to one edge is worse\n// than the overflow.\nfunction clamp(v: number, lo: number, hi: number) {\n return hi < lo ? v : Math.min(Math.max(v, lo), hi)\n}\n\n// A displaced label's tether, in the same screen pixels the label is placed in.\n// Both ends are computed here rather than in the component, because the label's\n// end is the point where its own box meets the line \u2014 geometry the placement\n// already has and the painter would have to rederive. It stops at the box edge\n// rather than at the centre because the label's background is translucent, so a\n// line run under it shows through the text.\nexport interface Leader {\n arcX: number\n arcY: number\n labelX: number\n labelY: number\n}\n\nexport interface GraphLabel {\n key: string\n text: string\n // screen pixels, already through the transform the canvas draws with: the\n // collision test needs them anyway, and returning layout units would have the\n // caller redo the arithmetic that decided which labels there are\n x: number\n y: number\n kind: 'node' | 'deletion'\n // present only on a label that had to move off the thing it names\n leader?: Leader\n}\n\n// bp in the unit that reads. Deliberately coarse: a label is a glance, and\n// \"12,345 bp\" costs twice the width of \"12 kb\" to say the same thing at the\n// scale a pangenome allele lives at.\nexport function formatBp(bp: number) {\n if (bp >= 1_000_000) {\n return `${+(bp / 1_000_000).toFixed(1)} Mb`\n }\n return bp >= 1000 ? `${+(bp / 1000).toFixed(1)} kb` : `${bp} bp`\n}\n\nfunction deletionText(bp: number) {\n return `${formatBp(bp)} del`\n}\n\n// Which way the arc bows, as a unit vector: its chord's midpoint towards its\n// apex. A deletion's bulge is perpendicular to the chord by construction, so\n// this points away from the backbone the arc leaves and rejoins \u2014 the side the\n// curve was opened into, and the side with room in it.\n//\n// Taken on SCREEN, which is where the label it aims has to clear the curve. That\n// used to be free \u2014 with one scale for both axes the transform is a similarity,\n// so a layout direction is a screen direction \u2014 and on a row layout it is not:\n// the two axes are bp and px and the arc leaves at a different angle in each.\nfunction arcOutward(\n curves: BezierCurve[],\n apex: { x: number; y: number },\n scaleX: number,\n scaleY: number,\n) {\n const first = curves[0]!\n const last = curves[curves.length - 1]!\n const dx = (apex.x - (first.x0 + last.x1) / 2) * scaleX\n const dy = (apex.y - (first.y0 + last.y1) / 2) * scaleY\n const len = Math.hypot(dx, dy)\n // A curve with no bow has no outward side; up is as good as any, and this is\n // unreachable for a deletion, whose bulge is what gives it extent at all.\n return len === 0 ? { x: 0, y: -1 } : { x: dx / len, y: dy / len }\n}\n\n// How far the box's own boundary is from its centre in a given direction \u2014 the\n// support function of an axis-aligned rectangle. Displacing a label by this plus\n// a gap puts its near EDGE that gap away from the point, whatever angle the\n// leader leaves at, which a single radius cannot do for a box six times wider\n// than it is tall.\nfunction boxSupport(box: Box, dir: { x: number; y: number }) {\n return (\n (Math.abs(dir.x) * (box.x1 - box.x0)) / 2 +\n (Math.abs(dir.y) * (box.y1 - box.y0)) / 2\n )\n}\n\n// Where a ray along `dir` through the box's centre crosses the box, again from\n// the centre \u2014 which is where a leader coming in along that ray has to stop.\n//\n// This is not `boxSupport`, and the difference is the whole leader: support is\n// the distance to the supporting line perpendicular to `dir`, which a box six\n// times wider than it is tall touches at a corner, so a diagonal approach hits\n// the box's real edge four or five times sooner. Stopping the line at the\n// support distance drew an 8px stub at the arc with 50px of white between it and\n// the text it was supposed to tie to.\nfunction boxRayCrossing(box: Box, dir: { x: number; y: number }) {\n const halfW = (box.x1 - box.x0) / 2\n const halfH = (box.y1 - box.y0) / 2\n return Math.min(\n dir.x === 0 ? Infinity : halfW / Math.abs(dir.x),\n dir.y === 0 ? Infinity : halfH / Math.abs(dir.y),\n )\n}\n\n// The node's own extent along its polyline, IN SCREEN PX \u2014 what the label has\n// to fit inside. Measured through the transform rather than in layout units and\n// scaled afterwards, because on a row layout the two axes are in different units\n// and there is no single number to scale a mixed length by.\nfunction drawnLength(segments: NodeSegment[], scaleX: number, scaleY: number) {\n let total = 0\n for (let i = 1; i < segments.length; i++) {\n const a = segments[i - 1]!\n const b = segments[i]!\n total += Math.hypot((b.x - a.x) * scaleX, (b.y - a.y) * scaleY)\n }\n return total\n}\n\n// The extent of a run's drawn points, in layout units. One pass rather than\n// four `Math.min(...points.map(...))` spreads over the same array: those\n// allocate a mapped copy per axis, and a run long enough \u2014 a chain of alleles\n// across a whole-file import \u2014 overflows the argument limit and throws\n// RangeError instead of labelling anything. Same reason `backboneSpan` is a\n// fold.\nfunction runBounds(\n nodeIds: string[],\n nodePositions: Record<string, NodeSegment[]>,\n) {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const id of nodeIds) {\n for (const p of nodePositions[id] ?? []) {\n minX = Math.min(minX, p.x)\n maxX = Math.max(maxX, p.x)\n minY = Math.min(minY, p.y)\n maxY = Math.max(maxY, p.y)\n }\n }\n return minX === Infinity ? undefined : { minX, minY, maxX, maxY }\n}\n\n// Both caches below are keyed the way hit detection keys its spatial indexes: a\n// WeakMap on the positions object, so two graph views cannot evict each other\n// and an entry dies with the layout it describes, plus an explicit check of\n// everything the value actually depends on. A node drag mutates the positions\n// in place, so their identity cannot report that they moved \u2014 hence `version`,\n// which the model bumps on exactly that (`positionsVersion`).\n//\n// What makes the caching worth having is that this whole pass reruns on every\n// pan: the labels are placed in screen space, so a translate changes where they\n// go, and neither the order they are considered in nor the arcs they ride\n// depends on it.\n\n// Node label candidates in the order they are considered \u2014 biggest allele\n// first \u2014 which is a function of the sequence lengths alone.\nconst nodeOrderCache = new WeakMap<\n Record<string, NodeSegment[]>,\n {\n nodeLengths: Map<string, number>\n order: [string, NodeSegment[]][]\n }\n>()\n\nfunction labelOrder(\n nodePositions: Record<string, NodeSegment[]>,\n nodeLengths: Map<string, number>,\n) {\n const cached = nodeOrderCache.get(nodePositions)\n if (cached?.nodeLengths === nodeLengths) {\n return cached.order\n }\n const order = Object.entries(nodePositions)\n .filter(([id, segments]) => segments.length > 0 && nodeLengths.has(id))\n .sort(([a], [b]) => nodeLengths.get(b)! - nodeLengths.get(a)!)\n nodeOrderCache.set(nodePositions, { nodeLengths, order })\n return order\n}\n\n// One deletion arc as the renderer draws it, plus the two things placement\n// reads off it: where its apex is, and how big it is on screen.\ninterface ArcPlacement {\n deletion: DeletionEdge\n curves: BezierCurve[]\n apex: { x: number; y: number }\n extent: number\n}\n\nconst arcCache = new WeakMap<\n Record<string, NodeSegment[]>,\n {\n deletions: DeletionEdge[]\n scaleX: number\n scaleY: number\n version: number\n arcs: ArcPlacement[]\n }\n>()\n\n// Biggest deletion first, so an arc that survives a crowd is the one carrying\n// the most sequence.\nfunction arcPlacements(\n nodePositions: Record<string, NodeSegment[]>,\n deletions: DeletionEdge[],\n axis: AxisScale,\n version: number,\n) {\n const { scaleX, scaleY } = axis\n const cached = arcCache.get(nodePositions)\n if (\n cached?.deletions === deletions &&\n cached.scaleX === scaleX &&\n cached.scaleY === scaleY &&\n cached.version === version\n ) {\n return cached.arcs\n }\n const arcs: ArcPlacement[] = []\n for (const deletion of [...deletions].sort((a, b) => b.bp - a.bp)) {\n const curves = deletionArcCurves(nodePositions, deletion, axis)\n const apex = curves ? curveMidpoint(curves) : undefined\n if (curves && apex) {\n const { minX, minY, maxX, maxY } = curveBounds(curves)\n arcs.push({\n deletion,\n curves,\n apex,\n // in screen px, so the two axes' units cannot be added by accident\n extent: Math.max((maxX - minX) * scaleX, (maxY - minY) * scaleY),\n })\n }\n }\n arcCache.set(nodePositions, { deletions, scaleX, scaleY, version, arcs })\n return arcs\n}\n\ninterface SizeLabelArgs {\n nodePositions: Record<string, NodeSegment[]>\n // bp per node id, so this module never has to know what a GraphNode is\n nodeLengths: Map<string, number>\n deletions: DeletionEdge[]\n // Deletions the layout found that carry a segment, so `deletions` above\n // cannot: empty under a layout drawn at sequence scale, where a node's own\n // length IS what its drawn extent means. See AlleleDeletion.\n alleleDeletions?: AlleleDeletion[]\n // Screen px per layout unit, both axes. They are equal on an isotropic layout\n // and differ on a row layout, whose y is already px (scaleY === 1). One value\n // so a caller cannot pass one and default the other \u2014 see AxisScale.\n axis: AxisScale\n translateX: number\n translateY: number\n width: number\n height: number\n // Bumped whenever the layout's positions are mutated in place, i.e. by a node\n // drag. Everything cached across frames here is keyed by it \u2014 see the caches\n // above \u2014 and reading it is also what makes a dragged node's label follow it.\n version?: number\n}\n\nexport interface SizeLabelCandidate {\n label: GraphLabel\n box: Box\n}\n\n// Deletions come first, so an arc keeps its label against the nodes around it:\n// the arc is the only thing in the drawing that represents sequence which is\n// not there, and a reader who cannot read it has no other route to it.\nexport function sizeLabelCandidates({\n nodePositions,\n nodeLengths,\n deletions,\n alleleDeletions,\n axis,\n translateX,\n translateY,\n width,\n height,\n version = 0,\n}: SizeLabelArgs) {\n const { scaleX, scaleY } = axis\n const deletionCandidates: SizeLabelCandidate[] = []\n const nodeCandidates: SizeLabelCandidate[] = []\n for (const { deletion, curves, apex, extent } of arcPlacements(\n nodePositions,\n deletions,\n axis,\n version,\n )) {\n if (extent >= MIN_DELETION_LABEL_PX) {\n const text = deletionText(deletion.bp)\n const halfW = labelHalfWidth(text)\n const box = boxAt(halfW, 0, 0)\n const arcX = apex.x * scaleX + translateX\n const arcY = apex.y * scaleY + translateY\n // The node rule, applied to an arc: a label wider than twice what it\n // names is a label with an arc attached. Under it the label stays\n // centred on the curve, which is what every arc big enough to hold its\n // own name already does.\n const fits = extent >= (halfW * 2) / MAX_LABEL_OVERHANG\n // Displaced by the SUPPORT distance, so the whole box clears the arc\n // whatever angle it leaves at, then the leader is stopped at the box's\n // real edge along the same ray.\n const bow = arcOutward(curves, apex, scaleX, scaleY)\n const offset = boxSupport(box, bow) + LEADER_GAP_PX\n // ...and then slid back into the frame if that put it outside one. A\n // tethered label chooses where it goes, and the cull below keeps any box\n // merely OVERLAPPING the canvas, so displacing blind is how the MHC force\n // layout came out with a clipped `\u2026ips 1.5 kb of reference` against its\n // left edge. Sliding beats dropping and beats picking the arc's other\n // side: the leader is redrawn to wherever the words ended up, so it stays\n // unambiguous however far along the edge they had to move.\n const x = fits ? arcX : clamp(arcX + bow.x * offset, halfW, width - halfW)\n const y = fits\n ? arcY\n : clamp(\n arcY + bow.y * offset,\n LABEL_HALF_HEIGHT,\n height - LABEL_HALF_HEIGHT,\n )\n // From where the label actually sits, not from the bow, or the line\n // points off at the position the label would have had.\n const away = Math.hypot(x - arcX, y - arcY)\n let leader\n if (away > 0) {\n const dir = { x: (x - arcX) / away, y: (y - arcY) / away }\n const crossing = boxRayCrossing(box, dir)\n leader = {\n arcX,\n arcY,\n labelX: x - dir.x * crossing,\n labelY: y - dir.y * crossing,\n }\n }\n deletionCandidates.push({\n label: {\n key: `del:${deletion.edgeIndex}`,\n text,\n x,\n y,\n kind: 'deletion',\n leader,\n },\n box: boxAt(halfW, x, y),\n })\n }\n }\n\n // Segment-carrying deletions, next: the arcs above and these are the same\n // event and are said in the same words, so they queue together ahead of the\n // node lengths and biggest-first among themselves.\n //\n // A run's label rides the middle of what the layout DREW for it, which is the\n // reference the allele replaces, and it has to fit that extent by the same\n // factor-of-two rule a node label does \u2014 so the sliver a 2 bp skip occupies in\n // a base-level graph carries nothing, and the 7.1 kb one CFT073 leaves in the\n // E. coli pggb graph carries `7 kb del`.\n //\n // The nodes it covers are then barred from labelling themselves. Their length\n // is a true fact and the wrong one to print here: `93 bp` written across a bar\n // drawn 7.1 kb wide is read as the bar's size, which is the misreading this\n // whole label exists to fix. It stays in the tooltip and the details panel.\n const labelledByDeletion = new Set<string>()\n const alleleRuns = [...(alleleDeletions ?? [])].sort((a, b) => b.bp - a.bp)\n for (const run of alleleRuns) {\n const drawn = runBounds(run.nodeIds, nodePositions)\n if (!drawn) {\n continue\n }\n // ON SCREEN, not in layout units, and that is the whole subtlety here. A\n // run drawn over the reference it replaces can be far wider than the window\n // \u2014 the E. coli one starts 7 kb before the left edge \u2014 so its true midpoint\n // is nowhere, and a label placed there is culled for being off-canvas and\n // the bar goes unnamed. The visible slice is what a reader has, so the\n // label rides the middle of that, and the fit test asks whether the words\n // fit what is SHOWN rather than what is drawn.\n const shownLeft = Math.max(drawn.minX * scaleX + translateX, 0)\n const shownRight = Math.min(drawn.maxX * scaleX + translateX, width)\n const shownTop = Math.max(drawn.minY * scaleY + translateY, 0)\n const shownBottom = Math.min(drawn.maxY * scaleY + translateY, height)\n if (shownRight < shownLeft || shownBottom < shownTop) {\n continue\n }\n const extent = Math.max(shownRight - shownLeft, shownBottom - shownTop)\n const text = deletionText(run.bp)\n const halfW = labelHalfWidth(text)\n if (extent >= (halfW * 2) / MAX_LABEL_OVERHANG) {\n for (const id of run.nodeIds) {\n labelledByDeletion.add(id)\n }\n const x = clamp((shownLeft + shownRight) / 2, halfW, width - halfW)\n const y = clamp(\n (shownTop + shownBottom) / 2,\n LABEL_HALF_HEIGHT,\n height - LABEL_HALF_HEIGHT,\n )\n deletionCandidates.push({\n label: {\n key: `alleledel:${run.nodeIds.join(',')}`,\n text,\n x,\n y,\n kind: 'deletion',\n },\n box: boxAt(halfW, x, y),\n })\n }\n }\n\n // Biggest allele first: in a crowd, the label that survives should be the one\n // carrying the most sequence, not whichever node the layout happened to emit\n // first. The order is `labelOrder`'s, cached across frames; the three rejects\n // below are what a frame actually decides, cheapest first \u2014 a node barred by\n // a run's label, then one whose row is off the pane (the box's height is a\n // constant, so this needs no text), then one too short to hold the shortest\n // label there is.\n for (const [id, segments] of labelOrder(nodePositions, nodeLengths)) {\n if (labelledByDeletion.has(id)) {\n continue\n }\n const { x, y } = polylineMidpoint(segments)\n const screenY = y * scaleY + translateY\n if (\n screenY + LABEL_HALF_HEIGHT <= 0 ||\n screenY - LABEL_HALF_HEIGHT >= height\n ) {\n continue\n }\n const drawn = drawnLength(segments, scaleX, scaleY)\n // The same comparison the real fit test makes, against the narrowest box\n // any label can have \u2014 so nothing that would have passed is rejected here.\n if (drawn < (MIN_LABEL_HALF_WIDTH * 2) / MAX_LABEL_OVERHANG) {\n continue\n }\n const text = formatBp(nodeLengths.get(id)!)\n const halfW = labelHalfWidth(text)\n if (drawn >= (halfW * 2) / MAX_LABEL_OVERHANG) {\n const screenX = x * scaleX + translateX\n nodeCandidates.push({\n label: {\n key: `node:${id}`,\n text,\n x: screenX,\n y: screenY,\n kind: 'node',\n },\n box: boxAt(halfW, screenX, screenY),\n })\n }\n }\n\n return { deletions: deletionCandidates, nodes: nodeCandidates }\n}\n\n// Node labels are rationed to the pane's area: a base-level cut of hundreds of\n// short nodes fits a length on most of them at a moderate zoom, and a drawing\n// where every node states its length is a table with a graph under it. Biggest\n// first, so the ration goes to the alleles that carry sequence; zooming in\n// gives more area to fewer nodes and the labels come back.\nexport function nodeLabelBudget(width: number, height: number) {\n return Math.max(\n MIN_NODE_LABELS,\n Math.floor((width * height) / PX_PER_NODE_LABEL),\n )\n}\n\nexport function placeSizeLabels(\n candidates: SizeLabelCandidate[],\n take: TakeBox,\n budget = Infinity,\n) {\n const labels: GraphLabel[] = []\n for (const { label, box } of candidates) {\n if (labels.length >= budget) {\n break\n }\n if (take(box)) {\n labels.push(label)\n }\n }\n return labels\n}\n\nexport function graphLabels({\n reserved,\n ...args\n}: SizeLabelArgs & { reserved?: Box[] }): GraphLabel[] {\n const take = occupancy(args, reserved)\n const { deletions, nodes } = sizeLabelCandidates(args)\n return [\n ...placeSizeLabels(deletions, take),\n ...placeSizeLabels(nodes, take, nodeLabelBudget(args.width, args.height)),\n ]\n}\n", "import {\n nodeLabelBudget,\n placeSizeLabels,\n rowLabelBox,\n sizeLabelCandidates,\n} from './graphLabels'\nimport {\n LABEL_CHAR_PX,\n LABEL_PAD,\n LABEL_PX,\n occupancy,\n placeLabels,\n} from './overlayLabels'\n\nimport type { BubbleHalo, RouteLabel } from './bubbles/bubbleHalos'\nimport type { DeletionEdge } from './deletionEdges'\nimport type { GenePin } from './genes/genePins'\nimport type { GraphLabel } from './graphLabels'\nimport type { Box, PlacedLabel } from './overlayLabels'\nimport type { AlleleDeletion, NodeSegment } from './types'\nimport type { AxisScale } from './util/geometry'\n\nexport const HALO_FACTOR = 3.4\nexport const LEGEND_INSET_PX = 6\n// routes whose own stretches are drawn on top of each other stack their chips\nconst ROUTE_STACK = 8\nconst GENE_PIN_DROP_PX = 18\n// a gene's name drops a row or two rather than vanish under a bubble's\nconst GENE_STACK = 2\n// a chip's baseline this far down is the highest it sits whole in the pane\nconst TOPMOST_BASELINE = LABEL_PX + LABEL_PAD\n\nexport interface LabelLayoutSource {\n paneWidth: number\n canvasHeight: number\n axisScale: AxisScale\n translateX: number\n translateY: number\n contigThickness: number\n legendSize: { width: number; height: number }\n // the band along the top a reference strip draws in\n referenceStripZonePx?: number\n drawnRowLabels: { label: string; y: number }[]\n bubbleHalos: BubbleHalo[]\n genePins: GenePin[]\n poppedFrom?: { label: string }\n nodePositions?: Record<string, NodeSegment[]>\n // false where the drawing is not the canvas's nodes (the tube map), so a\n // length label would sit on a box's tubes rather than beside a node\n labelsNodeSizes: boolean\n nodeLengths: Map<string, number>\n showDeletionEdges: boolean\n deletions: DeletionEdge[]\n alleleDeletions: AlleleDeletion[]\n positionsVersion: number\n}\n\nexport interface LabelLayout {\n bubbles: PlacedLabel<BubbleHalo>[]\n genes: PlacedLabel<GenePin>[]\n routes: PlacedLabel<{ halo: BubbleHalo; route: RouteLabel }>[]\n sizes: GraphLabel[]\n}\n\nfunction byExtent<T>(items: T[], extent: (item: T) => number) {\n return [...items].sort((a, b) => extent(b) - extent(a))\n}\n\n// The order is what survives a crowd: the bubble's name, then the gene's, then\n// what a deletion skips, then who takes each route, and a node's length last.\nexport function layoutLabels(m: LabelLayoutSource): LabelLayout {\n const { paneWidth: width, canvasHeight: height, translateX, translateY } = m\n const { scaleX, scaleY } = m.axisScale\n const frame = { width, height }\n const screen = (p: { x: number; y: number }) => ({\n x: p.x * scaleX + translateX,\n y: p.y * scaleY + translateY,\n })\n const reserved: Box[] = m.drawnRowLabels.map(({ label, y }) =>\n rowLabelBox(label, y * scaleY + translateY),\n )\n const stripPx = m.referenceStripZonePx ?? 0\n if (stripPx > 0) {\n reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx })\n }\n if (m.legendSize.width > 0) {\n reserved.push({\n x0: width - LEGEND_INSET_PX - m.legendSize.width,\n x1: width,\n y0: 0,\n y1: stripPx + LEGEND_INSET_PX + m.legendSize.height,\n })\n }\n if (m.poppedFrom) {\n reserved.push({\n x0: 0,\n x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,\n y0: 0,\n y1: 44,\n })\n }\n const take = occupancy(frame, reserved)\n\n // On a row layout a bubble's name goes in the band above the top row, which\n // is the reference it varies, and stays at the pane's top edge once that row\n // scrolls out. Above its own nodes, a lower row's allele would put it on the\n // reference line.\n const onRows = m.drawnRowLabels.length > 0\n const rowsTop = onRows\n ? Math.min(...m.drawnRowLabels.map(r => r.y))\n : Infinity\n const halo = m.contigThickness * HALO_FACTOR\n const byBubble = byExtent(m.bubbleHalos, h => h.members)\n const bubbles = placeLabels(\n byBubble.map(h => {\n const { x, y } = screen({\n x: h.labelAt.x,\n y: Math.min(h.labelAt.y, rowsTop),\n })\n const baseline = y - halo / 2 - 6\n return {\n item: h,\n x,\n y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,\n text: h.label,\n }\n }),\n frame,\n take,\n )\n\n const genes = placeLabels(\n byExtent(m.genePins, pin => pin.gene.end - pin.gene.start).map(pin => {\n const { x, y } = screen(pin.at)\n return {\n item: pin,\n x,\n y: y + m.contigThickness + GENE_PIN_DROP_PX,\n text: pin.covered < 0.98 ? `${pin.gene.name} \u2026` : pin.gene.name,\n stack: GENE_STACK,\n }\n }),\n frame,\n take,\n )\n const sizeCandidates =\n m.nodePositions && m.labelsNodeSizes\n ? sizeLabelCandidates({\n nodePositions: m.nodePositions,\n nodeLengths: m.nodeLengths,\n deletions: m.showDeletionEdges ? m.deletions : [],\n alleleDeletions: m.alleleDeletions,\n axis: m.axisScale,\n translateX,\n translateY,\n width,\n height,\n version: m.positionsVersion,\n })\n : { deletions: [], nodes: [] }\n const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take)\n const routes = placeLabels(\n byBubble.flatMap(h =>\n h.routes.map(route => {\n const { x, y } = screen(route.at)\n return {\n item: { halo: h, route },\n x,\n y: y + 4,\n text: route.text,\n stack: ROUTE_STACK,\n }\n }),\n ),\n frame,\n take,\n )\n const nodeLabels = placeSizeLabels(\n sizeCandidates.nodes,\n take,\n nodeLabelBudget(width, height),\n )\n return {\n bubbles,\n genes,\n routes,\n sizes: [...deletionLabels, ...nodeLabels],\n }\n}\n", "import { packAbgr } from '@jbrowse/core/util/colorBits'\n\nimport {\n REFERENCE_RAMP_ALT_COLOR,\n REFERENCE_RAMP_ALT_CSS,\n REFERENCE_RAMP_MAX_HUE,\n hslToRgb,\n} from './renderer/GeometryBuilder'\n\n// How a lifted walk colours its lane, stated the way a grammar of graphics\n// states an encoding: a field, the quantity the walk has at each node it\n// visits, mapped through a scheme, the scale from that quantity to a colour.\n// Colour does one job at a time by default: a walk lifted alone shades light\n// to dark along itself, which follows it round a loop, and walks lifted\n// together each take one flat colour, which says which lane is which. The\n// reference takes grey, so no hue means anything but a haplotype.\n//\n// A session states it per walk, and leaves out whatever takes the default:\n//\n// \"walkLayers\": [\n// { \"walk\": \"GRCh38#0#chr6\" },\n// { \"walk\": \"HG00133#1#CM090050.1\", \"color\": { \"scheme\": \"purple\" } }\n// ]\n\nexport const WALK_FIELDS = [\n {\n value: 'progress',\n label: 'Progress along the walk',\n legend: 'light where each walk starts, dark where it ends',\n },\n { value: 'walk', label: 'One colour for the walk', legend: '' },\n {\n value: 'reference',\n label: 'Reference position',\n legend: 'along the reference left to right, charcoal off it',\n },\n] as const\n\nexport type WalkField = (typeof WALK_FIELDS)[number]['value']\n\n// Each family runs from a light colour where the walk starts to a dark one of\n// another hue where it ends, so a walk reads in hue as well as lightness, and\n// no two families share a stretch of the colour wheel: one walk's gradient\n// never passes through another's colours. The reference takes grey to black, so\n// that no hue on a lane means anything but a haplotype. The rainbow is the\n// reference-position ramp, so it goes with that field only.\n//\n// A dichromat sees one axis, blue to yellow, so cyan to navy and yellow to red\n// stay apart for every reader and come first; pink and lime fold into them.\nexport const WALK_SCHEMES = [\n { value: 'grey', label: 'Grey to black', ends: ['#a3a3a3', '#151515'] },\n { value: 'blue', label: 'Cyan to navy', ends: ['#6ee0f0', '#0b2c7a'] },\n { value: 'red', label: 'Yellow to red', ends: ['#ffd24a', '#b3121b'] },\n { value: 'purple', label: 'Pink to plum', ends: ['#f5a3d8', '#5e1a6e'] },\n { value: 'green', label: 'Lime to forest', ends: ['#b6e36c', '#0b5d3b'] },\n { value: 'rainbow', label: 'Rainbow' },\n] as const\n\nexport type WalkScheme = (typeof WALK_SCHEMES)[number]['value']\n\nexport interface WalkEncoding {\n field: WalkField\n scheme: WalkScheme\n}\n\nexport interface WalkLayer {\n walk: string\n color?: Partial<WalkEncoding>\n}\n\n// the families lifted haplotypes take by default, in the order they were picked\nconst FAMILIES: WalkScheme[] = ['blue', 'red', 'purple', 'green']\n\nconst isField = (v: unknown): v is WalkField =>\n WALK_FIELDS.some(f => f.value === v)\nconst isScheme = (v: unknown): v is WalkScheme =>\n WALK_SCHEMES.some(s => s.value === v)\n\n// A stated field or scheme this build does not have reads as unset\nexport function resolveEncoding(\n layer: WalkLayer,\n reference: boolean,\n picked: number,\n alone = true,\n): WalkEncoding {\n const { field: askedField, scheme: askedScheme } = layer.color ?? {}\n const field = isField(askedField) ? askedField : alone ? 'progress' : 'walk'\n const family = reference ? 'grey' : FAMILIES[picked % FAMILIES.length]!\n const scheme = isScheme(askedScheme)\n ? askedScheme === 'rainbow' && field !== 'reference'\n ? family\n : askedScheme\n : family\n return { field, scheme }\n}\n\n// Where a flat lane sits on its family: halfway between its ends\nconst BASE_T = 0.5\n\nconst toLinear = (c: number) =>\n c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4\nconst toGamma = (c: number) =>\n c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055\n\n// sRGB hex to OKLab, where a straight line between two colours changes\n// lightness evenly, so a family's steps read alike from end to end\nfunction oklab(hex: string) {\n const [r, g, b] = [1, 3, 5].map(i =>\n toLinear(Number.parseInt(hex.slice(i, i + 2), 16) / 255),\n ) as [number, number, number]\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ]\n}\n\nfunction oklabToRgb([L, a, b]: number[]) {\n const l = (L! + 0.3963377774 * a! + 0.2158037573 * b!) ** 3\n const m = (L! - 0.1055613458 * a! - 0.0638541728 * b!) ** 3\n const s = (L! - 0.0894841775 * a! - 1.291485548 * b!) ** 3\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ].map(c => toGamma(Math.max(0, Math.min(1, c))) * 255)\n}\n\n// A scheme at t in [0, 1] as rgb 0-255\nexport function schemeRgb(scheme: WalkScheme, t: number) {\n const x = Math.max(0, Math.min(1, t))\n const family = WALK_SCHEMES.find(s => s.value === scheme)\n if (!family || !('ends' in family)) {\n return hslToRgb(x * REFERENCE_RAMP_MAX_HUE, 0.7, 0.5).map(c => c * 255)\n }\n const from = oklab(family.ends[0])\n const to = oklab(family.ends[1])\n return oklabToRgb(from.map((v, i) => v + (to[i]! - v) * x))\n}\n\nexport function schemeCss(scheme: WalkScheme, t: number) {\n const [r, g, b] = schemeRgb(scheme, t).map(Math.round)\n return `rgb(${r}, ${g}, ${b})`\n}\n\nexport function schemeColor(scheme: WalkScheme, t: number) {\n const [r, g, b] = schemeRgb(scheme, t).map(Math.round)\n return packAbgr(r!, g!, b!, 255)\n}\n\n// A field's value at a node through the walk's scheme; `t` undefined is a node\n// the field has nothing to say about, off the reference for `reference`\nexport function encodedColor({ field, scheme }: WalkEncoding, t?: number) {\n return field === 'walk'\n ? schemeColor(scheme, BASE_T)\n : t === undefined\n ? NO_VALUE_COLOR\n : schemeColor(scheme, t)\n}\n\nexport const NO_VALUE_COLOR = REFERENCE_RAMP_ALT_COLOR\nexport const NO_VALUE_CSS = REFERENCE_RAMP_ALT_CSS\n\n// A swatch for the legend: the walk's colour, or the scheme across the field\nexport function encodingSwatchCss({ field, scheme }: WalkEncoding) {\n if (field === 'walk') {\n return schemeCss(scheme, BASE_T)\n }\n const stops = Array.from({ length: 7 }, (_, i) => {\n const t = i / 6\n return `${schemeCss(scheme, t)} ${t * 100}%`\n })\n return `linear-gradient(to right, ${stops.join(', ')})`\n}\n\nexport function fieldLegend(field: WalkField) {\n return WALK_FIELDS.find(f => f.value === field)!.legend\n}\n", "import { packAbgr } from '@jbrowse/core/util/colorBits'\n\nimport { fadeAbgr } from './colorBits'\nimport { meanDepth, nodeWidthPx } from '../nodeWidths'\nimport {\n PATH_LIGHTNESS,\n PATH_SATURATION,\n nameHue,\n pathHueAt,\n} from '../pathColors'\nimport { referenceMidpoints } from '../referenceSpan'\nimport { baseEdgeCurves } from '../util/edgeCurves'\nimport {\n dashCurves,\n pathRibbonOffsets,\n translateCurves,\n yToXOf,\n} from '../util/geometry'\n\nimport type { ResolvedColorScheme } from '../colorSchemes'\nimport type { NodeWidth } from '../nodeWidths'\nimport type { Graph, GraphNode, NodeSegment } from '../types'\nimport type { WalkLift } from '../walkHighlight'\nimport type {\n Arrowhead,\n EdgeCurveBatch,\n NodeStroke,\n RenderBatch,\n Run,\n} from './types'\nimport type { AxisScale, BezierCurve } from '../util/geometry'\n\n// Colors flow through the geometry builder as ABGR-in-u32 (see colorBits.ts),\n// so a brighten or fade is integer arithmetic and a batch of a hundred thousand\n// strokes carries its colours in one number each.\nconst EDGE_DEFAULT_COLOR = packAbgr(119, 119, 119, 217) // rgb(119,119,119) ~ 0.467, alpha 0.85\nconst EDGE_PATH_FALLBACK_COLOR = packAbgr(136, 136, 136, 217) // ~0.533, alpha 0.85\n// An edge that skips reference sequence: the graph's own statement that some\n// haplotype does not carry what the backbone does. Drawn thicker than a plain\n// link, and unconditionally rather than under a colour scheme, because a\n// deletion has no node to colour \u2014 nothing else in the drawing can carry it, so\n// there is no scheme it could disagree with. See deletionEdges.ts.\n//\n// NEAR-BLACK, not red. The reference-position ramp runs hue 0 to 300 at 70%/50%,\n// and its hue-0 end is rgb(217,38,38) \u2014 the old rgb(214,39,40) was that colour\n// to within three points, so at the start of any region the deletion arc and the\n// backbone under it were the same red (review: \"the 'red' coloring is too close\n// to the rainbow\"). Nothing else in the drawing is near-black: nodes are ramp\n// hues or the off-reference charcoal, plain links are mid grey. Weight and\n// darkness carry it instead of a hue, which is what keeps hue meaning reference\n// position and only that.\nconst EDGE_DELETION_COLOR = packAbgr(24, 24, 28, 240)\nconst DELETION_THICKNESS_FACTOR = 2.2\n// A lifted walk's links keep their plain ink, since the walk's lanes in the\n// nodes carry it, and the rest fades. The alpha they fade to keeps every colour\n// scheme's hues, only dimmer, on any background.\nexport const LIFT_BACKDROP_COLOR = packAbgr(160, 160, 160, 255)\nexport const FADED_ALPHA = 0.18\n// Dash period in screen px, so a dashed arc looks the same at any zoom. Dashes\n// are geometry rather than a stroke style, because only one of the two backends\n// has one; see dashCurves.\nconst DELETION_DASH_PX = 11\n\n// A node drawn under `drawPaths` is split into one lengthwise stripe per path in\n// the graph, in the legend's own order, and a path that does not visit the node\n// leaves its slot empty. The slot is what makes carriage readable: an absence\n// lands in the same place on every node, so \"which sample skips this segment\"\n// is a gap at a fixed height rather than something to be counted.\n//\n// Only the edges used to carry this, and how many of a graph's paths resolved\n// there was a function of how long the edge happened to be DRAWN: a kilobase\n// deletion is one long edge and separates all five strokes, while a 1 bp SNP\n// bubble is an edge a few px long and shows a speck (review: \"only deletion\n// really shows paths clearly, the edges between nodes are too small to see the\n// paths ... even coloring the length of the nodes using the per-sample colors\").\n// A node is drawn at its own length, which is the one thing in the drawing that\n// does not shrink to a joint.\n//\n// How many paths is too many to colour at all is not decided here \u2014 it is one\n// rule over the stripes, the ribbons and the key, so the model resolves it and\n// hands down the answer as `drawPaths`. See MAX_PATH_COLORS.\n//\n// A stripe below this many screen px is not a color, it is aliasing between\n// neighbouring stripes. Under it the node keeps its own scheme colour.\n// `contigThickness` is itself in screen px, so this bites on the path COUNT\n// rather than on the zoom.\nconst MIN_PATH_STRIPE_PX = 1.2\n// A lifted walk's lane is never thinner than this, so a node carrying three\n// lifted walks widens rather than splitting into hairlines\nconst MIN_WALK_LANE_PX = 4\n// Guards the screen-px-to-world division below against a degenerate transform.\nconst MIN_SCALE_FOR_OFFSET = 1e-6\n\n// An arrowhead's size in SCREEN px, grown from the half-width of the edge it\n// ends: the renderer expands it after the transform (see TransformUniform), so\n// a head is the same size at every zoom. A fixed 12 px head was twice the width\n// of a default 6 px node and hid the 1 bp nodes it pointed at; this one is about\n// a node wide on a default edge and grows with a deletion's or a walk's weight.\nconst ARROW_LENGTH_BASE_PX = 7\nconst ARROW_LENGTH_PER_EDGE_PX = 3\nconst ARROW_HALF_WIDTH_BASE_PX = 3\nconst ARROW_HALF_WIDTH_PER_EDGE_PX = 1.5\n// A head on an edge shorter than itself would overhang the node it leaves, so\n// it shrinks to fit, and below this fraction of its size it is dropped until a\n// zoom lengthens the edge. In a tight bubble the old heads, laid over every\n// 1 bp allele, were most of the ink.\nconst MIN_ARROW_FIT = 0.5\nconst MIN_ARROW_SCALE = 0.45\n\n// Steps per cubic when walking a curve back from its end, spaced by the square\n// of the step so they bunch where the head sits: the first is 1/144 of the\n// cubic, a few px on even a long edge.\nconst ARROW_CURVE_STEPS = 12\n// Below this an edge has no length, only the rounding of sampling one.\nconst ABUTTING_PX = 1e-6\n\n// Scratch for backFromEnd, reused across edges: this runs once per head per\n// rebuild, and objects per sample doubled the cost of a 16k-edge build.\nlet walkX = new Float64Array(64)\nlet walkY = new Float64Array(64)\nlet walkPx = new Float64Array(64)\n\n// A run of curves as points walked back from its end into the walk arrays,\n// each with its distance from the end in screen px, as far as `untilPx`.\n// Returns the point count.\nfunction backFromEnd(\n curves: BezierCurve[],\n scale: number,\n yToX: number,\n untilPx: number,\n) {\n const most = curves.length * ARROW_CURVE_STEPS + 1\n if (walkX.length < most) {\n walkX = new Float64Array(most)\n walkY = new Float64Array(most)\n walkPx = new Float64Array(most)\n }\n const last = curves[curves.length - 1]!\n walkX[0] = last.x1\n walkY[0] = last.y1\n walkPx[0] = 0\n let n = 1\n let px = 0\n for (let ci = curves.length - 1; ci >= 0 && px < untilPx; ci--) {\n const c = curves[ci]!\n for (let k = 1; k <= ARROW_CURVE_STEPS && px < untilPx; k++) {\n const t = 1 - (k / ARROW_CURVE_STEPS) ** 2\n const u = 1 - t\n const a = u * u * u\n const b = 3 * u * u * t\n const d = 3 * u * t * t\n const e = t * t * t\n const x = a * c.x0 + b * c.cx0 + d * c.cx1 + e * c.x1\n const y = a * c.y0 + b * c.cy0 + d * c.cy1 + e * c.y1\n px += Math.hypot(x - walkX[n - 1]!, (y - walkY[n - 1]!) * yToX) * scale\n walkX[n] = x\n walkY[n] = y\n walkPx[n] = px\n n++\n }\n }\n return n\n}\n\nfunction walkPointAt(n: number, px: number) {\n let i = 1\n while (i < n - 1 && walkPx[i]! < px) {\n i++\n }\n const from = walkPx[i - 1]!\n const to = walkPx[i]!\n const f = to > from ? Math.min(1, Math.max(0, (px - from) / (to - from))) : 0\n return {\n x: walkX[i - 1]! + (walkX[i]! - walkX[i - 1]!) * f,\n y: walkY[i - 1]! + (walkY[i]! - walkY[i - 1]!) * f,\n }\n}\n\n// Bandage's rule, from GraphicsItemNode::shape: a node's arrow is sized by the\n// node's own width. Here the head belongs to the edge, so the edge sets its\n// size, and the node it enters sets where the tip stops. The tip lands on that\n// node's outline, which a round cap puts `nodeHalfWidth` from the endpoint in\n// every direction; on the centreline it sank half a node deep and covered the\n// node's first few px. Only as far as the edge is long, so an abutting joint\n// keeps its head on the joint.\n//\n// A joint's head lies over the node the edge leaves, so that node's drawn\n// length is the room it fits to. A run of 1 bp nodes on a walk row otherwise\n// drew a head at every joint, a serration along the row.\n//\n// The head lies along the curve it ends, from the curve point a head-length\n// back to the tip. The end tangent alone runs along the entered node, and an\n// edge that turns in its last few px then drew a head pointing across its own\n// stroke.\nfunction arrowheadFor({\n curves,\n edgeHalfWidth,\n nodeHalfWidth,\n scale,\n yToX,\n color,\n fromSegments,\n toSegments,\n}: {\n curves: BezierCurve[]\n edgeHalfWidth: number\n nodeHalfWidth: number\n scale: number\n yToX: number\n color: number\n fromSegments: NodeSegment[]\n toSegments: NodeSegment[]\n}): Arrowhead | undefined {\n const length = ARROW_LENGTH_BASE_PX + ARROW_LENGTH_PER_EDGE_PX * edgeHalfWidth\n const n = backFromEnd(curves, scale, yToX, nodeHalfWidth + length)\n const lengthPx = walkPx[n - 1]!\n const inset = Math.min(nodeHalfWidth, lengthPx)\n const room =\n lengthPx < ABUTTING_PX\n ? polylinePx(fromSegments, scale, yToX)\n : lengthPx - inset\n const fit = Math.min(1, room / length)\n if (fit < MIN_ARROW_FIT) {\n return undefined\n }\n const tip = walkPointAt(n, inset)\n const base = walkPointAt(n, inset + length * fit)\n const dx = tip.x - base.x\n const dy = (tip.y - base.y) * yToX\n const last = curves[curves.length - 1]!\n return {\n x: tip.x,\n y: tip.y,\n angle:\n Math.hypot(dx, dy) > 0\n ? Math.atan2(dy, dx)\n : endTangent(last, yToX, intoDirection(last, toSegments)),\n length: length * fit,\n halfWidth:\n (ARROW_HALF_WIDTH_BASE_PX +\n ARROW_HALF_WIDTH_PER_EDGE_PX * edgeHalfWidth) *\n fit,\n color,\n }\n}\n\nfunction polylinePx(segments: NodeSegment[], scale: number, yToX: number) {\n let length = 0\n for (let i = 1; i < segments.length; i++) {\n length += Math.hypot(\n segments[i]!.x - segments[i - 1]!.x,\n (segments[i]!.y - segments[i - 1]!.y) * yToX,\n )\n }\n return length * scale\n}\n\n// The way into a node from the end an edge reaches it at: the direction an\n// abutting edge, which has no length of its own, points.\nfunction intoDirection(last: BezierCurve, toSegments: NodeSegment[]) {\n if (toSegments.length < 2) {\n return undefined\n }\n const first = toSegments[0]!\n const final = toSegments[toSegments.length - 1]!\n const atStart =\n Math.hypot(last.x1 - first.x, last.y1 - first.y) <=\n Math.hypot(last.x1 - final.x, last.y1 - final.y)\n return atStart\n ? { from: first, to: toSegments[1]! }\n : { from: final, to: toSegments[toSegments.length - 2]! }\n}\n\n// Per-point unit normals of a polyline, mitred at the interior joints so a\n// stripe slid along them keeps a constant distance from the node's own outline\n// where it bends.\n//\n// `yToX` puts the two axes in comparable units first (see computeEdgeCurves), so\n// the normal is perpendicular to the polyline AS DRAWN rather than to its\n// coordinates. It is 1 on an isotropic layout, where the two are the same thing.\nfunction pointNormalsOf(rawPoints: { x: number; y: number }[], yToX = 1) {\n const points =\n yToX === 1 ? rawPoints : rawPoints.map(p => ({ x: p.x, y: p.y * yToX }))\n const pointNormals: { nx: number; ny: number }[] = []\n for (let i = 0; i < points.length; i++) {\n let nx = 0\n let ny = 0\n\n if (i === 0) {\n const dx = points[1]!.x - points[0]!.x\n const dy = points[1]!.y - points[0]!.y\n const len = Math.hypot(dx, dy)\n if (len > 0) {\n nx = -dy / len\n ny = dx / len\n }\n } else if (i === points.length - 1) {\n const dx = points[i]!.x - points[i - 1]!.x\n const dy = points[i]!.y - points[i - 1]!.y\n const len = Math.hypot(dx, dy)\n if (len > 0) {\n nx = -dy / len\n ny = dx / len\n }\n } else {\n const dx1 = points[i]!.x - points[i - 1]!.x\n const dy1 = points[i]!.y - points[i - 1]!.y\n const len1 = Math.hypot(dx1, dy1)\n const dx2 = points[i + 1]!.x - points[i]!.x\n const dy2 = points[i + 1]!.y - points[i]!.y\n const len2 = Math.hypot(dx2, dy2)\n\n if (len1 > 0 && len2 > 0) {\n const nx1 = -dy1 / len1\n const ny1 = dx1 / len1\n const nx2 = -dy2 / len2\n const ny2 = dx2 / len2\n nx = (nx1 + nx2) / 2\n ny = (ny1 + ny2) / 2\n const dot = nx1 * nx + ny1 * ny\n if (dot > 0.1) {\n nx /= dot\n ny /= dot\n }\n } else if (len1 > 0) {\n nx = -dy1 / len1\n ny = dx1 / len1\n } else if (len2 > 0) {\n nx = -dy2 / len2\n ny = dx2 / len2\n }\n }\n pointNormals.push({ nx, ny })\n }\n return pointNormals\n}\n\n// The polyline shifted `distance` world units along its own normals, which is\n// the node equivalent of the constant perpendicular offset the edge path\n// strokes already take. `distance` is in x units and the normal is the drawn\n// one (pointNormalsOf), so the y component comes back out of x units here.\nfunction offsetPolyline(\n points: { x: number; y: number }[],\n normals: { nx: number; ny: number }[],\n distance: number,\n yToX = 1,\n) {\n return points.map((p, i) => ({\n x: p.x + normals[i]!.nx * distance,\n y: p.y + (normals[i]!.ny * distance) / yToX,\n }))\n}\n\nfunction packNorm(r: number, g: number, b: number, a: number) {\n return packAbgr(\n Math.round(r * 255),\n Math.round(g * 255),\n Math.round(b * 255),\n Math.round(a * 255),\n )\n}\n\nexport interface BuildOptions {\n nodePositions: Record<string, NodeSegment[]>\n graph: Graph\n nodeById: Map<string, GraphNode>\n colorScheme: ResolvedColorScheme\n contigThickness: number\n connectorThickness: number\n // Whether to paint per path \u2014 the RESOLVED answer, not the raw view prop.\n // Past MAX_PATH_COLORS a graph's paths are not drawable as colours at all and\n // the model hands over `false`, which is the one place that decides it for\n // the stripes, the ribbons and the key together. Same contract as\n // `colorScheme` above, which arrives with 'auto' already resolved.\n drawPaths: boolean\n // thicker by depth, or every node at `contigThickness`; see nodeWidths.ts\n nodeWidth?: NodeWidth\n // walks lifted out: each draws a lane of its own along the nodes it visits,\n // their links draw dark and heavy, and everything else fades to a fraction\n // of its alpha\n highlight?: WalkLift\n // Both scales together, and required. Every screen-metric constant here (dash\n // period, stripe width, arrowhead angle) divides by scaleX, and everything\n // that mixes the axes needs their ratio; taking them as one value is what\n // keeps a caller from supplying an x scale and defaulting y, which draws a\n // wrong picture and reports nothing. See AxisScale.\n axis: AxisScale\n linearLayout?: boolean\n viewportBounds?: { minX: number; minY: number; maxX: number; maxY: number }\n // What the 'reference-position' scheme paints from: each node's midpoint on\n // the reference, and the interval the hue spans. Unused by every other\n // scheme, and passed in rather than derived here for the reason `deletions`\n // below is \u2014 it costs a neighbour walk per node, it is a fact about the\n // graph, and this function runs on every pan. `computeReferenceRamp` is what\n // builds one.\n referenceRamp?: ReferenceRamp\n // links that skip reference sequence (deletionEdges()), keyed by their index\n // into graph.edges and carrying the backbone node ids they bypass. Passed in\n // rather than derived here because the model can hold it against the graph,\n // and because the same set names the hover text.\n deletions?: Map<number, string[]>\n hiddenEdges?: ReadonlySet<number>\n // Bumped when a drag moves the positions in place, which their identity\n // cannot report. Only the shared curve cache reads it; see baseEdgeCurves.\n version?: number\n}\n\nexport function hslToRgb(\n h: number,\n s: number,\n l: number,\n): [number, number, number] {\n const c = (1 - Math.abs(2 * l - 1)) * s\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = l - c / 2\n let r = 0\n let g = 0\n let b = 0\n if (h < 60) {\n r = c\n g = x\n } else if (h < 120) {\n r = x\n g = c\n } else if (h < 180) {\n g = c\n b = x\n } else if (h < 240) {\n g = x\n b = c\n } else if (h < 300) {\n r = x\n b = c\n } else {\n r = c\n b = x\n }\n return [r + m, g + m, b + m]\n}\n\n// Evenly-spaced RGB gradient stops (0-255 per channel).\nconst DEPTH_GRADIENT = [\n [68, 1, 84],\n [59, 82, 139],\n [33, 145, 140],\n [94, 201, 98],\n [253, 231, 37],\n] as const\n\n// ranks >= 1, i.e. everything off the reference backbone\nconst STABLE_RANK_GRADIENT = [\n [237, 137, 44],\n [158, 42, 122],\n] as const\n\nconst NODE_LENGTH_GRADIENT = [\n [220, 50, 50],\n [50, 120, 220],\n] as const\n\n// Sample a piecewise-linear RGB gradient; t is clamped to [0,1].\nfunction sampleGradient(\n stops: readonly (readonly [number, number, number])[],\n t: number,\n) {\n const pos = Math.max(0, Math.min(1, t)) * (stops.length - 1)\n const i = Math.min(stops.length - 2, Math.floor(pos))\n const f = pos - i\n const a = stops[i]!\n const b = stops[i + 1]!\n return packAbgr(\n Math.round(a[0] + (b[0] - a[0]) * f),\n Math.round(a[1] + (b[1] - a[1]) * f),\n Math.round(a[2] + (b[2] - a[2]) * f),\n 255,\n )\n}\n\n// Deterministic color from a string (djb2-style hash \u2192 HSL hue), for the\n// `random` node scheme. The path ribbons do NOT use this \u2014 see pathHueAt.\nfunction hashColor(str: string, alpha: number) {\n const [r, g, b] = hslToRgb(nameHue(str), PATH_SATURATION, PATH_LIGHTNESS)\n return packNorm(r, g, b, alpha)\n}\n\n// A hue ramp over reference coordinates, which is the one colouring a linear\n// track can reproduce exactly: it is a function of two stated numbers and a\n// node's reference midpoint, so `hsl(min(300,max(0,(mid-start)/span*300)),70%,50%)`\n// in a track's `color` slot paints a segment the colour the graph paints its\n// node. Stops at 300 rather than wrapping to 360, so the two ends of the window\n// are red and magenta rather than both red.\nexport const REFERENCE_RAMP_MAX_HUE = 300\nconst REFERENCE_RAMP_SATURATION = 0.7\nconst REFERENCE_RAMP_LIGHTNESS = 0.5\n// Off-reference segments come off the ramp entirely and paint one flat charcoal.\n//\n// They used to keep the hue of the reference they replace, paler and softer, on\n// the argument that the correspondence is the whole scheme. Two rounds of docs\n// review say otherwise: a pale hue is still a hue on the ramp, so an alternative\n// allele reads as reference sequence at that position rather than as sequence\n// the reference does not have \u2014 \"non-backbone parts of the bandage graph could\n// be coloured in a non-spectrum colouring\". Nothing is lost by dropping the hue,\n// because in both anchored layouts an allele's x already states which reference\n// position it attaches to, and in the force layout its position states nothing\n// to begin with.\n//\n// Distinct from the light grey a node with no reference coordinates at all gets:\n// this one is anchored and off-reference, that one is unplaceable.\nconst REFERENCE_RAMP_ALT_RGB = [60, 65, 72] as const\nexport const REFERENCE_RAMP_ALT_COLOR = packAbgr(...REFERENCE_RAMP_ALT_RGB, 255)\nexport const REFERENCE_RAMP_ALT_CSS = `rgb(${REFERENCE_RAMP_ALT_RGB.join(', ')})`\n\nexport interface ReferenceRamp {\n start: number\n span: number\n midpoints: Map<string, number>\n}\n\nexport interface ColorSchemeRange {\n minDepth: number\n maxDepth: number\n minLength: number\n maxLength: number\n maxRank: number\n nodeCount: number\n referenceRamp?: ReferenceRamp\n}\n\n// The domain is stated by the caller \u2014 the region the subgraph was cut from \u2014\n// rather than measured off the nodes, because a linear view has no way to know\n// what the cut reached: segments overrun the region at both edges, so a\n// measured domain would shift the hue of every node by an amount the other\n// panel cannot compute. With no region (a whole-file import) the drawn extent\n// is all there is, and the ramp is then only comparable to itself.\nexport function computeReferenceRamp(\n graph: Graph,\n domain: { start: number; end: number } | undefined,\n): ReferenceRamp {\n const midpoints = referenceMidpoints(graph)\n if (domain && domain.end > domain.start) {\n return {\n start: domain.start,\n span: domain.end - domain.start,\n midpoints,\n }\n }\n let min = Infinity\n let max = -Infinity\n for (const mid of midpoints.values()) {\n min = Math.min(min, mid)\n max = Math.max(max, mid)\n }\n return {\n start: min === Infinity ? 0 : min,\n span: max > min ? max - min : 1,\n midpoints,\n }\n}\n\nexport function computeColorSchemeRange(graph: Graph) {\n let minDepth = Infinity\n let maxDepth = -Infinity\n let minLength = Infinity\n let maxLength = -Infinity\n let maxRank = 0\n for (const n of graph.nodes) {\n if (n.stable !== undefined && n.stable.rank > maxRank) {\n maxRank = n.stable.rank\n }\n if (n.depth < minDepth) {\n minDepth = n.depth\n }\n if (n.depth > maxDepth) {\n maxDepth = n.depth\n }\n if (n.length < minLength) {\n minLength = n.length\n }\n if (n.length > maxLength) {\n maxLength = n.length\n }\n }\n return {\n minDepth,\n maxDepth,\n minLength,\n maxLength,\n maxRank,\n nodeCount: graph.nodes.length,\n } satisfies ColorSchemeRange\n}\n\nexport function getNodeColor(\n node: GraphNode,\n nodeIndex: number,\n colorScheme: ResolvedColorScheme,\n range: ColorSchemeRange,\n) {\n switch (colorScheme) {\n case 'random':\n return hashColor(node.id, 1)\n\n case 'depth':\n return sampleGradient(\n DEPTH_GRADIENT,\n range.maxDepth > range.minDepth\n ? (node.depth - range.minDepth) / (range.maxDepth - range.minDepth)\n : 0.5,\n )\n\n case 'node-length':\n return sampleGradient(\n NODE_LENGTH_GRADIENT,\n range.maxLength > range.minLength\n ? (node.length - range.minLength) /\n (range.maxLength - range.minLength)\n : 0.5,\n )\n\n // rGFA states the backbone rather than leaving it to be inferred: rank 0 is\n // the reference, higher ranks are the sequence that diverges from it. A\n // graph with no SR tags draws every node as unranked.\n case 'stable-rank':\n return node.stable === undefined\n ? packAbgr(160, 160, 160, 255)\n : node.stable.rank === 0\n ? packAbgr(52, 152, 219, 255)\n : sampleGradient(\n STABLE_RANK_GRADIENT,\n range.maxRank > 1\n ? (node.stable.rank - 1) / (range.maxRank - 1)\n : 0,\n )\n\n // Position on the reference, the one quantity both panels of a\n // graph-over-linear figure can state. A node with no reference position at\n // all \u2014 nothing anchored, or an allele whose flanks fell outside the cut \u2014\n // is grey rather than a hue it has not earned.\n case 'reference-position': {\n const ramp = range.referenceRamp\n const mid = ramp?.midpoints.get(node.id)\n if (ramp === undefined || mid === undefined) {\n return packAbgr(160, 160, 160, 255)\n }\n if (node.stable !== undefined && node.stable.rank > 0) {\n return REFERENCE_RAMP_ALT_COLOR\n }\n const frac = Math.max(0, Math.min(1, (mid - ramp.start) / ramp.span))\n const [r, g, b] = hslToRgb(\n frac * REFERENCE_RAMP_MAX_HUE,\n REFERENCE_RAMP_SATURATION,\n REFERENCE_RAMP_LIGHTNESS,\n )\n return packNorm(r, g, b, 1)\n }\n\n case 'grey':\n return packAbgr(160, 160, 160, 255)\n\n case 'rainbow': {\n const hue = range.nodeCount > 1 ? (nodeIndex / range.nodeCount) * 360 : 0\n const [r, g, b] = hslToRgb(hue, 0.75, 0.5)\n return packNorm(r, g, b, 1)\n }\n\n default:\n return packAbgr(52, 152, 219, 255)\n }\n}\n\n// The SCREEN angle an arrowhead points: a cubic's tangent at t=1, which runs\n// from its last control point to its endpoint, so no tessellation is needed.\n// The head is expanded from a screen-px size after the transform, so its\n// direction has to be the drawn one too, and `yToX` puts the y difference in x\n// units first.\n//\n// A control point on the endpoint states no direction there. The tangent's\n// limit as t -> 1 then runs from the FIRST control point, and failing that from\n// the start. An edge with no length at all, the abutting joint of an anchored\n// layout, has none of those, and `into` (the way into the node it enters) is\n// what is left; without it such a head pointed along +x whatever the node did.\nexport function endTangent(\n c: BezierCurve,\n yToX = 1,\n into?: { from: NodeSegment; to: NodeSegment },\n) {\n const candidates: [number, number][] = [\n [c.cx1, c.cy1],\n [c.cx0, c.cy0],\n [c.x0, c.y0],\n ]\n for (const [x, y] of candidates) {\n const dx = c.x1 - x\n const dy = (c.y1 - y) * yToX\n if (Math.hypot(dx, dy) > 0) {\n return Math.atan2(dy, dx)\n }\n }\n return into\n ? Math.atan2((into.to.y - into.from.y) * yToX, into.to.x - into.from.x)\n : 0\n}\n\n// Whether any part of a node's polyline falls inside the viewport. Testing the\n// *segments* rather than their endpoints is what makes a node wider than the\n// window visible: in the reference-anchored layouts x is in bp, so a backbone\n// segment routinely spans the whole viewport with both ends outside it, and\n// point containment culled exactly the segment the view exists to show.\n//\n// Segment bounding boxes, not exact line-rect intersection: culling only has to\n// avoid dropping something visible, and a diagonal kept by its bbox costs one\n// polyline.\nfunction isPolylineInBounds(\n segments: NodeSegment[],\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n) {\n let hit = false\n for (let i = 0; i < segments.length - 1 && !hit; i++) {\n const a = segments[i]!\n const b = segments[i + 1]!\n hit =\n Math.max(a.x, b.x) >= bounds.minX &&\n Math.min(a.x, b.x) <= bounds.maxX &&\n Math.max(a.y, b.y) >= bounds.minY &&\n Math.min(a.y, b.y) <= bounds.maxY\n }\n return hit\n}\n\nfunction isBezierInBounds(\n curves: BezierCurve[],\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n) {\n for (const c of curves) {\n const cMinX = Math.min(c.x0, c.cx0, c.cx1, c.x1)\n const cMaxX = Math.max(c.x0, c.cx0, c.cx1, c.x1)\n const cMinY = Math.min(c.y0, c.cy0, c.cy1, c.y1)\n const cMaxY = Math.max(c.y0, c.cy0, c.cy1, c.y1)\n if (\n cMaxX >= bounds.minX &&\n cMinX <= bounds.maxX &&\n cMaxY >= bounds.minY &&\n cMinY <= bounds.maxY\n ) {\n return true\n }\n }\n return false\n}\n\n// Per-graph derivations, cached on the graph: this function runs on every\n// debounced pan and zoom, and a walk over every path's every step is a fact\n// about the graph rather than about the frame.\n\n// By position in the path list, matching pathLegend's swatches exactly, so the\n// key beside the drawing names the strokes in it.\n//\n// One array plus a name lookup, rather than an array and a Map of the same\n// colours: two paths that share a name then draw the same colour on their\n// edges and on their nodes, instead of the Map keeping the last one written\n// while the array kept the first. `gfaConverter` no longer produces such a\n// pair, and this is what makes the drawing merely wrong rather than\n// inconsistent if anything ever does again.\nconst paletteCache = new WeakMap<\n Graph,\n { pathColorByIndex: number[]; pathIndexByName: Map<string, number> }\n>()\n\nfunction pathPaletteOf(graph: Graph) {\n let palette = paletteCache.get(graph)\n if (!palette) {\n const pathColorByIndex: number[] = []\n const pathIndexByName = new Map<string, number>()\n const paths = graph.paths ?? []\n for (let i = 0; i < paths.length; i++) {\n const [r, g, b] = hslToRgb(\n pathHueAt(i, paths.length),\n PATH_SATURATION,\n PATH_LIGHTNESS,\n )\n pathColorByIndex.push(packNorm(r, g, b, 0.85))\n if (!pathIndexByName.has(paths[i]!.name)) {\n pathIndexByName.set(paths[i]!.name, i)\n }\n }\n palette = { pathColorByIndex, pathIndexByName }\n paletteCache.set(graph, palette)\n }\n return palette\n}\n\n// nodeId -> the indices of the paths that visit it, i.e. which stripe slots\n// this node fills. Built from the paths themselves rather than read off the\n// node, because `GraphNode.samples` collapses a sample's haplotypes together\n// and the legend does not.\nconst slotsCache = new WeakMap<Graph, Map<string, number[]>>()\nconst NO_SLOTS: ReadonlyMap<string, number[]> = new Map()\n\nfunction pathSlotsOf(graph: Graph): ReadonlyMap<string, number[]> {\n let slots = slotsCache.get(graph)\n if (!slots) {\n slots = new Map()\n for (const [pathIdx, path] of (graph.paths ?? []).entries()) {\n for (const nodeId of new Set(path.nodeIds)) {\n const visits = slots.get(nodeId)\n if (visits) {\n visits.push(pathIdx)\n } else {\n slots.set(nodeId, [pathIdx])\n }\n }\n }\n slotsCache.set(graph, slots)\n }\n return slots\n}\n\nexport function buildGeometry(options: BuildOptions): RenderBatch {\n const {\n nodePositions,\n graph,\n nodeById,\n colorScheme,\n contigThickness,\n connectorThickness,\n drawPaths,\n nodeWidth = 'uniform',\n highlight,\n axis,\n linearLayout,\n viewportBounds,\n referenceRamp,\n deletions,\n hiddenEdges,\n version = 0,\n } = options\n const depthNorm = nodeWidth === 'depth' ? meanDepth(graph) : 0\n const scale = axis.scaleX\n const yToX = yToXOf(axis)\n // The offset-zero curve of every edge, from the one place that derives it \u2014\n // shared with the hit index, which draws its boxes from the same objects.\n const sharedCurves = baseEdgeCurves(\n nodePositions,\n graph,\n axis,\n deletions,\n version,\n )\n\n const nodeStrokes: NodeStroke[] = []\n const nodeStrokeRuns = new Map<string, Run>()\n const arrows: Arrowhead[] = []\n const arrowRuns = new Map<number, Run>()\n const edgeCurves: EdgeCurveBatch[] = []\n const edgeCurveRuns = new Map<number, Run>()\n\n const colorRange = { ...computeColorSchemeRange(graph), referenceRamp }\n\n const nodeIndexMap = new Map<string, number>()\n if (colorScheme === 'rainbow') {\n for (let i = 0; i < graph.nodes.length; i++) {\n nodeIndexMap.set(graph.nodes[i]!.id, i)\n }\n }\n\n const pathCount = graph.paths?.length ?? 0\n const { pathColorByIndex, pathIndexByName } = pathPaletteOf(graph)\n const pathColorByName = (name: string) =>\n pathColorByIndex[pathIndexByName.get(name) ?? -1] ??\n EDGE_PATH_FALLBACK_COLOR\n const nodePathSlots =\n drawPaths && pathCount > 1 ? pathSlotsOf(graph) : NO_SLOTS\n\n // An arrowhead at every joint of a few hundred short nodes is a serration\n // along the whole drawing, so heads wait for a zoom where a node is longer\n // than its head.\n const showArrows = scale > (linearLayout ? 1 : MIN_ARROW_SCALE)\n\n for (let ei = 0; ei < graph.edges.length; ei++) {\n const edge = graph.edges[ei]!\n const fromSegments = nodePositions[edge.from]\n const toSegments = nodePositions[edge.to]\n const baseCurves = sharedCurves.get(ei)\n if (\n !fromSegments?.length ||\n !toSegments?.length ||\n !baseCurves ||\n hiddenEdges?.has(ei)\n ) {\n continue\n }\n\n const numPaths = edge.pathIds?.length ?? 0\n const bypassed = deletions?.get(ei)\n const isDeletion = bypassed !== undefined\n const onWalk = highlight?.edgeIndexes.has(ei) ?? false\n const edgeThickness =\n (connectorThickness / 2) * (isDeletion ? DELETION_THICKNESS_FACTOR : 1)\n // One stroke per path crossing the edge, fanned off it: the shared curve\n // slid sideways, which is also what the hit index tests against. Rebuilding\n // the curve at each offset was 40% of a striped build and differed from a\n // translation only in a deletion's bow, by the offset's few px.\n const ribbons = drawPaths && numPaths > 0\n\n // An arrowhead states which way a LINK is read, which is a property of the\n // edge and not of each path crossing it. Drawn per ribbon it says one thing\n // once per haplotype: five stacked heads at a joint, in five colours, for a\n // single fact about a single link. That is the \"coloured confetti at each\n // joint\" that made `drawPaths` unusable on a force layout, and it reached\n // the anchored layouts too the moment their abutting edges started drawing\n // again. Measured on `ecoli_pggb_subgraph.gfa`: 72 arrowheads plain, 155\n // with ribbons, over 72 links.\n //\n // So one head per edge, in the edge's own colour rather than a ribbon's,\n // since choosing a ribbon's would privilege one haplotype for a fact that\n // belongs to none of them. `undefined` means this call draws no head.\n const plainEdgeColor = isDeletion ? EDGE_DELETION_COLOR : EDGE_DEFAULT_COLOR\n const edgeColor =\n !highlight || onWalk\n ? plainEdgeColor\n : fadeAbgr(plainEdgeColor, FADED_ALPHA)\n const toNode = nodeById.get(edge.to)\n const intoHalfWidth =\n (toNode\n ? nodeWidthPx(toNode, contigThickness, nodeWidth, depthNorm)\n : contigThickness) / 2\n const buildSingleEdge = (\n offsetX: number,\n offsetY: number,\n color: number,\n arrowColor: number | undefined,\n ) => {\n const curves =\n offsetX === 0 && offsetY === 0\n ? baseCurves\n : translateCurves(baseCurves, offsetX, offsetY)\n\n if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {\n return\n }\n\n // A deletion is drawn dashed, and that is the only thing in the drawing\n // that is. Weight and darkness were carrying it alone, and they could not:\n // an off-reference node is charcoal rgb(60,65,72) and the arc is near-black\n // rgb(24,24,28), which is ~25 points a channel apart, so in a figure the\n // two dark strokes read as the same ink and the arc is the rarer of them\n // (measured on `pangenome/hprc_lpa_kiv2`: 7,411 px of charcoal node against\n // ~5,000 px of arc). Hue cannot separate them either, since hue means\n // reference position and charcoal means having none. A broken line is\n // available, unused, and says what it draws: sequence that is not there.\n if (isDeletion) {\n for (const dash of dashCurves(curves, DELETION_DASH_PX / scale)) {\n edgeCurves.push({ curves: dash, thickness: edgeThickness, color })\n }\n } else {\n edgeCurves.push({ curves, thickness: edgeThickness, color })\n }\n\n const head =\n showArrows && arrowColor !== undefined\n ? arrowheadFor({\n curves,\n edgeHalfWidth: edgeThickness,\n nodeHalfWidth: intoHalfWidth,\n scale,\n yToX,\n color: arrowColor,\n fromSegments,\n toSegments,\n })\n : undefined\n if (head) {\n arrows.push(head)\n }\n }\n\n const edgeCurveStart = edgeCurves.length\n const arrowStart = arrows.length\n\n // A lifted walk's link carries on its lane from the node it leaves, so a\n // lane runs unbroken through the joints rather than beside a grey link\n const laneWalks =\n highlight && onWalk && !ribbons ? highlight.walks : undefined\n if (laneWalks) {\n const fromNode = nodeById.get(edge.from)\n const width = fromNode\n ? nodeWidthPx(fromNode, contigThickness, nodeWidth, depthNorm)\n : contigThickness\n const laneWidth = Math.max(width / laneWalks.length, MIN_WALK_LANE_PX)\n // the lanes' sideways direction where the link leaves its node, which\n // is the end a '-' link leaves from the start of\n const normals = pointNormalsOf(fromSegments, yToX)\n const { nx, ny } = (edge.fromStrand === '-'\n ? normals[0]\n : normals.at(-1)) ?? {\n nx: 0,\n ny: 0,\n }\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n laneWalks.forEach((walk, lane) => {\n if (!walk.edgeIndexes.has(ei)) {\n return\n }\n const offset =\n (lane - (laneWalks.length - 1) / 2) * laneWidth * worldPerScreenPx\n const curves =\n offset === 0\n ? baseCurves\n : translateCurves(baseCurves, nx * offset, (ny * offset) / yToX)\n if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {\n return\n }\n edgeCurves.push({\n curves,\n thickness: laneWidth / 2,\n color:\n walk.colors.get(edge.from) ??\n walk.colors.get(edge.to) ??\n plainEdgeColor,\n })\n })\n } else if (!ribbons) {\n buildSingleEdge(0, 0, edgeColor, edgeColor)\n } else {\n const offsets = pathRibbonOffsets(\n fromSegments,\n toSegments,\n numPaths,\n axis,\n )\n // The middle ribbon carries the head, which at an odd path count is the\n // one drawn at offset 0, i.e. on the edge's own line. At an even count it\n // is half a spacing off, which beats a sixth `computeEdgeCurves` call to\n // rebuild a curve the fan is already centred on.\n const arrowRibbon = Math.floor((numPaths - 1) / 2)\n for (let pathIdx = 0; pathIdx < numPaths; pathIdx++) {\n const offset = offsets[pathIdx]!\n const ribbonColor = pathColorByName(edge.pathIds![pathIdx]!)\n buildSingleEdge(\n offset.x,\n offset.y,\n highlight && !highlight.names.has(edge.pathIds![pathIdx]!)\n ? fadeAbgr(ribbonColor, FADED_ALPHA)\n : ribbonColor,\n pathIdx === arrowRibbon ? edgeColor : undefined,\n )\n }\n }\n\n const edgeCurveCount = edgeCurves.length - edgeCurveStart\n if (edgeCurveCount > 0) {\n edgeCurveRuns.set(ei, { start: edgeCurveStart, count: edgeCurveCount })\n }\n const arrowCount = arrows.length - arrowStart\n if (arrowCount > 0) {\n arrowRuns.set(ei, { start: arrowStart, count: arrowCount })\n }\n }\n\n for (const [nodeId, segments] of Object.entries(nodePositions)) {\n const node = nodeById.get(nodeId)\n if (!node) {\n continue\n }\n\n if (viewportBounds && !isPolylineInBounds(segments, viewportBounds)) {\n continue\n }\n\n const faded = highlight !== undefined && !highlight.nodeIds.has(nodeId)\n // Under lifted walks the lanes carry every colour, so the rest fades in\n // grey rather than in a scheme no lane is drawn in\n const own = faded\n ? LIFT_BACKDROP_COLOR\n : getNodeColor(\n node,\n nodeIndexMap.get(nodeId) ?? 0,\n colorScheme,\n colorRange,\n )\n const color = faded ? fadeAbgr(own, FADED_ALPHA) : own\n const width = nodeWidthPx(node, contigThickness, nodeWidth, depthNorm)\n const nodeThickness = width / 2\n\n const start = nodeStrokes.length\n // The node's drawn width is the same either way: the stripes divide it\n // rather than inflate it, so turning paths on does not re-weight the\n // drawing against the edges and the layout underneath it.\n //\n // A thickness is SCREEN px and a point is world units. So the slot width is\n // screen px and the sideways shift that places a stripe has to be taken\n // back into world units, or the stripes fan apart as you zoom in and\n // collapse into one as you zoom out.\n const slots = nodePathSlots.get(nodeId)\n const slotWidth = width / pathCount\n const drawable = segments.length >= 2\n // Each lifted walk keeps one lane across the whole drawing, so a lane\n // missing from a node is a walk that does not visit it\n const lanes =\n highlight && !faded && !slots?.length ? highlight.walks : undefined\n if (drawable && lanes) {\n const laneWidth = Math.max(width / lanes.length, MIN_WALK_LANE_PX)\n const normals = pointNormalsOf(segments, yToX)\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n lanes.forEach((walk, lane) => {\n const laneColor = walk.colors.get(nodeId)\n if (laneColor === undefined) {\n return\n }\n const offset =\n (lane - (lanes.length - 1) / 2) * laneWidth * worldPerScreenPx\n nodeStrokes.push({\n points:\n lanes.length === 1\n ? segments\n : offsetPolyline(segments, normals, offset, yToX),\n thickness: laneWidth / 2,\n color: laneColor,\n })\n })\n } else if (drawable && slots?.length && slotWidth >= MIN_PATH_STRIPE_PX) {\n const normals = pointNormalsOf(segments, yToX)\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n for (const slot of slots) {\n const offset =\n (slot - (pathCount - 1) / 2) * slotWidth * worldPerScreenPx\n const stripe = pathColorByIndex[slot] ?? EDGE_PATH_FALLBACK_COLOR\n nodeStrokes.push({\n points: offsetPolyline(segments, normals, offset, yToX),\n thickness: slotWidth / 2,\n // a node off the lifted walk fades whichever way it is painted\n color: faded ? fadeAbgr(stripe, FADED_ALPHA) : stripe,\n })\n }\n } else if (drawable) {\n nodeStrokes.push({ points: segments, thickness: nodeThickness, color })\n }\n const count = nodeStrokes.length - start\n if (count > 0) {\n nodeStrokeRuns.set(nodeId, { start, count })\n }\n }\n\n return {\n nodeStrokes,\n nodeStrokeRuns,\n arrows,\n arrowRuns,\n edgeCurves,\n edgeCurveRuns,\n }\n}\n", "import {\n abgrAlpha,\n abgrBlue,\n abgrGreen,\n abgrRed,\n packAbgr,\n} from '@jbrowse/core/util/colorBits'\n\n// The one ABGR helper core does not have. Everything else the renderer needs \u2014\n// the packing, the channel accessors, both css formatters \u2014 comes straight from\n// `@jbrowse/core/util/colorBits`, which ReExports/list.ts now names, so the\n// plugin bundle binds it to the host's copy. The plugin targets the latest\n// JBrowse beta; 5.0.0-beta.4 serves that module without `packAbgr`, and the\n// plugin fails to install there.\n\n// Scale a packed color's channels, clamped at full brightness and leaving alpha\n// alone. factor === 1 returns the color unchanged.\n// The same colour at a fraction of its alpha, so a faded node reads as the\n// same ink through it on any background.\nexport function fadeAbgr(c: number, alpha: number) {\n return packAbgr(\n abgrRed(c),\n abgrGreen(c),\n abgrBlue(c),\n Math.round(abgrAlpha(c) * alpha),\n )\n}\n\nexport function brightenAbgr(c: number, factor: number) {\n return packAbgr(\n Math.min(255, Math.round(abgrRed(c) * factor)),\n Math.min(255, Math.round(abgrGreen(c) * factor)),\n Math.min(255, Math.round(abgrBlue(c) * factor)),\n abgrAlpha(c),\n )\n}\n", "import { isBackbone } from './anchoredNodes'\n\nimport type { Graph, GraphNode } from './types'\n\n// How long an allele chain can be and still be located from both ends. An\n// allele is a run of off-reference segments between two backbone flanks, so the\n// two flanks sit at *different* hop depths from any segment in the middle and\n// the walk has to keep going after it finds the first one. Minigraph alleles are\n// short runs, so this reaches both flanks throughout the fixtures; a longer run\n// degrades to the flanks it did reach rather than to nothing.\nconst MAX_BACKBONE_HOPS = 4\n\nexport interface ReferenceSpan {\n start: number\n end: number\n}\n\n// Undirected adjacency over node ids. Edges carry the topology in both\n// directions for this purpose: finding the backbone a node hangs off doesn't\n// care which way the L line was written.\nexport function buildNeighbors(graph: Graph) {\n const neighbors = new Map<string, string[]>()\n function add(from: string, to: string) {\n const list = neighbors.get(from)\n if (list) {\n list.push(to)\n } else {\n neighbors.set(from, [to])\n }\n }\n for (const edge of graph.edges) {\n add(edge.from, edge.to)\n add(edge.to, edge.from)\n }\n return neighbors\n}\n\n// The reference interval an allele branches into: between the end of the\n// backbone segment upstream of it and the start of the one downstream.\n// `[min(ends), max(starts)]` picks those two out of the reached flanks without\n// needing to know which is which, and it degrades usefully in both directions \u2014\n// a pure insertion, whose flanks abut, collapses to the single reference point\n// it is inserted at, and a bubble with only one flank inside the window yields\n// that flank's own span.\nfunction flankingSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}) {\n const seen = new Set([nodeId])\n let frontier = [nodeId]\n let minEnd = Infinity\n let maxStart = -Infinity\n\n // Backbone nodes are terminal \u2014 they are collected, never queued \u2014 so the walk\n // cannot cross the reference into a neighbouring bubble. It stays inside the\n // allele it started in and stops on its own at the flanks.\n for (let hop = 0; hop < MAX_BACKBONE_HOPS && frontier.length > 0; hop++) {\n const next: string[] = []\n for (const id of frontier) {\n const adjacent = neighbors.get(id)\n if (adjacent) {\n for (const neighborId of adjacent) {\n if (!seen.has(neighborId)) {\n seen.add(neighborId)\n const neighbor = nodeById.get(neighborId)\n if (neighbor && isBackbone(neighbor)) {\n minEnd = Math.min(minEnd, neighbor.stable.start + neighbor.length)\n maxStart = Math.max(maxStart, neighbor.stable.start)\n } else {\n next.push(neighborId)\n }\n }\n }\n }\n }\n frontier = next\n }\n\n return minEnd === Infinity\n ? undefined\n : {\n start: Math.min(minEnd, maxStart),\n end: Math.max(minEnd, maxStart),\n }\n}\n\n// The reference interval to highlight for a node, in the coordinates of the\n// stable sequence the subgraph was cut from.\n//\n// A rank-0 node states its own, so the interval is exact. A rank>0 allele has\n// none: its SO is an offset on a *different* stable sequence and is not\n// comparable to the reference axis (see the axis note in\n// layout/anchoredLayout.ts), so it is located by the backbone it branches from\n// instead.\nexport function nodeReferenceSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}): ReferenceSpan | undefined {\n const node = nodeById.get(nodeId)\n return node === undefined\n ? undefined\n : isBackbone(node)\n ? { start: node.stable.start, end: node.stable.start + node.length }\n : flankingSpan({ nodeId, nodeById, neighbors })\n}\n\n// Every node's midpoint on the reference, keyed by id: what a position-based\n// colour scheme ramps over. Alleles come through the same flanking walk hover\n// sync uses, so a bubble takes the colour of the reference interval it branches\n// from instead of dropping out of the ramp \u2014 which is the whole point, since an\n// allele is exactly what a linear view cannot draw at a position of its own.\nexport function referenceMidpoints(graph: Graph) {\n const nodeById = new Map(graph.nodes.map(node => [node.id, node]))\n const neighbors = buildNeighbors(graph)\n const midpoints = new Map<string, number>()\n for (const node of graph.nodes) {\n const span = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors })\n if (span) {\n midpoints.set(node.id, (span.start + span.end) / 2)\n }\n }\n return midpoints\n}\n", "import { bypassedPoints } from '../deletionEdges'\nimport { computeEdgeCurves, selfLinkOf } from './geometry'\n\nimport type { Graph, NodeSegment } from '../types'\nimport type { AxisScale, BezierCurve } from './geometry'\n\n// Every edge's curve as it is DRAWN, at offset zero, derived once for a layout\n// at a zoom.\n//\n// `computeEdgeCurves` already says why there must be one derivation of this\n// rather than several: the drawing, the hit index and the label that rides an\n// arc all have to agree on where the curve is, and a second derivation is a\n// second thing to keep in step. That was true of the CODE and not of the WORK \u2014\n// the geometry builder and the edge hit index called it with identical\n// arguments, per edge, and each paid in full. The index's whole build was 14 ms\n// on a 12k-edge cut and most of it was this, redone.\n//\n// So the answer is the same shape as the argument: one function, and everyone\n// who wants the offset-zero curve of edge `ei` asks it. A path ribbon is that\n// curve rebuilt at its own offset, which is the one variant that is not shared.\n//\n// Cached the way hit detection caches its indexes, and for the same reasons: a\n// WeakMap on the positions object so two graph views cannot evict each other and\n// an entry dies with the layout it describes, plus a check of everything the\n// curves actually depend on. `deletions` is in that list because it decides\n// which edges bow and around what; `version` is, because a node drag mutates the\n// positions in place and their identity cannot report it.\nexport type EdgeCurves = Map<number, BezierCurve[]>\n\nconst cache = new WeakMap<\n Record<string, NodeSegment[]>,\n {\n graph: Graph\n scaleX: number\n scaleY: number\n deletions: Map<number, string[]> | undefined\n version: number\n curves: EdgeCurves\n }\n>()\n\nexport function baseEdgeCurves(\n nodePositions: Record<string, NodeSegment[]>,\n graph: Graph,\n axis: AxisScale,\n // bypassed backbone ids per deletion edge, i.e. `deletionEdges()` keyed by\n // edge index. An edge named here bows around the run it skips.\n deletions: Map<number, string[]> | undefined,\n version: number,\n): EdgeCurves {\n const { scaleX, scaleY } = axis\n const cached = cache.get(nodePositions)\n if (\n cached?.graph === graph &&\n cached.scaleX === scaleX &&\n cached.scaleY === scaleY &&\n cached.deletions === deletions &&\n cached.version === version\n ) {\n return cached.curves\n }\n const curves: EdgeCurves = new Map()\n for (let ei = 0; ei < graph.edges.length; ei++) {\n const edge = graph.edges[ei]!\n const from = nodePositions[edge.from]\n const to = nodePositions[edge.to]\n // An edge whose endpoints the layout did not place has no curve, and its\n // absence here is the same \"skip it\" every consumer already applies.\n if (from?.length && to?.length) {\n const bypassed = deletions?.get(ei)\n curves.set(\n ei,\n computeEdgeCurves(\n from,\n to,\n selfLinkOf(edge),\n 0,\n 0,\n axis,\n bypassed ? bypassedPoints(nodePositions, bypassed) : [],\n ),\n )\n }\n }\n cache.set(nodePositions, {\n graph,\n scaleX,\n scaleY,\n deletions,\n version,\n curves,\n })\n return curves\n}\n", "import { isAnchored } from '@jbrowse/bandage-core/anchoredNodes'\nimport {\n panSNContig,\n panSNHaplotype,\n panSNSample,\n} from '@jbrowse/bandage-core/pansn'\n\nimport type { AnchoredNode } from '@jbrowse/bandage-core/anchoredNodes'\nimport type { Graph, GraphNode } from '@jbrowse/bandage-core/types'\n\nexport interface GraphLocation {\n // PanSN sample name off the node's own stable sequence. The assembly that\n // contributed the sequence, which is a *name*, not a resolved assembly \u2014 see\n // resolveContributors.\n sample: string\n // `sample#hap` where the stable name states a haplotype (HPRC release 2.1\n // names every node `NA20809#2#CM094351.1`), so a sample loaded as two\n // assemblies opens the right one; undefined where it states none\n haplotype: string | undefined\n refName: string\n start: number\n end: number\n}\n\n// Where a node's own sequence lives, in the coordinates of the assembly that\n// contributed it. rGFA states this on every segment: SN names the stable\n// sequence and SO the offset on it, so this is exact at every rank.\n//\n// It is the other question from nodeReferenceSpan, which asks where a node sits\n// on the *reference* axis and can only approximate that off the backbone. A\n// rank>0 node has an exact coordinate of its own; what it has no coordinate for\n// is the reference.\nexport function nodeOwnLocation(node: GraphNode): GraphLocation | undefined {\n return isAnchored(node) ? anchoredNodeLocation(node) : undefined\n}\n\nfunction anchoredNodeLocation(node: AnchoredNode): GraphLocation {\n return {\n sample: panSNSample(node.stable.refName),\n haplotype: panSNHaplotype(node.stable.refName),\n refName: panSNContig(node.stable.refName),\n start: node.stable.start,\n end: node.stable.start + node.length,\n }\n}\n\nexport interface Contributor extends GraphLocation {\n // lowest rank this assembly holds here: 0 means it is the reference backbone\n rank: number\n nodeCount: number\n}\n\ninterface Span {\n start: number\n end: number\n}\n\n// The widest run of spans with no gap larger than `maxGap`, which is the locus\n// this assembly contributes *here*. A bare union of every span would be wrong\n// on a graph where a sample also contributes sequence from a distant\n// duplication: the union then reads as a multi-megabase locus, and a linear view\n// launched at it shows neither end.\nfunction widestCluster(spans: Span[], maxGap: number) {\n const sorted = [...spans].sort((a, b) => a.start - b.start)\n let best: Span | undefined\n let current: Span | undefined\n for (const span of sorted) {\n current =\n current && span.start - current.end <= maxGap\n ? { start: current.start, end: Math.max(current.end, span.end) }\n : { start: span.start, end: span.end }\n if (!best || current.end - current.start > best.end - best.start) {\n best = current\n }\n }\n return best\n}\n\ninterface Group {\n sample: string\n haplotype: string | undefined\n refName: string\n rank: number\n spans: Span[]\n}\n\nfunction groupKey(loc: GraphLocation) {\n return `${loc.haplotype ?? loc.sample}\u0000${loc.refName}`\n}\n\n// Every assembly with a segment in this graph, and the locus each one covers on\n// its own coordinates.\n//\n// This is what makes a launch out of the graph possible at all: the graph names\n// its contributors in the data (rGFA's SN tag), so a node, a row and the graph\n// as a whole can each say which assembly they belong to without consulting an\n// alignment.\n//\n// Ordered reference-first, then by name, so the row order matches\n// sampleRowLayout's and neither reshuffles as the window moves.\nexport function contributingAssemblies(\n graph: Graph,\n { maxGap = Infinity }: { maxGap?: number } = {},\n): Contributor[] {\n const groups = new Map<string, Group>()\n for (const node of graph.nodes) {\n if (isAnchored(node)) {\n const loc = anchoredNodeLocation(node)\n const key = groupKey(loc)\n const group = groups.get(key)\n if (group) {\n group.spans.push({ start: loc.start, end: loc.end })\n group.rank = Math.min(group.rank, node.stable.rank)\n } else {\n groups.set(key, {\n sample: loc.sample,\n haplotype: loc.haplotype,\n refName: loc.refName,\n rank: node.stable.rank,\n spans: [{ start: loc.start, end: loc.end }],\n })\n }\n }\n }\n\n // One entry per assembly (a haplotype where the names state one, else the\n // sample): an assembly contributing on several contigs is launched at the\n // contig it contributes most of, since a linear view opens on one region.\n const bySample = new Map<string, Contributor>()\n for (const group of groups.values()) {\n const span = widestCluster(group.spans, maxGap)\n if (span) {\n const candidate = {\n sample: group.sample,\n haplotype: group.haplotype,\n refName: group.refName,\n start: span.start,\n end: span.end,\n rank: group.rank,\n nodeCount: group.spans.length,\n }\n const key = group.haplotype ?? group.sample\n const existing = bySample.get(key)\n if (!existing || candidate.nodeCount > existing.nodeCount) {\n bySample.set(key, candidate)\n }\n }\n }\n\n // Reference first, then by name: the same order sampleRowLayout gives its\n // rows, so the menu lists the assemblies in the order they are stacked on\n // screen. Sorting by rank instead would order them by minigraph's build\n // order, which is not what the labels beside the drawing say.\n return [...bySample.values()].sort(\n (a, b) =>\n Number(a.rank !== 0) - Number(b.rank !== 0) ||\n a.sample.localeCompare(b.sample) ||\n (a.haplotype ?? '').localeCompare(b.haplotype ?? ''),\n )\n}\n\n// The assembly a location opens on: its haplotype's where the name states one\n// and the session holds it, else its sample's. A session holding both\n// haplotypes of a sample opens the right one; one holding a haplotype under\n// the bare sample name still opens it.\nexport function resolveLocationAssembly(\n resolve: (sample: string) => string | undefined,\n loc: Pick<GraphLocation, 'sample' | 'haplotype'>,\n) {\n const byHaplotype =\n loc.haplotype === undefined ? undefined : resolve(loc.haplotype)\n return byHaplotype ?? resolve(loc.sample)\n}\n\n// Contributors that name an assembly this session has loaded, which are the\n// only ones a linear or synteny view can open on.\n//\n// The two cases this filters apart are both normal. An E. coli pangenome demo\n// loads every strain as its own assembly, so every contributor resolves and the\n// whole graph is navigable. An HPRC graph names hundreds of haplotypes that no\n// session would ever load as assemblies, so only the reference resolves \u2014\n// there the reference projection (nodeReferenceSpan) is the way back to a\n// coordinate, not this.\n//\n// `resolve` is a name lookup rather than a list of names, because a PanSN sample\n// is the *graph's* spelling and need not be the assembly's. HPRC's graph writes\n// `CHM13`, and the assembly holding that sequence is UCSC's `hs1`, with CHM13\n// among its aliases; matching on names alone leaves that donor unopenable unless\n// the reader renames their assembly after the graph. The resolved (canonical)\n// name replaces the sample, so a launch opens the assembly the session knows.\nexport function resolveContributors(\n contributors: Contributor[],\n resolve: (sample: string) => string | undefined,\n) {\n return contributors.flatMap(c => {\n const sample = resolveLocationAssembly(resolve, c)\n return sample === undefined ? [] : [{ ...c, sample }]\n })\n}\n\n// 1-based inclusive, which is what a locstring means. Unformatted digits: this\n// is parsed, not read. locLabel is the one people read.\nexport function locString(loc: GraphLocation) {\n return `${loc.refName}:${loc.start + 1}-${loc.end}`\n}\n\nexport function locLabel(loc: { refName: string; start: number; end: number }) {\n return `${loc.refName}:${(loc.start + 1).toLocaleString()}-${loc.end.toLocaleString()}`\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M9.01 14H2v2h7.01v3L13 15l-3.99-4zm5.98-1v-3H22V8h-7.01V5L11 9z\"\n}), 'CompareArrows');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M17 5h-2V3h2zm-2 16h2v-2.59L19.59 21 21 19.59 18.41 17H21v-2h-6zm4-12h2V7h-2zm0 4h2v-2h-2zm-8 8h2v-2h-2zM7 5h2V3H7zM3 17h2v-2H3zm2 4v-2H3c0 1.1.9 2 2 2M19 3v2h2c0-1.1-.9-2-2-2m-8 2h2V3h-2zM3 9h2V7H3zm4 12h2v-2H7zm-4-8h2v-2H3zm0-8h2V3c-1.1 0-2 .9-2 2\"\n}), 'HighlightAlt');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M3 16h5v-2H3zm6.5 0h5v-2h-5zm6.5 0h5v-2h-5zM3 20h2v-2H3zm4 0h2v-2H7zm4 0h2v-2h-2zm4 0h2v-2h-2zm4 0h2v-2h-2zM3 12h8v-2H3zm10 0h8v-2h-8zM3 4v4h18V4z\"\n}), 'LineStyle');", "import CompareArrowsIcon from '@mui/icons-material/CompareArrows'\nimport HighlightAltIcon from '@mui/icons-material/HighlightAlt'\nimport LineStyleIcon from '@mui/icons-material/LineStyle'\n\nimport { locLabel } from './contributors'\n\nimport type { Contributor, GraphLocation } from './contributors'\nimport type { LaunchableSyntenyTrack } from './syntenyTracks'\nimport type { MenuItem } from '@jbrowse/core/ui'\n\nconst LINEAR_LABEL = 'Linear genome view'\nconst SYNTENY_LABEL = 'Linear synteny view'\nconst NO_SYNTENY_TRACK =\n 'No synteny track in this session aligns these assemblies'\n\nexport interface LinearTarget {\n location: GraphLocation\n assembly: string\n}\n\n// One flat item when there is one thing to open, a submenu when there is a\n// choice. Same shape as subgraphMenuItems uses in the other direction, so the\n// two ends of the graph <-> linear trip read the same way.\nfunction oneOrMany<T>({\n label,\n icon,\n entries,\n entryLabel,\n onSelect,\n disabledHelpText,\n nameTheOnlyEntry = true,\n}: {\n label: string\n icon: React.ElementType\n entries: T[]\n entryLabel: (entry: T) => string\n onSelect: (entry: T) => () => void\n disabledHelpText?: string\n // Whether a lone entry's name is worth appending to the item. It is when the\n // entry is where you land (`GRCh38 chr6:31,980,000-32,050,000`) and not when\n // it is the ingredient used to get there \u2014 a synteny track's name says\n // nothing the item does not already say, and reading it costs the width of\n // `pggb graph: all-vs-all synteny (wfmash)`.\n nameTheOnlyEntry?: boolean\n}): MenuItem[] {\n const [first] = entries\n // Nothing to open: a *disabled* item with the reason in its tooltip, when\n // there is a reason worth reading. An item that vanishes teaches nobody that\n // the session is one synteny track short of a multi-genome view.\n const empty: MenuItem[] = disabledHelpText\n ? [{ label, icon, disabled: true, disabledHelpText, onClick: () => {} }]\n : []\n return entries.length === 0\n ? empty\n : entries.length === 1 && first\n ? [\n {\n label: nameTheOnlyEntry ? `${label} \u2014 ${entryLabel(first)}` : label,\n icon,\n onClick: onSelect(first),\n },\n ]\n : [\n {\n label,\n icon,\n type: 'subMenu',\n subMenu: entries.map(entry => ({\n label: entryLabel(entry),\n onClick: onSelect(entry),\n })),\n },\n ]\n}\n\n// The graph's way out: a linear view of any assembly the graph names here, and a\n// synteny view of all of them at once. Until this existed the triangle had two\n// edges \u2014 a linear view could open a graph or a synteny view of a locus, and the\n// graph could open nothing at all.\n//\n// `contributors` are already resolved against the session's assemblies. On an\n// E. coli pangenome every strain is its own assembly, so all of them appear and\n// the synteny item is the interesting one; on an HPRC graph the 400-odd\n// contributing haplotypes are not loaded assemblies and only the reference\n// appears, which is the honest answer rather than a missing feature.\nexport function graphLaunchMenuItems({\n contributors,\n syntenyTracks,\n onShowLinear,\n onShowSynteny,\n}: {\n contributors: Contributor[]\n syntenyTracks: LaunchableSyntenyTrack[]\n onShowLinear: (target: LinearTarget) => void\n onShowSynteny: (trackId: string) => void\n}): MenuItem[] {\n // sorted widest first\n const widest = syntenyTracks[0]?.coverage ?? contributors.length\n return [\n ...oneOrMany({\n label: LINEAR_LABEL,\n icon: LineStyleIcon,\n entries: contributors,\n entryLabel: c => `${c.sample} ${locLabel(c)}`,\n onSelect: c => () => {\n onShowLinear({ location: c, assembly: c.sample })\n },\n }),\n ...(contributors.length >= 2\n ? oneOrMany({\n // the count the launch opens, which is what the widest track aligns\n // and not always every contributor; see launchSyntenyView\n label: `${SYNTENY_LABEL} (${widest} assemblies)`,\n icon: CompareArrowsIcon,\n entries: syntenyTracks,\n entryLabel: track =>\n track.coverage < widest\n ? `${track.name} (${track.coverage} of ${widest})`\n : track.name,\n nameTheOnlyEntry: false,\n disabledHelpText: NO_SYNTENY_TRACK,\n onSelect: track => () => {\n onShowSynteny(track.trackId)\n },\n })\n : []),\n ]\n}\n\n// A single node's way out, for the node context menu. Two different questions,\n// both worth offering:\n//\n// - the node's own coordinates, on the assembly that contributed it. Exact,\n// from the node's SN/SO, and available at every rank \u2014 but only openable\n// when that assembly is loaded, which for an HPRC haplotype it is not.\n// - where the node sits on the reference the graph was cut against. Always\n// openable, and off the backbone it is the flanking projection rather than\n// an exact span (see nodeReferenceSpan), so it is labelled as the region\n// around the node rather than as the node.\n//\n// Labels are verbs. Review: \"the lineargenomeview menu item should say launch\n// if that is what it does\" \u2014 a bare `Linear genome view \u2014 hg38 chr6:\u2026` names a\n// place and leaves the reader to guess whether clicking scrolls the view beside\n// the graph or opens a new one. It does the first when there is one to scroll\n// (see showInLinearView), so \"Open in\" covers both without claiming a launch.\n//\n// Verb, assembly, and where \u2014 nothing else. Every word here is read inside a\n// menu opened on a node, so \"this node in the hg38 view\" and \"in linear view\"\n// restate the context they are already in; what a coordinate opens in is not in\n// question, and the assembly name is.\n//\n// Flat, not under a \"Launch view\" submenu: this menu is a handful of items and\n// every one is contextual to the node just clicked.\nexport function nodeLaunchMenuItems({\n own,\n reference,\n highlight,\n onShowLinear,\n onHighlight,\n}: {\n own: LinearTarget | undefined\n reference: LinearTarget | undefined\n // the node's exact reference span, where `reference` is padded to open on\n highlight: LinearTarget | undefined\n onShowLinear: (target: LinearTarget) => void\n // absent when no linear view in the session is this graph's to mark\n onHighlight?: (target: LinearTarget) => void\n}): MenuItem[] {\n const entries = [\n own\n ? {\n label: `Open in ${own.assembly} \u2014 ${locLabel(own.location)}`,\n target: own,\n }\n : undefined,\n // Only when it says something the item above doesn't. A backbone segment on\n // a loaded reference answers both questions with the same assembly and\n // nearly the same span, and offering that twice reads as two different\n // places to go.\n reference && reference.assembly !== own?.assembly\n ? {\n label: `Open in ${reference.assembly} \u2014 around this node`,\n target: reference,\n }\n : undefined,\n ].filter(entry => entry !== undefined)\n\n // The reference projection, not the node's own coordinates: a highlight is\n // drawn in the linear view already on screen, and that view is on the\n // reference the subgraph was cut from.\n return [\n ...(onHighlight && highlight\n ? [\n {\n label: `Highlight in ${highlight.assembly}`,\n icon: HighlightAltIcon,\n onClick: () => {\n onHighlight(highlight)\n },\n },\n ]\n : []),\n ...entries.map(({ label, target }) => ({\n label,\n icon: LineStyleIcon,\n onClick: () => {\n onShowLinear(target)\n },\n })),\n ]\n}\n", "import { parseCs } from '../gaf/parseGaf'\n\nimport type { GafRecord } from '../gaf/parseGaf'\nimport type { Graph, GraphNode } from '../types'\nimport type { InputTrack, Mismatch } from '@gmod/tubemap-core'\n\n// GAF reads as tube map reads over a cut. A read keeps the steps whose\n// segments the cut holds, and its cs edits land on the node they fall in, at\n// a position in the direction the read walks it, as vg's GAM mappings state\n// theirs.\n\nexport function canonicalStrand(node: GraphNode) {\n return node.id.endsWith('-') ? '-' : '+'\n}\n\n// A visit to a node as tubemap-core names it: `-` where the walk runs against\n// the strand the node is drawn in\nexport function stepName(node: GraphNode, strand: '+' | '-') {\n return strand === canonicalStrand(node) ? node.id : `-${node.id}`\n}\n\n// Where each step starts on the walk, where that can be known: counted from\n// the walk's start up to the first segment outside the cut, and back from its\n// end down to the last. A step between two such segments has no start.\nfunction stepStarts(lengths: readonly (number | undefined)[], total: number) {\n const starts: (number | undefined)[] = lengths.map(() => undefined)\n let head = 0\n let i = 0\n for (; i < lengths.length && lengths[i] !== undefined; i++) {\n starts[i] = head\n head += lengths[i]!\n }\n let tail = total\n for (let j = lengths.length - 1; j > i && lengths[j] !== undefined; j--) {\n tail -= lengths[j]!\n starts[j] = tail\n }\n return starts\n}\n\nfunction mismatchesByStep(\n record: GafRecord,\n lengths: readonly (number | undefined)[],\n) {\n const byStep: Mismatch[][] = record.path.map(() => [])\n if (record.cs === undefined) {\n return byStep\n }\n const starts = stepStarts(lengths, record.pathLength)\n // the step holding walk position `pos`, or for `atEnd` the one ending there\n const stepAt = (pos: number, atEnd = false) => {\n for (let s = 0; s < starts.length; s++) {\n const start = starts[s]\n const length = lengths[s]\n if (\n start !== undefined &&\n length !== undefined &&\n start <= pos &&\n (pos < start + length || (atEnd && pos === start + length))\n ) {\n return { s, local: pos - start, room: start + length - pos }\n }\n }\n return undefined\n }\n const add = (s: number, mismatch: Mismatch) => {\n const list = byStep[s]!\n const last = list.at(-1)\n if (\n mismatch.type === 'substitution' &&\n last?.type === 'substitution' &&\n last.pos + last.seq!.length === mismatch.pos\n ) {\n last.seq += mismatch.seq!\n } else {\n list.push(mismatch)\n }\n }\n let pos = record.pathStart\n for (const op of parseCs(record.cs)) {\n if (op.op === 'match') {\n pos += op.length\n } else if (op.op === 'substitution') {\n const at = stepAt(pos)\n if (at) {\n add(at.s, { type: 'substitution', pos: at.local, seq: op.seq })\n }\n pos += 1\n } else if (op.op === 'insertion') {\n const at = stepAt(pos, pos === record.pathEnd)\n if (at) {\n add(at.s, { type: 'insertion', pos: at.local, seq: op.seq })\n }\n } else {\n let left = op.length\n while (left > 0) {\n const at = stepAt(pos)\n const take = at ? Math.min(left, at.room) : left\n if (at) {\n add(at.s, { type: 'deletion', pos: at.local, length: take })\n }\n pos += take\n left -= take\n }\n }\n }\n return byStep\n}\n\n// `firstId` follows the haplotype tracks' ids, which tubemap-core shares\n// between tracks and reads.\nexport function tubeMapReads(\n graph: Graph,\n records: readonly GafRecord[],\n firstId: number,\n): InputTrack[] {\n const nodeByName = new Map(graph.nodes.map(n => [n.name, n]))\n const reads: InputTrack[] = []\n for (const record of records) {\n const nodes = record.path.map(step => nodeByName.get(step.name))\n const kept = nodes.flatMap((node, i) => (node ? [i] : []))\n const first = kept[0]\n const last = kept.at(-1)\n if (first !== undefined && last !== undefined) {\n const mismatches = mismatchesByStep(\n record,\n nodes.map(n => n?.length),\n )\n const names = kept.map(i => stepName(nodes[i]!, record.path[i]!.strand))\n const lastLength = nodes[last]!.length\n reads.push({\n id: firstId + reads.length,\n name: record.name,\n type: 'read',\n sourceTrackID: 1,\n sequence: names,\n sequenceNew: kept.map((i, k) => ({\n nodeName: names[k]!,\n mismatches: mismatches[i]!,\n })),\n firstNodeOffset: first === 0 ? record.pathStart : 0,\n finalNodeCoverLength:\n last === record.path.length - 1\n ? lastLength - (record.pathLength - record.pathEnd)\n : lastLength,\n mapping_quality:\n record.mappingQuality === 255 ? 0 : record.mappingQuality,\n is_secondary: record.secondary,\n is_reverse: record.strand === '-',\n })\n }\n }\n return reads\n}\n\n// sequenceTubeMap's read palettes: blues forward, reds reverse, staggered by\n// read so neighbours differ\nexport const FORWARD_READ_COLORS = ['#6baed6', '#4292c6', '#2171b5', '#08519c']\nexport const REVERSE_READ_COLORS = ['#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15']\n\nexport function readColor(read: { id: number; is_reverse?: boolean }) {\n const colors = read.is_reverse ? REVERSE_READ_COLORS : FORWARD_READ_COLORS\n return colors[read.id % colors.length]!\n}\n", "import { REFERENCE_RANK, isAnchored } from '../anchoredNodes'\nimport { buildNeighbors } from '../referenceSpan'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph } from '../types'\n\n// An rGFA subgraph's alleles on the reference axis, from the stable\n// coordinates and the L lines alone: an allele is a run of off-reference\n// segments, and the rank-0 segments it detaches from and reattaches to give it\n// a reference span. rGFA carries no W or P lines, so nothing here reads a walk.\n\nexport interface ProjectedAllele {\n // reference bp the allele replaces, from the flanking rank-0 segments\n start: number\n refSpan: number\n // the run's strand-suffixed node ids, ordered by `nodeOffsets`\n nodeIds: string[]\n // bp from the allele's entry to the start of each node, along the run's own\n // edges, so a layout puts a node where it sits within the allele\n nodeOffsets: number[]\n // bp of the longest path through the run, the denominator for nodeOffsets:\n // a bubble's branches are alternatives, so their sum is no assembly's length\n pathLength: number\n}\n\n// PanSN is `sample#haplotype#contig`, but minigraph accepts a bare contig name\n// and the E. coli fixture is `K12#1#chr`, so this parses what is there.\nexport function parsePanSN(refName: string) {\n const parts = refName.split('#')\n const haplotype = parts.length >= 2 ? Number(parts[1]) : Number.NaN\n return {\n sample: parts[0] ?? refName,\n haplotype: Number.isFinite(haplotype) ? haplotype : undefined,\n }\n}\n\n// One connected run of off-reference segments and the rank-0 segments on its\n// boundary. Walked through anchored nodes only: everything reported about a\n// run is read off a stable coordinate.\nfunction collectRun(\n seed: string,\n byId: Map<string, AnchoredNode>,\n adj: Map<string, string[]>,\n claimed: Set<string>,\n) {\n const members: AnchoredNode[] = []\n const anchors: AnchoredNode[] = []\n const queue = [seed]\n claimed.add(seed)\n\n while (queue.length > 0) {\n const id = queue.pop()!\n const node = byId.get(id)\n if (node) {\n members.push(node)\n for (const neighborId of adj.get(id) ?? []) {\n const neighbor = byId.get(neighborId)\n if (neighbor) {\n if (neighbor.stable.rank === REFERENCE_RANK) {\n anchors.push(neighbor)\n } else if (!claimed.has(neighborId)) {\n claimed.add(neighborId)\n queue.push(neighborId)\n }\n }\n }\n }\n }\n return { members, anchors }\n}\n\n// The reference interval an allele replaces: from the end of the last backbone\n// segment before it to the start of the first one after it. Anchors on two\n// stable sequences, or in an order that runs backwards (an inverted or\n// translocated attachment), state no span rather than a negative one.\nfunction anchorSpan(anchors: AnchoredNode[]) {\n const refNames = new Set(anchors.map(a => a.stable.refName))\n if (refNames.size !== 1 || anchors.length < 2) {\n return undefined\n }\n const sorted = [...anchors].sort((a, b) => a.stable.start - b.stable.start)\n const first = sorted[0]!\n const last = sorted.at(-1)!\n const start = first.stable.start + first.length\n const end = last.stable.start\n return end >= start ? { start, end, entry: first } : undefined\n}\n\n// Shortest-path bp from the upstream anchor to each member over the run's own\n// edges, which only orders the run. The edges are undirected (a GFA states a\n// reverse-complement link backwards and single-node mode collapses both\n// orientations), so there is no topological order and a longest-path\n// relaxation would not converge.\nfunction runDistances(\n members: AnchoredNode[],\n entry: AnchoredNode,\n adj: Map<string, string[]>,\n) {\n const byId = new Map(members.map(m => [m.id, m]))\n const dist = new Map<string, number>()\n const queue: string[] = []\n for (const neighborId of adj.get(entry.id) ?? []) {\n if (byId.has(neighborId)) {\n dist.set(neighborId, 0)\n queue.push(neighborId)\n }\n }\n if (queue.length === 0) {\n for (const member of members) {\n dist.set(member.id, 0)\n queue.push(member.id)\n }\n }\n for (const id of queue) {\n const reach = dist.get(id)! + byId.get(id)!.length\n for (const neighborId of adj.get(id) ?? []) {\n if (byId.has(neighborId)) {\n const known = dist.get(neighborId)\n if (known === undefined || reach < known) {\n dist.set(neighborId, reach)\n queue.push(neighborId)\n }\n }\n }\n }\n return dist\n}\n\n// Where each member sits within the allele: ordered by distance from the entry\n// and swept forward, so a node starts where the last of its already-placed\n// neighbours ends. Offsets are then monotone along every edge in the run, which\n// is what keeps a bubble's entry and exit curves from crossing; where the two\n// branches differ in length the short one gets slack rather than the long one a\n// backwards edge.\nfunction runOffsets(\n members: AnchoredNode[],\n entry: AnchoredNode,\n adj: Map<string, string[]>,\n) {\n const dist = runDistances(members, entry, adj)\n const ordered = [...members].sort((a, b) => dist.get(a.id)! - dist.get(b.id)!)\n const rank = new Map(ordered.map((m, i) => [m.id, i]))\n const byId = new Map(members.map(m => [m.id, m]))\n const offsets = new Map<string, number>()\n for (const [i, member] of ordered.entries()) {\n let offset = 0\n for (const neighborId of adj.get(member.id) ?? []) {\n const neighbor = byId.get(neighborId)\n if (neighbor && rank.get(neighborId)! < i) {\n offset = Math.max(offset, offsets.get(neighborId)! + neighbor.length)\n }\n }\n offsets.set(member.id, offset)\n }\n return { offsets, ordered }\n}\n\n// Every allele with a reference span, by start. A run that reaches the edge of\n// the cut has one anchor and no span, which is the ordinary case at a window\n// boundary; placeOffReference chains those off wherever they branch.\nexport function projectAlleles(graph: Graph): ProjectedAllele[] {\n const byId = new Map(\n graph.nodes.filter(isAnchored).map(n => [n.id, n] as const),\n )\n const adj = buildNeighbors(graph)\n const claimed = new Set<string>()\n const alleles: ProjectedAllele[] = []\n\n for (const node of byId.values()) {\n if (node.stable.rank !== REFERENCE_RANK && !claimed.has(node.id)) {\n const { members, anchors } = collectRun(node.id, byId, adj, claimed)\n const span = anchorSpan(anchors)\n if (span) {\n const { offsets, ordered } = runOffsets(members, span.entry, adj)\n alleles.push({\n start: span.start,\n refSpan: span.end - span.start,\n nodeIds: ordered.map(m => m.id),\n nodeOffsets: ordered.map(m => offsets.get(m.id)!),\n pathLength: ordered.reduce(\n (max, m) => Math.max(max, offsets.get(m.id)! + m.length),\n 0,\n ),\n })\n }\n }\n }\n\n return alleles.sort((a, b) => a.start - b.start)\n}\n", "import { projectAlleles } from '../alleleProjection/projectAlleles'\nimport { buildNeighbors } from '../referenceSpan'\n\nimport type { AlleleDeletion, Graph, NodeSegment } from '../types'\n\n// Where off-reference segments go on a reference x axis. Shared by the two\n// row-structured layouts, which differ only in what a row means (stable rank\n// vs contributing assembly); how far along the reference a segment reaches is\n// the same question in both, and answering it twice is how they drifted.\n//\n// The rule: a segment occupies the reference its allele replaces, never its own\n// sequence length. x is reference bp \u2014 the axis the backbone declares and the\n// linear view beside it is drawn on \u2014 so an insertion, which consumes no\n// reference, must not advance along it. Both layouts used to advance by\n// sequence length, which made every off-reference x a false statement about\n// reference position and, on a 1 kb window holding a 100 kb allele, drew 101x\n// the window so that zoom-to-fit rendered the reference at 1% of the frame.\n//\n// Magnitude is the cost. An axis of reference coordinates has no way to state\n// how much sequence an insertion adds, so it is not stated here: it is in the\n// node tooltip, and drawn to scale in the allele-inventory track, where a CIGAR\n// gives the alignments display a channel for length that is not the x axis.\n\n// Reference bp an allele has to skip before the drawing calls it a deletion:\n// the 1 bp floor `deletionEdges` uses, a guard against abutting anchors' zero\n// gap rather than a biological threshold. Whether a small one is drawn is\n// decided by its label's fit in graphLabels.\nconst MIN_DELETION_BP = 1\n\n// Floor on an off-reference node's drawn span, as a fraction of the window.\n// Node thickness is a constant number of screen pixels, so a segment on a\n// sub-percent slice of the window draws wider than it is long and reads as a\n// dot on a stalk, and a pure insertion occupies no reference at all. Rank-0\n// nodes keep their exact offsets, so the reference axis is untouched.\nconst MIN_ALLELE_SPAN_FRACTION = 0.015\n\ninterface PlaceArgs {\n graph: Graph\n // the reference interval the drawing is scaled against (referenceSpan)\n span: number\n // y for a segment, which is the whole of what the two layouts disagree about\n rowY: (node: Graph['nodes'][number]) => number\n // the backbone, already placed at its declared offsets. Mutated in place, and\n // read for the branch points the unanchored walk below starts from.\n positions: Record<string, NodeSegment[]>\n}\n\nexport function placeOffReference({\n graph,\n span,\n rowY,\n positions,\n}: PlaceArgs): AlleleDeletion[] {\n const minSpan = span * MIN_ALLELE_SPAN_FRACTION\n const alleles = projectAlleles(graph)\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n // Alleles standing in for more reference than they carry. Collected here\n // rather than derived by the label pass, because the reason they need saying\n // is the placement done in this function: a run drawn over the reference it\n // replaces has a width that is not its length, and nothing downstream can\n // tell that from a node drawn at its own scale. Same measure the deletion\n // arcs use \u2014 reference bp a haplotype does not carry \u2014 so the two kinds of\n // deletion in a drawing are stated in the same units and the same words.\n const alleleDeletions: AlleleDeletion[] = []\n\n for (const allele of alleles) {\n const skipped = allele.refSpan - allele.pathLength\n if (skipped >= MIN_DELETION_BP) {\n alleleDeletions.push({ nodeIds: allele.nodeIds, bp: skipped })\n }\n const drawn = Math.max(allele.refSpan, minSpan)\n // A node sits where it sits *within* the allele: its bp offset from the\n // entry, scaled so the run's longest path fills the reference the allele\n // replaces. So a bubble path occupies the reference exactly once however\n // many segments it is built from, and two parallel branches share the x\n // range rather than queue up after each other \u2014 they are alternatives over\n // the same interval, not consecutive sequence.\n //\n // Offsets are measured over the graph rather than over nodeIds' order (see\n // runOffsets). Concatenating in traversal order put graph neighbours 26 kb\n // apart on a 27 kb allele, and the edges spanning those gaps crossed as a\n // long X. A run of all zero-length segments has no distances to scale by;\n // splitting evenly beats 0/0, which wrote NaN into every position after it.\n //\n // Where the floor widens a run past the reference it replaces, the overhang\n // hangs off the right, so the run can end past the rank-0 segment it\n // rejoins and its exit edge runs backwards. That is handled where it shows,\n // in computeEdgeCurves, which sizes each control point by how far the other\n // endpoint lies along its own tangent \u2014 a backwards edge gets a straight\n // leader rather than the loop that read as a crossed bowtie.\n const scale = allele.pathLength > 0 ? drawn / allele.pathLength : 0\n allele.nodeIds.forEach((nodeId, i) => {\n const node = byId.get(nodeId)\n if (node) {\n const offset = allele.nodeOffsets[i]!\n const even = drawn / allele.nodeIds.length\n // both ends from the offset rather than start-plus-width, so two nodes\n // the sweep placed end to end land on exactly the same x instead of on\n // two roundings of it\n const at = (bp: number) => allele.start + bp * scale\n const y = rowY(node)\n positions[nodeId] = scale\n ? [\n { x: at(offset), y },\n { x: at(offset + node.length), y },\n ]\n : [\n { x: allele.start + i * even, y },\n { x: allele.start + (i + 1) * even, y },\n ]\n }\n })\n }\n\n // A run that reaches the edge of the fetched subgraph has only one backbone\n // anchor, so no reference span can be stated for it. That is the normal case\n // at a window boundary, not an error, and dropping it is indistinguishable\n // from the layout being broken \u2014 the sample-rows layout did drop them.\n //\n // They get the floor each, chained from wherever they branch: the only honest\n // claim available is \"attaches here and continues past the edge\", and a floor\n // keeps a chain of them bounded by the window instead of by their sequence.\n // The same undirected adjacency the reference walk builds, from the one\n // function that builds it. The copy here rebuilt each list with a spread per\n // edge, which is quadratic in a node's degree and is the shape a collapsed\n // repeat's hub node is worst at.\n const neighbors = buildNeighbors(graph)\n // Read forward while appending, so this is a BFS with an O(1) dequeue rather\n // than a quadratic shift() over what ends up being every node in the run.\n const queue = Object.keys(positions)\n for (const fromId of queue) {\n const fromEnd = positions[fromId]!.at(-1)!.x\n for (const nextId of neighbors.get(fromId) ?? []) {\n const next = byId.get(nextId)\n if (next && !(nextId in positions)) {\n const y = rowY(next)\n positions[nextId] = [\n { x: fromEnd, y },\n { x: fromEnd + minSpan, y },\n ]\n queue.push(nextId)\n }\n }\n }\n\n return alleleDeletions\n}\n", "import { placeOffReference } from './placeOffReference'\nimport { ROW_HEIGHT_PX } from './rowSpacing'\nimport {\n backboneNodes,\n backbonePositions,\n referenceSpan,\n} from '../anchoredNodes'\n\nimport type { Graph, LayoutResult, RowLabel } from '../types'\n\n// Layout for rGFA, where the graph states its own backbone instead of leaving a\n// force simulation to find one. Every segment carries SN/SO/SR (gfatools\n// doc/rGFA.md), so both axes come from the file:\n//\n// x reference bp, for every segment. Rank-0 segments sit at the offset they\n// declare; an off-reference segment cannot use its own SO, which is an\n// offset on a different stable sequence, so it takes the slice of the\n// reference its allele replaces (placeOffReference).\n// y one row per stable rank *present in this subgraph*, the way lh3's own\n// rGFA viewer (VRPG) does it: rank 0 is the reference line, higher ranks\n// below it in order. In SCREEN PIXELS, not bp \u2014 see rowSpacing.ts, shared\n// with the sample-row layout.\n\n// Rank is a property of the whole graph, not of the window being drawn: HPRC's\n// minigraph graph ranks up to 89, but an MHC window holds only ranks\n// 0/1/3/6/14/23. Indexing rows by raw rank would leave 17 of 24 rows empty and\n// zoom-to-fit would shrink the drawing to fit that void (measured: 0.3% scale).\n// So rows are the ranks actually present, in rank order \u2014 identical to the raw\n// rank whenever the window happens to hold a contiguous run from 0.\nfunction rankRows(graph: Graph) {\n const present = new Set<number>()\n for (const node of graph.nodes) {\n if (node.stable) {\n present.add(node.stable.rank)\n }\n }\n return new Map([...present].sort((a, b) => a - b).map((r, i) => [r, i]))\n}\n\nexport function anchoredLayout(\n graph: Graph,\n region?: { start: number; end: number },\n): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n if (backbone.length === 0) {\n return undefined\n }\n\n const span = referenceSpan(backbone, region)\n const rows = rankRows(graph)\n\n // Row 0 by construction: a backbone exists, so rank 0 is present, and\n // `rankRows` numbers the present ranks in ascending order.\n const nodePositions = backbonePositions(backbone)\n\n const alleleDeletions = placeOffReference({\n graph,\n span,\n rowY: node =>\n (node.stable ? (rows.get(node.stable.rank) ?? rows.size) : rows.size) *\n ROW_HEIGHT_PX,\n positions: nodePositions,\n })\n\n // Named from the same `rows` map that placed every node, so the axis and the\n // drawing cannot disagree. Rank 0 is spelled out: \"the reference\" is what a\n // reader needs, and \"Rank 0\" alone reads as an ordinal with no meaning.\n const rowLabels: RowLabel[] = [...rows].map(([rank, row]) => ({\n label: rank === 0 ? 'Reference (rank 0)' : `Rank ${rank}`,\n y: row * ROW_HEIGHT_PX,\n }))\n\n return {\n nodePositions,\n rowLabels,\n referenceAxis: true,\n pixelRows: true,\n alleleDeletions,\n }\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { isBackbone } from '../anchoredNodes'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph, GraphNode, LayoutResult, NodeSegment } from '../types'\n\n// Reference-ordered layered layout: x is reference ORDER, not bp. Backbone\n// segments take increasing layers by stable offset, every other node the layer\n// between the anchors it hangs off, and a node is as wide as the log of its bp,\n// so a SNP allele gets the same room as a 10 kb segment and a bubble reads as a\n// lens rather than a bar under a line. y is a lane in screen px with the\n// reference pinned at lane 0 (ADR-042).\n\nconst GUTTER = 12\nconst LANE_PX = ROW_HEIGHT_PX\n\nfunction nodeWidth(node: GraphNode) {\n return 10 + 8 * Math.log2(1 + node.length)\n}\n\nfunction compare<T extends string | number>(a: T, b: T) {\n return a < b ? -1 : a > b ? 1 : 0\n}\n\n// Where a node sorts: `at` is the rank of a placed node, the backbone and what\n// the walks visit, and a node the search claims sorts before or after the one\n// that claimed it, by its steps from it. A tuple rather than an offset nudged\n// per step: on a base-level graph the backbone nodes are a bp apart, and a\n// chain a few nodes long nudged its keys past the ones that follow.\ninterface OrderKey {\n at: number\n side: -1 | 0 | 1\n hops: number\n}\n\nfunction compareKeys(a: OrderKey, b: OrderKey) {\n return (\n compare(a.at, b.at) || compare(a.side, b.side) || compare(a.hops, b.hops)\n )\n}\n\nconst UNREACHED: OrderKey = { at: Infinity, side: 0, hops: 0 }\n\n// Every node id in reference order: backbone by stable offset, every other node\n// beside a backbone node (orderKeys), nodes nothing reaches last. Name breaks\n// ties, so the order is a strict total order and the same graph always gets\n// the same one.\nexport function referenceOrder(graph: Graph) {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const key = orderKeys(graph)\n return graph.nodes\n .map(n => n.id)\n .sort(\n (a, b) =>\n compareKeys(key.get(a) ?? UNREACHED, key.get(b) ?? UNREACHED) ||\n compare(byId.get(a)!.name, byId.get(b)!.name) ||\n compare(a, b),\n )\n}\n\n// A walk states where its nodes lie, which the links alone do not. Starting\n// from the backbone in offset order, each node a walk visits for the first time\n// goes into the order immediately after the node that walk came from, and the\n// nodes a walk visits before it meets a placed one go immediately before it.\n//\n// That keeps every true boundary. A walk's step joins two nodes on the same\n// side of a node every haplotype crosses once, unless one of them is that\n// node, so by induction the nodes between two such boundaries stay one\n// unbroken stretch of the order and no link among them reaches across. On the\n// eight-haplotype KIV-2 cut (15,808 nodes) the walks imply 28 bubbles and this\n// derives those 28, with 5 of 65,776 walk steps against the order. Placing\n// every node by the links alone derived 21, with 23,308 steps against it.\n//\n// A walk on the reverse strand is read end-first.\nfunction placeByWalks(graph: Graph, backbone: AnchoredNode[]) {\n const isNode = new Set(graph.nodes.map(n => n.id))\n const startOf = new Map(backbone.map(n => [n.id, n.stable.start]))\n const next = new Map<string, string | undefined>()\n const prev = new Map<string, string | undefined>()\n backbone.forEach((n, i) => {\n prev.set(n.id, backbone[i - 1]?.id)\n next.set(n.id, backbone[i + 1]?.id)\n })\n let head = backbone[0]?.id\n const insertAfter = (at: string, id: string) => {\n const after = next.get(at)\n next.set(at, id)\n prev.set(id, at)\n next.set(id, after)\n if (after !== undefined) {\n prev.set(after, id)\n }\n }\n const insertBefore = (at: string, id: string) => {\n const before = prev.get(at)\n if (before === undefined) {\n head = id\n prev.set(id, undefined)\n next.set(id, at)\n prev.set(at, id)\n } else {\n insertAfter(before, id)\n }\n }\n\n for (const path of graph.paths ?? []) {\n let rising = 0\n let falling = 0\n let last: number | undefined\n for (const id of path.nodeIds) {\n const start = startOf.get(id)\n if (start !== undefined) {\n if (last !== undefined && start !== last) {\n if (start > last) {\n rising++\n } else {\n falling++\n }\n }\n last = start\n }\n }\n const ids = falling > rising ? [...path.nodeIds].reverse() : path.nodeIds\n let from: string | undefined\n const leading = new Set<string>()\n for (const id of ids) {\n if (next.has(id)) {\n if (from === undefined) {\n for (const lead of leading) {\n insertBefore(id, lead)\n }\n }\n from = id\n } else if (isNode.has(id)) {\n if (from === undefined) {\n leading.add(id)\n } else {\n insertAfter(from, id)\n from = id\n }\n }\n }\n }\n\n const placed: string[] = []\n for (let id = head; id !== undefined; id = next.get(id)) {\n placed.push(id)\n }\n return placed\n}\n\n// The nodes no walk visits, which is every off-reference node of a graph with\n// no walks: each goes beside the nearest placed node that reaches it (BFS over\n// the links as undirected, seeded in order so a tie goes left).\n//\n// That search meets itself in the middle of an allele, so the far half of a\n// chain A>X1>X2>B is claimed from B, its RIGHT anchor. Placed after B, as every\n// claimed node once was, X2 sorted past the node it leads into, its link was\n// turned round, and the insertion stopped being a bubble. So a run of nodes\n// claimed from one placed node lies BEFORE it, farthest node first, when it is\n// such a far half: it touches nothing right of that node, and every dead end of\n// the search inside it is a place the search from the left arrived. Steps are\n// a position only along a chain. In a tangle the farthest node is a dead end\n// with nothing before it, and sorted first its links to its real neighbours\n// sweep across every boundary between, so any other run stays after.\nfunction placeBySearch(\n adjacent: Map<string, string[]>,\n key: Map<string, OrderKey>,\n) {\n const queue = [...key.entries()]\n .sort(([a, ka], [b, kb]) => compareKeys(ka, kb) || compare(a, b))\n .map(([id]) => id)\n const placed = queue.length\n // the queue grows while the loop walks it, which for-of follows\n for (const id of queue) {\n const from = key.get(id)!\n for (const nb of adjacent.get(id) ?? []) {\n if (!key.has(nb)) {\n key.set(nb, { at: from.at, side: 1, hops: from.hops + 1 })\n queue.push(nb)\n }\n }\n }\n\n const searched = new Set(queue.slice(placed))\n const sided = new Set<string>()\n for (const seed of searched) {\n if (sided.has(seed)) {\n continue\n }\n const { at } = key.get(seed)!\n const run = [seed]\n sided.add(seed)\n let farHalf = true\n for (const id of run) {\n const { hops } = key.get(id)!\n let deadEnd = true\n let touchesLeft = false\n for (const nb of adjacent.get(id) ?? []) {\n const other = key.get(nb)!\n if (other.at === at && searched.has(nb)) {\n deadEnd &&= other.hops <= hops\n if (!sided.has(nb)) {\n sided.add(nb)\n run.push(nb)\n }\n } else if (other.at !== at) {\n touchesLeft ||= other.at < at\n farHalf &&= other.at < at\n }\n }\n farHalf &&= !deadEnd || touchesLeft\n }\n if (farHalf) {\n for (const id of run) {\n const k = key.get(id)!\n key.set(id, { ...k, side: -1, hops: -k.hops })\n }\n }\n }\n}\n\nfunction orderKeys(graph: Graph) {\n const ids = new Set(graph.nodes.map(n => n.id))\n const adjacent = new Map<string, string[]>()\n for (const e of graph.edges) {\n if (!ids.has(e.from) || !ids.has(e.to)) {\n continue\n }\n ;(adjacent.get(e.from) ?? adjacent.set(e.from, []).get(e.from)!).push(e.to)\n ;(adjacent.get(e.to) ?? adjacent.set(e.to, []).get(e.to)!).push(e.from)\n }\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort(\n (a, b) =>\n compare(a.stable.start, b.stable.start) || compare(a.name, b.name),\n )\n const key = new Map<string, OrderKey>()\n placeByWalks(graph, backbone).forEach((id, rank) => {\n key.set(id, { at: rank, side: 0, hops: 0 })\n })\n placeBySearch(adjacent, key)\n return key\n}\n\nexport interface LayeredGraph {\n layers: string[][]\n layerOf: Map<string, number>\n // predecessors in layer order, for the y sweep\n preds: Map<string, string[]>\n}\n\n// The layered DAG the ordered layout draws and the bubble decomposition reads\n// (bubbles/bubblesFromGraph.ts), from one function so the two cannot disagree\n// about which nodes share a layer.\nexport function layerGraph(graph: Graph): LayeredGraph {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const order = referenceOrder(graph)\n const key = new Map(order.map((id, i) => [id, i]))\n const isRef = (id: string) => isBackbone(byId.get(id)!)\n\n // Every edge directed from lower reference key to higher, so the graph is\n // acyclic by construction and no cycle-removal heuristic can misplace the\n // reference.\n const preds = new Map<string, string[]>(order.map(id => [id, []]))\n const succs = new Map<string, string[]>(order.map(id => [id, []]))\n const link = (a: string, b: string) => {\n preds.get(b)!.push(a)\n succs.get(a)!.push(b)\n }\n for (const e of graph.edges) {\n if (e.from === e.to || !key.has(e.from) || !key.has(e.to)) {\n continue\n }\n if (key.get(e.from)! < key.get(e.to)!) {\n link(e.from, e.to)\n } else {\n link(e.to, e.from)\n }\n }\n // The backbone chained by virtual edges, so the reference stays monotone even\n // where adjacent segments are not linked inside the cut.\n const backbone = order.filter(isRef)\n for (let i = 1; i < backbone.length; i++) {\n link(backbone[i - 1]!, backbone[i]!)\n }\n\n // Longest-path layering; the key order is a topological order of this DAG.\n const layerOf = new Map<string, number>()\n for (const id of order) {\n let l = 0\n for (const p of preds.get(id)!) {\n l = Math.max(l, layerOf.get(p)! + 1)\n }\n layerOf.set(id, l)\n }\n // Then each allele slides right to centre between the layer its predecessors\n // force and the one its successors allow, so a short allele sits in the\n // middle of its bubble rather than hugging the left anchor.\n for (let i = order.length - 1; i >= 0; i--) {\n const id = order[i]!\n const s = succs.get(id)!\n if (isRef(id) || s.length === 0) {\n continue\n }\n let minSucc = Infinity\n for (const t of s) {\n minSucc = Math.min(minSucc, layerOf.get(t)!)\n }\n const lo = layerOf.get(id)!\n const hi = Math.max(lo, minSucc - 1)\n layerOf.set(id, Math.floor((lo + hi) / 2))\n }\n\n const layers: string[][] = []\n for (const id of order) {\n ;(layers[layerOf.get(id)!] ??= []).push(id)\n }\n return { layers, layerOf, preds }\n}\n\nexport function orderedLayout(graph: Graph): LayoutResult | undefined {\n if (!graph.nodes.some(isBackbone)) {\n return undefined\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const isRef = (id: string) => isBackbone(byId.get(id)!)\n const { layers, layerOf: layer, preds } = layerGraph(graph)\n\n const layerWidth = layers.map(ids => {\n let w = 0\n for (const id of ids) {\n w = Math.max(w, nodeWidth(byId.get(id)!))\n }\n return w\n })\n const layerX: number[] = []\n let x = 0\n for (let l = 0; l < layers.length; l++) {\n layerX[l] = x\n x += layerWidth[l]! + GUTTER\n }\n\n // y: sweep the layers left to right, each allele at the barycenter of its\n // placed predecessors, then at the nearest FREE lane to it. Lane 0 is the\n // reference line and stays reserved even in a layer with no backbone node,\n // or an allele between two backbone segments reads as reference.\n const y = new Map<string, number>()\n for (const ids of layers) {\n const free: { id: string; ideal: number }[] = []\n for (const id of ids) {\n if (isRef(id)) {\n y.set(id, 0)\n continue\n }\n const placed = preds.get(id)!.filter(p => y.has(p))\n let ideal = 0\n for (const p of placed) {\n ideal += y.get(p)! / placed.length\n }\n // a node straight off the backbone wants to leave it, not sit on it\n free.push({ id, ideal: ideal === 0 ? LANE_PX : ideal })\n }\n free.sort((a, b) => a.ideal - b.ideal)\n const taken = new Set([0])\n for (const { id, ideal } of free) {\n let lane = Math.round(ideal / LANE_PX)\n if (lane === 0) {\n lane = ideal >= 0 ? 1 : -1\n }\n for (let d = 1; taken.has(lane); d++) {\n if (!taken.has(lane + d)) {\n lane += d\n } else if (!taken.has(lane - d)) {\n lane -= d\n }\n }\n taken.add(lane)\n y.set(id, lane * LANE_PX)\n }\n }\n\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const n of graph.nodes) {\n const l = layer.get(n.id)!\n const w = nodeWidth(n)\n const cx = layerX[l]! + layerWidth[l]! / 2\n const ny = y.get(n.id)!\n nodePositions[n.id] = [\n { x: cx - w / 2, y: ny },\n { x: cx + w / 2, y: ny },\n ]\n }\n return { nodePositions, referenceAxis: false, pixelRows: true }\n}\n", "import { placeOffReference } from './placeOffReference'\nimport { ROW_HEIGHT_PX } from './rowSpacing'\nimport { parsePanSN } from '../alleleProjection/projectAlleles'\nimport {\n backboneNodes,\n backbonePositions,\n isOffReference,\n referenceSpan,\n} from '../anchoredNodes'\n\nimport type { Graph, LayoutResult, RowLabel } from '../types'\n\n// One row per contributing assembly, x on the reference.\n//\n// The anchored layout rows by *rank*, which is a property of the graph's\n// construction order rather than of any sample: at an HPRC locus rank 1 holds\n// alleles from dozens of different haplotypes, so a row there means nothing\n// biological. This layout rows by the assembly each allele came from, so a row\n// is one sample and reading across it says what that sample does to the\n// reference: a deletion leaves the row empty across the span it removes, and an\n// insertion is a mark at the point it attaches. Insertion *size* is deliberately\n// not on this axis \u2014 see placeOffReference.\n//\n// Positions come from `projectAlleles`, which derives them from the stable\n// coordinates and the L-lines alone. rGFA has no W/P records to consult, so\n// anything requiring walks could not run on the format the graph tracks read.\n//\n// What a row means differs with where the coordinates came from, and the\n// difference is not cosmetic. On rGFA a segment's SN names the assembly that\n// *first contributed* it (SR is build order), so a row is first-seen\n// attribution. On a path-anchored graph it names an assembly that actually\n// carries the segment. Carriage is a set, though, and a row here is one node's\n// one position: where several assemblies carry a segment, it draws on the row\n// of the first path in the file that visits it, and `GraphNode.samples` holds\n// the rest. Drawing a segment once per carrier needs the layout to emit more\n// nodes than the graph has, which the renderer keys by node id and cannot do.\n\n// Every assembly with a segment in this subgraph, ordered by how much\n// off-reference sequence it contributes here, most first. Taken from the nodes\n// rather than from the projection's per-allele attribution: a bubble path can\n// mix assemblies, and a segment belongs on the row of the assembly its own\n// stable name gives it, not on the row of whichever contributor happens to\n// dominate the run it sits in.\n//\n// Read off the same field `rowY` places by, so every row that exists has nodes\n// on it and every node lands on a row that exists.\n//\n// **Sorted by content rather than by name**, which is the change and the\n// tradeoff. Alphabetical put `HG00099` above `HG00280` for no reason a reader\n// could see, so a row's neighbours said nothing and the stack had to be read one\n// row at a time. By contributed sequence it reads the way a sorted pileup does:\n// the haplotypes doing the most to the reference are together at the top, and\n// how far down the stack the marks stop IS how many samples carry anything.\n//\n// What that costs is comparing one window against another \u2014 the order is a fact\n// about the window, so a sample is not in the same place in two of them. That is\n// the case \u00A78's \"a row set cannot be requested\" is for, and an explicit sample\n// list is the right answer to it rather than an order nobody can read. A\n// follow's re-cut is the exception it can answer already: `keep` is the rows on\n// screen, those samples hold their order, and a sample new to the window goes\n// below them by content.\n//\n// Name breaks ties, so the order is still deterministic and a pan that changes\n// nothing about the content reshuffles nothing.\nfunction contributingSamples(graph: Graph, keep: readonly string[]) {\n const carried = new Map<string, number>()\n for (const node of graph.nodes) {\n if (isOffReference(node)) {\n const { sample } = parsePanSN(node.stable.refName)\n carried.set(sample, (carried.get(sample) ?? 0) + node.length)\n }\n }\n const held = new Map(keep.map((sample, row) => [sample, row]))\n return [...carried.keys()].sort(\n (a, b) =>\n (held.get(a) ?? Infinity) - (held.get(b) ?? Infinity) ||\n carried.get(b)! - carried.get(a)! ||\n a.localeCompare(b),\n )\n}\n\nexport function sampleRowLayout(\n graph: Graph,\n region?: { start: number; end: number },\n keep: readonly string[] = [],\n): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n const samples = contributingSamples(graph, keep)\n if (backbone.length === 0 || samples.length === 0) {\n return undefined\n }\n const span = referenceSpan(backbone, region)\n\n // The backbone keeps row 0 and every sample gets a row below it, in the\n // order contributingSamples gives.\n const rowOf = new Map(samples.map((s, i) => [s, i + 1]))\n const nodePositions = backbonePositions(backbone)\n\n // `placeOffReference` also reaches nodes with no coordinate at all: it walks\n // out from what it placed to close a run that leaves the fetched window, and\n // on a path-anchored graph a segment no P or W line visits never got a\n // `stable` to begin with. That is legal GFA rather than a broken file, so\n // asserting the coordinate here threw a bare \"cannot read properties of\n // undefined\" out of the layout and left the view showing it instead of a\n // graph. Such a node belongs on no sample's row, so it takes the spare row\n // below the named ones, which is what the sibling layout does with an\n // unrecognised rank.\n const spareRow = samples.length + 1\n const alleleDeletions = placeOffReference({\n graph,\n span,\n rowY: node => {\n const stable = node.stable\n const row = stable\n ? rowOf.get(parsePanSN(stable.refName).sample)\n : undefined\n return (row ?? spareRow) * ROW_HEIGHT_PX\n },\n positions: nodePositions,\n })\n\n // Built from the same `rowOf` map that placed the alleles, so a label cannot\n // name a row the layout put somewhere else. Row 0 is the backbone, named for\n // the assembly it comes from rather than \"reference\", because on this layout\n // that name is one of the samples and reads as a peer of the rows below it.\n const referenceSample = parsePanSN(backbone[0]!.stable.refName).sample\n const rowLabels: RowLabel[] = [\n { label: referenceSample, y: 0 },\n ...samples.map(sample => ({\n label: sample,\n y: rowOf.get(sample)! * ROW_HEIGHT_PX,\n })),\n ]\n\n return {\n nodePositions,\n rowLabels,\n sampleRows: samples,\n referenceAxis: true,\n pixelRows: true,\n alleleDeletions,\n }\n}\n", "export function emptyTrackShapes() {\n return {\n rectangles: [],\n curves: [],\n corners: [],\n verticalRectangles: [],\n featureRectangles: [],\n };\n}\n", "// The tube map layout, from input nodes and tracks to drawable shapes. Ported\n// from the original sequenceTubeMap JS, where it shared one module with the d3\n// drawing; the passes below still share module state, but layoutTubeMap resets\n// all of it on entry and runs synchronously, so no two calls see each other's.\nimport { emptyTrackShapes } from './types.js';\nconst DEBUG = false;\nconst DEFAULT_TRACK_COLORS = [\n '#1f77b4',\n '#ff7f0e',\n '#2ca02c',\n '#d62728',\n '#9467bd',\n '#8c564b',\n '#e377c2',\n '#7f7f7f',\n];\nfunction configFrom(options) {\n return {\n mergeNodesFlag: options.mergeNodes ?? true,\n showReads: options.showReads ?? true,\n coarsenedReadView: options.coarsenedReadView ?? false,\n ignoreStrand: options.ignoreStrand ?? false,\n nodeWidthOption: options.nodeWidthOption ?? 'normal',\n charWidth: options.charWidth ?? 8.401,\n trackWidth: options.trackWidth ?? 15,\n mappingQualityCutoff: options.mappingQualityCutoff ?? 0,\n focusReadNames: options.focusReadNames ?? null,\n showExonsFlag: options.showExons ?? false,\n trackColor: options.trackColor ??\n (track => DEFAULT_TRACK_COLORS[track.id % DEFAULT_TRACK_COLORS.length]),\n trackAlpha: options.trackAlpha ?? (() => 1),\n };\n}\nlet config = configFrom({});\nlet bed = null;\nlet nodes = [];\n// Each track has a `path`, which is an array of Segment objects describing pieces of the path that need to be drawn, in order along the path.\nlet tracks = [];\n// Each read also has a `path` list of Segments, but reads are organized vertically using a different system than non-read tracks.\nlet reads = [];\nlet nodeMap = new Map();\nlet nodesPerOrder = [];\n// Scratch array used only during generateNodeOrder. Indexed by node index;\n// undefined = \"not yet assigned.\" Copied into node.order at end of layout.\nlet nodeOrders = [];\n// Lane assignment info for tracks, in one list per horizontal \"order\" slot.\n// Duplicates info in tracks' `path` lists but is organized by order. Reads do not use this.\nlet assignments = [];\nlet extraLeft = [];\nlet extraRight = [];\nlet maxOrder = -1; // horizontal order of the rightmost node\nlet shapes = emptyTrackShapes();\nlet imageBounds = { minX: 0, maxX: 0, minY: 0, maxY: 0 };\nlet trackForRuler;\nlet coarsenedEdgeMeta = new Map();\n// Lay out `inputNodes` and `inputTracks` (and `inputReads`, when\n// `showReads`), or return undefined when nothing visible is left to draw.\n// Neither input is modified. Track 0 is the reference the layout straightens.\nexport function layoutTubeMap(inputNodes, inputTracks, inputReads = [], options = {}) {\n config = configFrom(options);\n bed = options.bed ?? null;\n shapes = emptyTrackShapes();\n assignments = [];\n extraLeft = [];\n extraRight = [];\n nodesPerOrder = [];\n imageBounds = { minX: 0, maxX: 0, minY: 0, maxY: 0 };\n trackForRuler = undefined;\n coarsenedEdgeMeta = new Map();\n if (inputNodes.length === 0 || inputTracks.length === 0) {\n return undefined;\n }\n // Boundary promotion: layout passes below populate the Node/Track fields\n // (width, order, x/y, path, indexSequence, etc.) before any reader runs.\n const copies = deepCopy(inputNodes);\n // Nodes are referenced in inputs by internal `name` attribute and not by\n // index. Internally in e.g. a path's indexSequence we need to reference\n // nodes by *signed* index, so index 0 can never be used: budge everything\n // down and leave a hole (rather than an `undefined` entry) at 0, which we\n // won't iterate over. Made after the copy, because not every structuredClone\n // keeps a hole.\n nodes = [];\n copies.forEach((node, i) => {\n nodes[i + 1] = node;\n });\n tracks = deepCopy(inputTracks);\n // Whether any reads were loaded at all, distinct from `reads.length` below:\n // a mapping-quality cutoff or focus-name filter can filter every read out,\n // and that should not make the coarsened view fall back to bunching\n // haplotypes instead \u2014 the graph does have reads, they're just all hidden.\n const hadInputReads = inputReads.length > 0;\n // Drop the reads we will never draw before cloning them \u2014 the deep copy of a\n // large GAM is the single most expensive step in a redraw.\n reads = deepCopy(filterReads(inputReads));\n for (let i = tracks.length - 1; i >= 0; i -= 1) {\n const t = tracks[i];\n if (t.type === undefined) {\n t.type = 'haplotype';\n }\n if (t.hidden === true) {\n tracks.splice(i, 1);\n continue;\n }\n if (t.indexOfFirstBase !== undefined) {\n trackForRuler = t.name;\n }\n }\n if (tracks.length === 0) {\n return undefined;\n }\n // Run against the visible tracks only, so hiding the reference doesn't leave\n // the layout straightened around a track that is no longer drawn.\n straightenTrack(0);\n nodeMap = generateNodeMap();\n generateTrackIndexSequences(tracks);\n if (config.showReads && reads.length > 0)\n generateTrackIndexSequences(reads);\n generateNodeWidth();\n if (config.mergeNodesFlag) {\n generateNodeSuccessors();\n generateNodeOrder();\n if (config.showReads && reads.length > 0)\n reverseReversedReads();\n mergeNodes();\n nodeMap = generateNodeMap();\n generateNodeWidth();\n generateTrackIndexSequences(tracks);\n if (config.showReads && reads.length > 0)\n generateTrackIndexSequences(reads);\n }\n generateNodeSuccessors();\n generateNodeDegree();\n if (DEBUG)\n console.log(`${nodes.length} nodes.`);\n generateNodeOrder();\n maxOrder = getMaxOrder();\n // can cause problems when there is a reversed single track node\n // OTOH, can solve problems with complex inversion patterns\n switchNodeOrientation();\n generateNodeOrder();\n maxOrder = getMaxOrder();\n // Coarsened (Sankey) mode normally collapses the *read* list into synthetic\n // per-edge bands (below). A haplotype-only graph has no reads to coarsen,\n // so a coarsened request there instead coarsens every haplotype but the\n // reference: that one keeps its normal per-track lane (so it still carries\n // the ruler) while the rest are pulled out of `tracks` here, before\n // calculateTrackWidth/generateLaneAssignment ever see them, and rejoin the\n // layout below through the same reads-style overlay used for coarsened\n // reads.\n const coarsenHaplotypes = config.showReads && config.coarsenedReadView && !hadInputReads;\n if (coarsenHaplotypes) {\n const rulerIndex = trackForRuler === undefined\n ? -1\n : tracks.findIndex(t => t.name === trackForRuler);\n const refIndex = rulerIndex === -1 ? 0 : rulerIndex;\n const altHaplotypes = tracks.filter((_, i) => i !== refIndex);\n if (altHaplotypes.length > 0) {\n const bands = buildCoarsenedSyntheticBands(altHaplotypes, 'haplotype');\n // A haplotype that only ever visits one node produces no edge, so an\n // all-single-node set of alts would otherwise leave `reads` empty and\n // fall through to the no-overlay branch below, which never gives their\n // nodes a y/contentHeight. Keep them off to the side instead of\n // dropping them silently.\n if (bands.length > 0) {\n tracks = [tracks[refIndex]];\n reads = bands;\n }\n }\n }\n calculateTrackWidth();\n generateLaneAssignment();\n if (config.showExonsFlag && bed !== null)\n addTrackFeatures();\n // Coarsened (Sankey) mode: collapse the read list (or, when coarsening\n // haplotypes, the alt haplotypes pulled out above) to one synthetic \"read\"\n // per (srcSigned \u2192 dstSigned) edge BEFORE the normal read placement runs.\n // Each synthetic read traverses exactly two nodes, so the rest of the\n // pipeline (placeReads, generateSVGShapesFromPath, curve drawing) handles\n // it like any normal read: it gets a lane, picks up the \"right-down-left-\n // up-right\" topology for loops automatically, and uses the same elegant\n // bezier. Node heights end up proportional to *edge* count (typically\n // tens) rather than *read* or *haplotype* count (potentially thousands).\n const drawCoarsenedReads = config.showReads &&\n config.coarsenedReadView &&\n reads.length > 0 &&\n !coarsenHaplotypes;\n if (config.showReads && reads.length > 0) {\n generateReadOnlyNodeAttributes();\n reverseReversedReads();\n generateTrackIndexSequences(reads);\n if (drawCoarsenedReads) {\n reads = buildCoarsenedSyntheticBands(reads, 'read');\n reverseReversedReads();\n generateTrackIndexSequences(reads);\n }\n placeReads();\n tracks = tracks.concat(reads);\n }\n else {\n nodes.forEach(node => {\n if (node) {\n node.incomingReads = [];\n node.outgoingReads = [];\n node.internalReads = [];\n }\n });\n }\n generateNodeXCoords();\n generateSVGShapesFromPath();\n if (DEBUG) {\n console.log('Tracks:');\n console.log(tracks);\n console.log('Nodes:');\n console.log(nodes);\n console.log('Lane assignment:');\n console.log(assignments);\n }\n getImageDimensions();\n return {\n nodes,\n tracks,\n reads,\n nodeMap,\n shapes,\n bounds: imageBounds,\n maxOrder,\n trackForRuler,\n coarsenedEdgeMeta,\n };\n}\nfunction generateTrackColor(track, highlight = 'plain') {\n return config.trackColor(track, highlight);\n}\nfunction generateTrackAlpha(track) {\n return config.trackAlpha(track);\n}\n// structuredClone preserves sparse-array holes; JSON round-trip would fill them with null.\nfunction deepCopy(val) {\n return structuredClone(val);\n}\n// Return true if the given name names a reverse strand node, and false otherwise.\nexport function isReverse(nodeName) {\n const s = String(nodeName);\n return s.length >= 1 && s.startsWith('-');\n}\n// Get the forward version of a node name, which may be either forward or backward (negative)\nexport function forward(nodeName) {\n return isReverse(nodeName) ? String(nodeName).substring(1) : nodeName;\n}\n// Get the reverse version of a node name, which may be either forward or backward (negative)\nexport function reverse(nodeName) {\n return isReverse(nodeName) ? nodeName : `-${nodeName}`;\n}\n// Get the opposite orientation node name for the given node.\nexport function flip(nodeName) {\n return isReverse(nodeName) ? forward(nodeName) : reverse(nodeName);\n}\n// Signed-index orientation test. Uses 1/n to distinguish -0 from +0 since\n// indexSequence entries can be negative-zero for reverse-strand visits of node 0.\nexport function isForwardIndex(n) {\n return (n || 1 / n) >= 0;\n}\n// straighten track given by index by inverting inverted nodes\n// only keep them inverted if this single track runs through them in both directions\n// Operates on the per-render `nodes`/`tracks` copies; inputs are left untouched.\nfunction straightenTrack(index) {\n const nodesToInvert = new Set();\n // find out which nodes should be inverted\n let currentSequence = tracks[index].sequence;\n for (let i = 0; i < currentSequence.length; i += 1) {\n const cur = currentSequence[i];\n if (isReverse(cur)) {\n const nodeName = forward(cur);\n const firstForwardIndex = currentSequence.indexOf(nodeName);\n if (firstForwardIndex === -1 || firstForwardIndex > i) {\n // only if this inverted node is no repeat\n nodesToInvert.add(nodeName);\n }\n }\n }\n // invert nodes in the tracks' sequence\n for (const track of tracks) {\n currentSequence = track.sequence;\n for (let j = 0; j < currentSequence.length; j += 1) {\n const cur = currentSequence[j];\n if (!isReverse(cur)) {\n if (nodesToInvert.has(cur)) {\n currentSequence[j] = reverse(cur);\n }\n }\n else if (nodesToInvert.has(forward(cur))) {\n currentSequence[j] = forward(cur);\n }\n }\n }\n // invert the sequence within the nodes\n nodes.forEach(node => {\n if (node && nodesToInvert.has(node.name)) {\n node.seq = node.seq.split('').reverse().join('');\n }\n });\n}\n// generates attributes (node.y, node.contentHeight) for nodes without tracks, only reads\nfunction generateReadOnlyNodeAttributes() {\n nodesPerOrder = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n nodesPerOrder[i] = [];\n }\n const orderY = new Map();\n nodes.forEach(node => {\n if (node.y !== undefined) {\n setMapToMax(orderY, node.order, node.y + node.contentHeight);\n }\n });\n // for order values where there is no node with haplotypes, orderY is calculated via tracks\n tracks.forEach(track => {\n if (track.type === 'haplotype') {\n track.path.forEach(step => {\n setMapToMax(orderY, step.order, (step.y ?? 0) + track.width);\n });\n }\n });\n nodes.forEach((node, i) => {\n if (node.order >= 0 && node.y === undefined) {\n node.y = (orderY.get(node.order) ?? 0) + 25;\n node.contentHeight = 0;\n nodesPerOrder[node.order].push(i);\n }\n });\n}\nfunction setMapToMax(map, key, value) {\n const existing = map.get(key);\n if (existing === undefined) {\n map.set(key, value);\n }\n else {\n map.set(key, Math.max(existing, value));\n }\n}\nexport const READ_WIDTH = 7;\n// add info about reads to nodes (incoming, outgoing and internal reads)\nfunction assignReadsToNodes() {\n nodes.forEach(node => {\n if (node) {\n node.incomingReads = [];\n node.outgoingReads = [];\n node.internalReads = [];\n }\n });\n reads.forEach((read, idx) => {\n // Honor a pre-set width (e.g. coarsened Sankey bands pre-size themselves\n // by traversing-read count) \u2014 only fall back to the default for normal\n // reads which leave width unset.\n if (read.width === undefined || read.width === 0) {\n read.width = READ_WIDTH;\n }\n if (read.path.length === 1) {\n const firstNode = read.path[0].node;\n if (firstNode !== null) {\n nodes[firstNode].internalReads.push(idx);\n }\n }\n else {\n read.path.forEach((element, pathIdx) => {\n if (pathIdx === 0) {\n const firstNode = read.path[0].node;\n if (firstNode !== null) {\n nodes[firstNode].outgoingReads.push([idx, pathIdx]);\n }\n }\n else {\n const elemNode = element.node;\n if (elemNode !== null) {\n nodes[elemNode].incomingReads.push([idx, pathIdx]);\n }\n }\n });\n }\n });\n}\n// calculate paths (incl. correct y coordinate) for all reads\nfunction placeReads() {\n generateBasicPathsForReads();\n assignReadsToNodes();\n // sort nodes by order, then by y-coordinate\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n // Organize read IDs by source track\n const readsBySource = new Map();\n for (let i = 0; i < reads.length; i++) {\n const source = reads[i].sourceTrackID;\n const bucket = readsBySource.get(source);\n if (bucket === undefined) {\n readsBySource.set(source, [i]);\n }\n else {\n bucket.push(i);\n }\n }\n const allSources = Array.from(readsBySource.keys()).sort((a, b) => a - b);\n // Space out read tracks if multiple exist\n const topMargin = allSources.length > 1 ? READ_WIDTH : 0;\n sortedNodes.forEach(node => {\n if (!node)\n return;\n // For each node\n for (const source of allSources) {\n // Go through all source tracks in order\n // Place the reads from this source in this node.\n // Use a margin to separate multiple read tracks if we have them.\n placeReadSet(readsBySource.get(source), node, topMargin);\n }\n });\n // place read segments which are without node\n const bottomY = calculateBottomY();\n const elementsWithoutNode = [];\n reads.forEach((read, idx) => {\n const len = read.path.length;\n // For each path index, precompute the nearest preceding/following segment\n // whose node is non-null and truthy. Falls back to the boundary segment when\n // none qualifies, matching the original walking-loop semantics.\n const prevSegment = new Array(len);\n let lastValidPrev;\n for (let i = 0; i < len; i++) {\n if (i === 0) {\n prevSegment[i] = undefined;\n }\n else {\n prevSegment[i] = lastValidPrev ?? read.path[0];\n }\n const seg = read.path[i];\n if (seg.node !== null && seg.node)\n lastValidPrev = seg;\n }\n const nextSegment = new Array(len);\n let firstValidNext;\n for (let i = len - 1; i >= 0; i--) {\n if (i === len - 1) {\n nextSegment[i] = undefined;\n }\n else {\n nextSegment[i] = firstValidNext ?? read.path[len - 1];\n }\n const seg = read.path[i];\n if (seg.node !== null && seg.node)\n firstValidNext = seg;\n }\n read.path.forEach((element, pathIdx) => {\n if (element.y === undefined) {\n const previousVisitToNode = prevSegment[pathIdx];\n const nextVisitToNode = nextSegment[pathIdx];\n // Specifically referring to segments between a cycle that's traversing from right to left\n const betweenCycleReverseTraversal = Boolean(\n // A segment can be between a cycle if it there are nodes on both sides\n nextVisitToNode &&\n previousVisitToNode &&\n nextVisitToNode.isForward !== undefined &&\n previousVisitToNode.isForward !== undefined &&\n // A segment is between a cycle if the next node it visits is behind the previous node it visited\n (previousVisitToNode.order > nextVisitToNode.order ||\n // A segment can also be between a cycle if it's visiting the same node it just visited in the same direction\n (nextVisitToNode.order === previousVisitToNode.order &&\n nextVisitToNode.isForward === previousVisitToNode.isForward)));\n element.betweenCycleReverseTraversal = betweenCycleReverseTraversal;\n elementsWithoutNode.push({\n readIndex: idx,\n pathIndex: pathIdx,\n previousY: previousVisitToNode?.y,\n previousNode: previousVisitToNode?.node,\n });\n }\n });\n });\n elementsWithoutNode.sort(compareNoNodeReads);\n elementsWithoutNode.forEach(element => {\n const read = reads[element.readIndex];\n const segment = read.path[element.pathIndex];\n segment.y = bottomY[segment.order];\n bottomY[segment.order] += read.width;\n });\n if (DEBUG) {\n console.log('Reads:');\n console.log(reads);\n }\n}\n// Place a particular collection of reads, identified by a list of read\n// numbers, into the given node at the right Y coordinates. All reads in all\n// nodes above it, and no reads in any nodes below it, are already placed.\n// Makes the given node bigger if needed and moves other nodes down if needed.\n// If topMargin is set, applies that amount of spacing down from whatever is above the reads.\nfunction placeReadSet(readIDs, node, topMargin) {\n // Turn the read IDs into a set\n const toPlace = new Set(readIDs);\n // Get arrays of the read entry/exit/internal-ness records we want to work on\n let incomingReads = node.incomingReads.filter(([readID]) => toPlace.has(readID));\n const outgoingReads = node.outgoingReads.filter(([readID]) => toPlace.has(readID));\n const internalReads = node.internalReads.filter(readID => toPlace.has(readID));\n // Only actually use the top margin if we have any reads on the node.\n if (incomingReads.length === 0 &&\n outgoingReads.length === 0 &&\n internalReads.length === 0) {\n topMargin = 0;\n }\n // Determine where we start vertically in the node.\n // TODO: Why do we have to double this to keep reads out of adjacent lanes???\n const startY = node.y + node.contentHeight + topMargin * 2;\n // sort incoming reads \u2014 decorate with a precomputed key so each compare is O(1).\n // The original comparator walks pathIdx backwards in lockstep on both sides, picking\n // the first step where either has a defined y (or reaches index 0). The walk's outcome\n // for a single entry is fully captured by:\n // decisionStep: the smallest k>=1 where path[pathIdx-k].y is defined, or pathIdx+1\n // if no preceding segment has a defined y (the \"reached 0\" case).\n // y: the defined y at decisionStep, or undefined for \"reached 0\".\n // Smaller decisionStep wins; ties put the \"reached 0\" entries first;\n // otherwise compare y's.\n const incomingKeys = incomingReads.map(entry => {\n const [readID, pathIdx] = entry;\n const path = reads[readID].path;\n let decisionStep = pathIdx + 1;\n let foundY;\n for (let k = 1; k <= pathIdx; k++) {\n const y = path[pathIdx - k]?.y;\n if (y !== undefined) {\n decisionStep = k;\n foundY = y;\n break;\n }\n }\n return { entry, decisionStep, y: foundY };\n });\n incomingKeys.sort(compareIncomingReadKeys);\n incomingReads = incomingKeys.map(k => k.entry);\n // place incoming reads\n let currentY = startY;\n const occupiedUntil = new Map();\n incomingReads.forEach(readElement => {\n const read = reads[readElement[0]];\n read.path[readElement[1]].y = currentY;\n setOccupiedUntil(occupiedUntil, read, readElement[1], currentY, node);\n currentY += read.width;\n });\n let maxY = currentY;\n // sort outgoing reads\n outgoingReads.sort(compareReadOutgoingSegmentsByGoingTo);\n // place outgoing reads\n const occupiedFrom = new Map();\n currentY = startY;\n outgoingReads.forEach(readElement => {\n const read = reads[readElement[0]];\n const firstNodeOffset = read.firstNodeOffset ?? 0;\n // place in next lane\n read.path[readElement[1]].y = currentY;\n occupiedFrom.set(currentY, firstNodeOffset);\n // if no conflicts. The original predicate was strict-less-than, which\n // treats two reads that exactly touch (incoming ends at base K, outgoing\n // starts at base K+1) as conflicting. They don't overlap, so they can\n // share a lane \u2014 relax to <=. This also matters for coarsened Sankey\n // bands at single-base nodes (e.g. a 1bp \"G\" node): with edge-only offsets\n // the math becomes 0+1 < 1 (conflict, wrong) vs. 0+1 <= 1 (no conflict).\n const occUntil = occupiedUntil.get(currentY);\n if (occUntil === undefined || occUntil + 1 <= firstNodeOffset) {\n currentY += read.width;\n maxY = Math.max(maxY, currentY);\n }\n else {\n // otherwise push down incoming reads to make place for outgoing Read\n occupiedUntil.set(currentY, 0);\n incomingReads.forEach(incReadElementIndices => {\n const incRead = reads[incReadElementIndices[0]];\n const incReadPathElement = incRead.path[incReadElementIndices[1]];\n if (incReadPathElement.y !== undefined &&\n incReadPathElement.y >= currentY) {\n incReadPathElement.y += read.width;\n setOccupiedUntil(occupiedUntil, incRead, incReadElementIndices[1], incReadPathElement.y, node);\n }\n });\n currentY += read.width;\n maxY += read.width;\n }\n });\n // sort internal reads\n internalReads.sort(compareInternalReads);\n // place internal reads\n internalReads.forEach(readIdx => {\n const currentRead = reads[readIdx];\n const firstNodeOffset = currentRead.firstNodeOffset ?? 0;\n const finalNodeCoverLength = currentRead.finalNodeCoverLength ?? 0;\n currentY = startY;\n while (firstNodeOffset < (occupiedUntil.get(currentY) ?? -Infinity) + 2 ||\n finalNodeCoverLength > (occupiedFrom.get(currentY) ?? Infinity) - 3) {\n currentY += currentRead.width || READ_WIDTH;\n }\n currentRead.path[0].y = currentY;\n occupiedUntil.set(currentY, finalNodeCoverLength);\n maxY = Math.max(maxY, currentY);\n });\n // adjust node height and move other nodes vertically down\n const heightIncrease = maxY - node.y - node.contentHeight;\n node.contentHeight += heightIncrease;\n adjustVertically3(node, heightIncrease);\n}\n// Smaller decisionStep wins. On a tie, entries that walked off the front of the\n// path (no defined y) sort first and tie with each other; otherwise compare y.\nexport function compareIncomingReadKeys(a, b) {\n if (a.decisionStep !== b.decisionStep) {\n return a.decisionStep - b.decisionStep;\n }\n if (a.y === undefined || b.y === undefined) {\n return (a.y === undefined ? 0 : 1) - (b.y === undefined ? 0 : 1);\n }\n return a.y - b.y;\n}\n// keeps track of where reads end within nodes\nfunction setOccupiedUntil(map, read, pathIndex, y, node) {\n if (pathIndex === read.path.length - 1) {\n // last node of current read\n map.set(y, read.finalNodeCoverLength ?? 0);\n }\n else {\n // read covers the whole node\n map.set(y, node.sequenceLength);\n }\n}\n// compare read segments which are outside of nodes to sort them in a good horizontal\n// and then vertical display order.\nfunction compareNoNodeReads(a, b) {\n const readA = reads[a.readIndex];\n const readB = reads[b.readIndex];\n const segmentA = readA.path[a.pathIndex];\n const segmentB = readB.path[b.pathIndex];\n // Sort by order by segments\n if (segmentA.order !== segmentB.order) {\n return segmentA.order - segmentB.order;\n }\n // Sort by reads' source track\n if (readA.sourceTrackID !== readB.sourceTrackID) {\n return readA.sourceTrackID - readB.sourceTrackID;\n }\n // Sort by order of previous node\n if (a.previousNode && b.previousNode) {\n const prevNodeA = nodes[a.previousNode];\n const prevNodeB = nodes[b.previousNode];\n if (prevNodeA && prevNodeB && prevNodeA.order !== prevNodeB.order) {\n return prevNodeA.order - prevNodeB.order;\n }\n }\n // We want to sort in reverse order when the segment is along the reverse-going part of a cycle.\n // This ensures a loop that starts on the outside, stays on the outside,\n // and rolls up in order with other loops.\n const aPrev = a.previousY ?? 0;\n const bPrev = b.previousY ?? 0;\n if (segmentA.betweenCycleReverseTraversal &&\n segmentB.betweenCycleReverseTraversal) {\n return bPrev - aPrev;\n }\n else {\n return aPrev - bPrev;\n }\n}\n// compare read segments by where they are going to\nfunction compareReadOutgoingSegmentsByGoingTo([readIndexA, pathIndexA], [readIndexB, pathIndexB]) {\n // Expect two arrays both containing 2 integers.\n // The first index of each array contains the read index\n // The second index of each array contains the path index\n // Segments are first sorted by the y value of their last node,\n // then by the node they end on,\n // then by length in final node\n const readA = reads[readIndexA];\n const readB = reads[readIndexB];\n let previousValidYA;\n let previousValidYB;\n let lastPathIndexA = readA.path.length - 1;\n let lastPathIndexB = readB.path.length - 1;\n while (!previousValidYA && lastPathIndexA >= 0) {\n previousValidYA = readA.path[lastPathIndexA]?.y;\n lastPathIndexA -= 1;\n }\n while (!previousValidYB && lastPathIndexB >= 0) {\n previousValidYB = readB.path[lastPathIndexB]?.y;\n lastPathIndexB -= 1;\n }\n if (previousValidYA && previousValidYB) {\n return previousValidYA - previousValidYB;\n }\n // Couldn't find a valid y value for at least one of the reads, sort by which node reads end on\n const initialNodeA = readA.path[pathIndexA]?.node;\n const initialNodeB = readB.path[pathIndexB]?.node;\n let nodeA = initialNodeA != null ? nodes[initialNodeA] : null;\n let nodeB = initialNodeB != null ? nodes[initialNodeB] : null;\n // Follow the reads' paths until we find the node they diverge at\n // Or, they go through all the same nodes and we do a tiebreaker at the end\n while (nodeA != null && nodeB != null && nodeA === nodeB) {\n if (pathIndexA < readA.path.length - 1) {\n pathIndexA += 1;\n while (readA.path[pathIndexA]?.node === null)\n pathIndexA += 1; // skip null nodes in path\n const nextNodeIdx = readA.path[pathIndexA]?.node;\n nodeA = nextNodeIdx != null ? nodes[nextNodeIdx] : null;\n }\n else {\n nodeA = null;\n }\n if (pathIndexB < readB.path.length - 1) {\n pathIndexB += 1;\n while (readB.path[pathIndexB]?.node === null)\n pathIndexB += 1; // skip null nodes in path\n const nextNodeIdx = readB.path[pathIndexB]?.node;\n nodeB = nextNodeIdx != null ? nodes[nextNodeIdx] : null;\n }\n else {\n nodeB = null;\n }\n }\n if (nodeA != null) {\n if (nodeB != null)\n return compareNodesByOrder(nodeA, nodeB);\n return 1; // nodeB is null, nodeA not null\n }\n if (nodeB != null)\n return -1; // nodeB not null, nodeA null\n // both nodes are null -> both end in the same node\n const beginDiff = (readA.firstNodeOffset ?? 0) - (readB.firstNodeOffset ?? 0);\n if (beginDiff !== 0)\n return beginDiff;\n // break tie: both reads cover the same nodes and begin at the same position\n // One or both reads didn't have a previously valid Y value, compare by the endPosition of the read\n return (readA.finalNodeCoverLength ?? 0) - (readB.finalNodeCoverLength ?? 0);\n}\n// Compare tracks based on ordering at their first convergence\nfunction compareTrackByInitialOrdering(trackA, trackB) {\n // Find the first node where the two tracks converge, sort by layer.\n // Tracks with no path sort first, and two of them tie.\n if (trackA.path === undefined || trackB.path === undefined) {\n return ((trackA.path === undefined ? 0 : 1) - (trackB.path === undefined ? 0 : 1));\n }\n const pathA = trackA.path;\n const pathB = trackB.path;\n let AIndex = 0;\n let BIndex = 0;\n while (AIndex < pathA.length && BIndex < pathB.length) {\n const a = pathA[AIndex];\n const b = pathB[BIndex];\n const trackAOrder = a.order;\n const trackBOrder = b.order;\n if (trackAOrder === trackBOrder && a.node && b.node) {\n return (b.y ?? 0) - (a.y ?? 0);\n }\n else if (trackAOrder < trackBOrder) {\n AIndex += 1;\n }\n else if (trackAOrder > trackBOrder) {\n BIndex += 1;\n }\n else {\n // Orders are equal but are traversing out of nodes\n AIndex += 1;\n BIndex += 1;\n }\n }\n // Tracks do not converge, keep the same ordering\n return 0;\n}\n// compare 2 reads which are completely within a single node\nfunction compareInternalReads(idxA, idxB) {\n const a = reads[idxA];\n const b = reads[idxB];\n // compare by first base within first node\n const aFirst = a.firstNodeOffset ?? 0;\n const bFirst = b.firstNodeOffset ?? 0;\n if (aFirst < bFirst)\n return -1;\n else if (aFirst > bFirst)\n return 1;\n // compare by last base within last node\n const aLast = a.finalNodeCoverLength ?? 0;\n const bLast = b.finalNodeCoverLength ?? 0;\n if (aLast < bLast)\n return -1;\n else if (aLast > bLast)\n return 1;\n return 0;\n}\n// determine biggest y-coordinate for each order-value\nfunction calculateBottomY() {\n const bottomY = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n bottomY.push(0);\n }\n nodes.forEach(node => {\n if (node.y !== undefined) {\n bottomY[node.order] = Math.max(bottomY[node.order], node.y + node.contentHeight + 20);\n }\n });\n tracks.forEach(track => {\n track.path.forEach(element => {\n bottomY[element.order] = Math.max(bottomY[element.order], (element.y ?? 0) + track.width);\n });\n });\n return bottomY;\n}\n// generate path-info for each read\n// containing order, node and orientation, but no concrete coordinates\n// TODO: Duplicates a lot of the same work as generateLaneAssignment() does for non-read tracks.\nfunction generateBasicPathsForReads() {\n reads.forEach(read => {\n // add info for start of track\n let currentNodeIndex = Math.abs(read.indexSequence[0]);\n let currentNodeIsForward = isForwardIndex(read.indexSequence[0]);\n let currentNode = nodes[currentNodeIndex];\n let previousNode;\n let previousNodeIsForward;\n read.path = [];\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n for (let i = 1; i < read.sequence.length; i += 1) {\n previousNode = currentNode;\n previousNodeIsForward = currentNodeIsForward;\n currentNodeIndex = Math.abs(read.indexSequence[i]);\n currentNodeIsForward = isForwardIndex(read.indexSequence[i]);\n currentNode = nodes[currentNodeIndex];\n if (currentNode.order > previousNode.order) {\n if (!previousNodeIsForward) {\n // backward to forward at previous node\n read.path.push({\n order: previousNode.order,\n isForward: true,\n node: null,\n });\n }\n for (let j = previousNode.order + 1; j < currentNode.order; j += 1) {\n // forward without nodes\n read.path.push({ order: j, isForward: true, node: null });\n }\n if (!currentNodeIsForward) {\n // forward to backward at current node\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: currentNodeIndex,\n });\n }\n else {\n // current Node forward\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: currentNodeIndex,\n });\n }\n }\n else if (currentNode.order < previousNode.order) {\n if (previousNodeIsForward) {\n // turnaround from fw to bw at previous node\n read.path.push({\n order: previousNode.order,\n isForward: false,\n node: null,\n });\n }\n for (let j = previousNode.order - 1; j > currentNode.order; j -= 1) {\n // bachward without nodes\n read.path.push({ order: j, isForward: false, node: null });\n }\n if (currentNodeIsForward) {\n // backward to forward at current node\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: currentNodeIndex,\n });\n }\n else {\n // backward at current node\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: currentNodeIndex,\n });\n }\n }\n else {\n if (currentNodeIsForward !== previousNodeIsForward) {\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n }\n else {\n read.path.push({\n order: currentNode.order,\n isForward: !currentNodeIsForward,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n }\n }\n }\n });\n}\n// reverse reads which are reversed\nfunction reverseReversedReads() {\n reads.forEach(read => {\n let pos = 0;\n while (pos < read.sequence.length && read.sequence[pos].startsWith('-')) {\n pos += 1;\n }\n if (pos === read.sequence.length) {\n // completely reversed read\n read.is_reverse = true;\n read.sequence = read.sequence.reverse(); // invert sequence\n for (let i = 0; i < read.sequence.length; i += 1) {\n read.sequence[i] = forward(read.sequence[i]); // visit nodes forward\n // TODO: Do we really want to visit all the nodes forward here? Are we\n // sure we aren't in mixed orientation?\n }\n const sequenceNew = read.sequenceNew ?? [];\n sequenceNew.reverse(); // invert sequence\n read.sequenceNew = sequenceNew;\n for (const entry of sequenceNew) {\n entry.nodeName = forward(entry.nodeName); // visit nodes forward\n reverseMismatches(entry.mismatches, nodeByName(entry.nodeName).sequenceLength);\n }\n // adjust firstNodeOffset and finalNodeCoverLength\n const temp = read.firstNodeOffset ?? 0;\n const firstLen = nodeByName(read.sequence[0]).sequenceLength;\n read.firstNodeOffset = firstLen - (read.finalNodeCoverLength ?? 0);\n const lastLen = nodeByName(read.sequence[read.sequence.length - 1]).sequenceLength;\n read.finalNodeCoverLength = lastLen - temp;\n }\n });\n}\n// Flip a node's mismatches onto the opposite strand: positions are measured\n// from the other end of the node, and sequences are complemented.\n// sequenceLength (not node.width, which is only equal to it in 'normal'\n// node-width mode) is the right pivot because mismatch positions are base\n// offsets. Exported for testing.\nexport function reverseMismatches(mismatches, sequenceLength) {\n mismatches.forEach(mm => {\n if (mm.type === 'insertion') {\n mm.pos = sequenceLength - mm.pos;\n }\n else if (mm.type === 'deletion') {\n mm.pos = sequenceLength - mm.pos - (mm.length ?? 0);\n }\n else if (mm.type === 'substitution') {\n mm.pos = sequenceLength - mm.pos - (mm.seq?.length ?? 0);\n }\n if (mm.seq !== undefined) {\n // NOTE: reverse-complement followed by reverse is a plain complement.\n // Preserved verbatim from the original code rather than \"fixed\" here.\n mm.seq = getReverseComplement(mm.seq).split('').reverse().join('');\n }\n });\n}\nconst COMPLEMENT = { A: 'T', T: 'A', C: 'G', G: 'C' };\nfunction getReverseComplement(s) {\n let result = '';\n for (let i = s.length - 1; i >= 0; i -= 1) {\n result += COMPLEMENT[s.charAt(i)] ?? 'N';\n }\n return result;\n}\n// for each track: generate sequence of node indices from seq. of node names\nfunction generateTrackIndexSequences(tracksOrReads) {\n tracksOrReads.forEach(track => {\n track.indexSequence = [];\n track.sequence.forEach(rawNodeName => {\n let nodeName = rawNodeName;\n // if node was switched, reverse it here\n // Q? Is flipping the index enough? It looks like yes. Or should we also flip the node name in 'sequence'?\n const fwdIdx = nodeMap.get(forward(nodeName));\n const switched = nodes[fwdIdx]?.switched ?? false;\n if (switched) {\n nodeName = flip(nodeName);\n }\n // Get the index to visit the node. If the node is switched, this means\n // visiting it reverse. Otherwise, this means visiting it forward.\n const nodeIndex = nodeMap.get(forward(nodeName));\n if (nodeIndex === 0) {\n // If a node index is ever 0, we can't visit it in reverse, so we don't allow that to happen.\n throw new Error('Node ' + forward(nodeName) + ' has prohibited index 0');\n }\n if (isReverse(nodeName) !== switched) {\n // If we visit the node in reverse XOR the node is switched, go through\n // it right to left as displayed.\n track.indexSequence.push(-nodeIndex);\n }\n else {\n // If either the node isn't switched and we go through it forward, or\n // the node is switched *and* we go through it backward, go through it\n // left to right as displayed.\n track.indexSequence.push(nodeIndex);\n }\n });\n });\n}\n// Tracks enter and leave a node this far outside it horizontally. Note that\n// getImageDimensions() below only adds it on the right: the pannable area\n// understates the left edge of the drawing by this much.\nconst NODE_HORIZONTAL_SLACK = 20;\n// get the minimum and maximum coordinates used in the image to calculate image dimensions\nfunction getImageDimensions() {\n // Sentinels, deliberately crossed: if nothing below runs, minZoom() and\n // alignSVG() detect the empty content rather than computing a negative scale.\n const bounds = { minX: 99, maxX: -99, minY: 99, maxY: -99 };\n nodes.forEach(node => {\n if (!node)\n return;\n if (node.x !== undefined) {\n bounds.minX = Math.min(bounds.minX, node.x);\n bounds.maxX = Math.max(bounds.maxX, node.x + NODE_HORIZONTAL_SLACK + node.pixelWidth);\n }\n if (node.y !== undefined) {\n bounds.minY = Math.min(bounds.minY, node.y - 10);\n bounds.maxY = Math.max(bounds.maxY, node.y + node.contentHeight + 10);\n }\n });\n tracks.forEach(track => {\n track.path.forEach(segment => {\n const y = segment.y ?? 0;\n bounds.maxY = Math.max(bounds.maxY, y + track.width);\n bounds.minY = Math.min(bounds.minY, y);\n });\n });\n imageBounds = bounds;\n}\n// Resolve a (possibly reverse-oriented) node name to its layout node. Every\n// caller runs after generateNodeMap, so a miss is a programming error.\nfunction nodeByName(nodeName) {\n const index = nodeMap.get(forward(nodeName));\n const node = index === undefined ? undefined : nodes[index];\n if (node === undefined) {\n throw new Error(`Unknown node ${nodeName}`);\n }\n return node;\n}\n// map node names to node indices\nfunction generateNodeMap() {\n nodeMap = new Map();\n nodes.forEach((node, index) => {\n if (node) {\n nodeMap.set(node.name, index);\n }\n });\n return nodeMap;\n}\n// adds a successor-array to each node containing the indices of the nodes coming directly after the current node\nfunction generateNodeSuccessors() {\n const successorSets = nodes.map(() => new Set());\n const predecessorSets = nodes.map(() => new Set());\n const addEdges = (track) => {\n for (let i = 0; i < track.indexSequence.length - 1; i += 1) {\n const current = Math.abs(track.indexSequence[i]);\n const follower = Math.abs(track.indexSequence[i + 1]);\n successorSets[current].add(follower);\n predecessorSets[follower].add(current);\n }\n };\n tracks.forEach(addEdges);\n if (config.showReads && reads.length > 0) {\n reads.forEach(addEdges);\n }\n nodes.forEach((node, i) => {\n if (node) {\n node.successors = Array.from(successorSets[i]);\n node.predecessors = Array.from(predecessorSets[i]);\n }\n });\n}\nfunction generateNodeOrderOfSingleTrack(sequence) {\n let forwardOrder = 0;\n let backwardOrder = 0;\n let minOrder = 0;\n sequence.forEach(nodeIndex => {\n const idx = Math.abs(nodeIndex);\n if (nodeIndex < 0) {\n if (nodeOrders[idx] === undefined)\n nodeOrders[idx] = backwardOrder;\n const order = nodeOrders[idx];\n if (order < minOrder)\n minOrder = order;\n forwardOrder = order;\n backwardOrder = order - 1;\n }\n else {\n if (nodeOrders[idx] === undefined)\n nodeOrders[idx] = forwardOrder;\n const order = nodeOrders[idx];\n forwardOrder = order + 1;\n backwardOrder = order;\n }\n });\n if (minOrder < 0) {\n increaseOrderForAllNodes(-minOrder);\n }\n}\n// calculate the order-value of nodes contained in sequence which are to the left of the first node which already has an order-value\nfunction generateNodeOrderTrackBeginning(sequence) {\n let anchorIndex = 0;\n let minOrder = 0;\n while (anchorIndex < sequence.length &&\n nodeOrders[Math.abs(sequence[anchorIndex])] === undefined) {\n anchorIndex += 1; // anchor = first node in common with existing graph\n }\n if (anchorIndex >= sequence.length) {\n return null;\n }\n const anchorSeqVal = sequence[anchorIndex];\n let currentOrder;\n let increment;\n if (anchorSeqVal >= 0) {\n // regular node\n currentOrder = nodeOrders[anchorSeqVal] - 1;\n increment = -1;\n }\n else {\n // reverse node\n currentOrder = nodeOrders[-anchorSeqVal] + 1;\n increment = 1;\n }\n for (let j = anchorIndex - 1; j >= 0; j -= 1) {\n // assign order to nodes which are left of anchor node\n const idx = Math.abs(sequence[j]);\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder += increment;\n }\n }\n if (minOrder < 0) {\n increaseOrderForAllNodes(-minOrder);\n }\n return anchorIndex;\n}\n// Sentinel order for nodes no track or read reaches.\nexport const UNREACHABLE_ORDER = -1;\n// Replace every unassigned entry with UNREACHABLE_ORDER, producing a dense\n// array. Holes matter here: `new Array(n)` is all holes and forEach/map skip\n// them, so an in-place forEach would leave the sentinel unset.\nexport function fillUnassignedOrders(orders) {\n const filled = [];\n for (const order of orders) {\n filled.push(order ?? UNREACHABLE_ORDER);\n }\n return filled;\n}\n// generate global sequence of nodes from left to right, starting with first track and adding other tracks sequentially\nfunction generateNodeOrder() {\n let modifiedSequence;\n let currentOrder;\n let rightIndex;\n let leftIndex;\n let minOrder = 0;\n const tracksAndReads = config.showReads && reads.length > 0 ? tracks.concat(reads) : tracks;\n const reachability = {\n stamp: new Int32Array(nodes.length),\n generation: 0,\n };\n // fill() makes the array dense: `new Array(n)` alone is all holes, which\n // forEach skips, so neither the sentinel pass nor the copy-back below ran.\n nodeOrders = new Array(nodes.length).fill(undefined);\n nodes.forEach(node => {\n if (node) {\n node.order = undefined;\n }\n });\n generateNodeOrderOfSingleTrack(tracks[0].indexSequence);\n for (let i = 1; i < tracksAndReads.length; i += 1) {\n if (DEBUG)\n console.log(`generating order for track ${i + 1}`);\n rightIndex = generateNodeOrderTrackBeginning(tracksAndReads[i].indexSequence);\n if (rightIndex === null) {\n if (tracksAndReads[i].type === 'haplotype') {\n generateNodeOrderOfSingleTrack(tracksAndReads[i].indexSequence);\n }\n else {\n tracksAndReads.splice(i, 1);\n reads.splice(i - tracks.length, 1);\n i -= 1;\n }\n continue;\n }\n modifiedSequence = uninvert(tracksAndReads[i].indexSequence);\n while (rightIndex < modifiedSequence.length) {\n // move right until the end of the sequence\n leftIndex = rightIndex;\n rightIndex += 1;\n while (rightIndex < modifiedSequence.length &&\n nodeOrders[modifiedSequence[rightIndex]] === undefined) {\n rightIndex += 1;\n }\n if (rightIndex < modifiedSequence.length) {\n // middle segment between two anchors\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] + 1;\n for (let j = leftIndex + 1; j < rightIndex; j += 1) {\n nodeOrders[modifiedSequence[j]] = currentOrder;\n currentOrder += 1;\n }\n if (nodeOrders[modifiedSequence[rightIndex]] >\n nodeOrders[modifiedSequence[leftIndex]]) {\n if (nodeOrders[modifiedSequence[rightIndex]] < currentOrder) {\n increaseOrderForSuccessors(modifiedSequence[rightIndex], modifiedSequence[rightIndex - 1], currentOrder);\n }\n }\n else {\n if (tracksAndReads[i].indexSequence[rightIndex] >= 0 &&\n !isSuccessor(modifiedSequence[rightIndex], modifiedSequence[leftIndex], reachability)) {\n // no real reversal\n increaseOrderForSuccessors(modifiedSequence[rightIndex], modifiedSequence[rightIndex - 1], currentOrder);\n }\n else {\n // real reversal\n if (tracksAndReads[i].indexSequence[leftIndex] < 0 ||\n (nodes[modifiedSequence[leftIndex + 1]].degree < 2 &&\n nodeOrders[modifiedSequence[rightIndex]] <\n nodeOrders[modifiedSequence[leftIndex]])) {\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] - 1;\n for (let j = leftIndex + 1; j < rightIndex; j += 1) {\n nodeOrders[modifiedSequence[j]] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder -= 1;\n }\n }\n }\n }\n }\n else {\n // right segment to the right of last anchor\n if (tracksAndReads[i].indexSequence[leftIndex] >= 0) {\n // elongate towards the right\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] + 1;\n for (let j = leftIndex + 1; j < modifiedSequence.length; j += 1) {\n const idx = modifiedSequence[j];\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n currentOrder += 1;\n }\n }\n }\n else {\n // elongate towards the left\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] - 1;\n for (let j = leftIndex + 1; j < modifiedSequence.length; j += 1) {\n const idx = modifiedSequence[j];\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder -= 1;\n }\n }\n }\n }\n }\n }\n if (minOrder < 0)\n increaseOrderForAllNodes(-minOrder);\n // Nodes unreachable from any track get UNREACHABLE_ORDER so every node ends\n // up with a defined order; downstream code uses `order >= 0` to skip them.\n const finalOrders = fillUnassignedOrders(nodeOrders);\n nodeOrders = finalOrders;\n finalOrders.forEach((order, i) => {\n const node = nodes[i];\n if (node !== undefined) {\n node.order = order;\n }\n });\n}\nfunction isSuccessor(first, second, scratch) {\n scratch.generation += 1;\n const { stamp, generation } = scratch;\n const stack = [first];\n stamp[first] = generation;\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === second)\n return true;\n for (const childIndex of nodes[current].successors) {\n if (stamp[childIndex] !== generation) {\n stamp[childIndex] = generation;\n stack.push(childIndex);\n }\n }\n }\n return false;\n}\n// get order number of the rightmost node\nfunction getMaxOrder() {\n let max = -1;\n nodeOrders.forEach(order => {\n if (order !== undefined && order > max)\n max = order;\n });\n return max;\n}\n// generates sequence keeping the order but switching all reversed (negative) nodes to forward nodes\nfunction uninvert(sequence) {\n return sequence.map(v => Math.abs(v));\n}\n// increases the order-value of all nodes by amount\nfunction increaseOrderForAllNodes(amount) {\n nodeOrders.forEach((order, i) => {\n if (order !== undefined)\n nodeOrders[i] = order + amount;\n });\n}\n// increases the order-value for currentNode and (if necessary) successor nodes recursively\nfunction increaseOrderForSuccessors(startingNode, tabuNode, newOrder) {\n const increasedOrders = new Map();\n // Walked with a head pointer rather than shift(), which is O(length) per pop.\n const queue = [[startingNode, newOrder]];\n let head = 0;\n while (head < queue.length) {\n const [currentNode, currentOrder] = queue[head];\n head += 1;\n const currentNodeOrder = nodeOrders[currentNode];\n if (currentNodeOrder !== undefined && currentNodeOrder < currentOrder) {\n if (!increasedOrders.has(currentNode) ||\n increasedOrders.get(currentNode) < currentOrder) {\n increasedOrders.set(currentNode, currentOrder);\n nodes[currentNode].successors.forEach(successor => {\n if (nodeOrders[successor] > currentNodeOrder &&\n successor !== tabuNode) {\n // only increase order of successors to the right of currentNode\n queue.push([successor, currentOrder + 1]);\n }\n });\n if (currentNode !== startingNode) {\n nodes[currentNode].predecessors.forEach(predecessor => {\n if (nodeOrders[predecessor] > currentNodeOrder &&\n predecessor !== tabuNode) {\n // only increase order of predecessors to the right of currentNode\n queue.push([predecessor, currentOrder + 1]);\n }\n });\n }\n }\n }\n }\n increasedOrders.forEach((value, key) => {\n nodeOrders[key] = value;\n });\n}\n// calculates the node degree: the number of tracks passing through the node / the node height\nfunction generateNodeDegree() {\n nodes.forEach(node => {\n node.tracks = [];\n });\n tracks.forEach(track => {\n track.indexSequence.forEach(nodeIndex => {\n nodes[Math.abs(nodeIndex)].tracks.push(track.id);\n });\n });\n nodes.forEach(node => {\n if (node.tracks !== undefined)\n node.degree = node.tracks.length;\n });\n}\n// Optimize the orientations for nodes in the global `nodes` for displaying the\n// paths in the global `tracks` and the read paths, if applicable, in the\n// global `reads`\nfunction switchNodeOrientation() {\n const pivotPath = tracks[0];\n let countPaths = tracks.slice(1, tracks.length);\n if (config.showReads && reads.length > 0) {\n countPaths = countPaths.concat(reads);\n }\n switchNodeOrientationForPaths(countPaths, pivotPath);\n if (config.showReads && reads.length > 0) {\n // Any changes should be committed back\n for (let i = 0; i < reads.length; i++) {\n if (reads[i] !== countPaths[i + tracks.length - 1]) {\n throw new Error('Read inequality');\n }\n }\n }\n}\n// If more of the given paths pass through a specific node in reverse direction than in\n// regular direction, switch its orientation. Processes all paths' nodes in\n// place, so if you want e.g. a first track with nodes fixed in that\n// orientation, pass it as pivotPath.\n// References and modifies the global nodes variable.\nfunction switchNodeOrientationForPaths(paths, pivotPath) {\n const toSwitch = new Map();\n const pivotNames = pivotPath ? new Set(pivotPath.sequence) : null;\n for (const path of paths) {\n const sequence = path.sequence;\n for (let j = 0; j < sequence.length; j += 1) {\n const nodeName = forward(sequence[j]);\n const currentNode = nodeByName(nodeName);\n if (pivotNames && !pivotNames.has(nodeName)) {\n // do not change orientation for nodes which are part of the pivot path\n const prevOrder = j > 0 ? nodeByName(sequence[j - 1]).order : undefined;\n const nextOrder = j < sequence.length - 1\n ? nodeByName(sequence[j + 1]).order\n : undefined;\n if ((prevOrder === undefined || prevOrder < currentNode.order) &&\n (nextOrder === undefined || currentNode.order < nextOrder)) {\n // Node is visited in increasing order along the path. Reverse\n // visits count towards switching, forward visits against it.\n addToSwitchScore(toSwitch, nodeName, isReverse(sequence[j]) ? 1 : -1);\n }\n if ((prevOrder === undefined || prevOrder > currentNode.order) &&\n (nextOrder === undefined || currentNode.order > nextOrder)) {\n // Node is visited in *decreasing* order along the path, so is already\n // backward: the votes are the other way round.\n addToSwitchScore(toSwitch, nodeName, isReverse(sequence[j]) ? -1 : 1);\n }\n }\n }\n }\n for (const path of paths) {\n path.sequence.forEach((node, nodeIndex) => {\n const score = toSwitch.get(forward(node));\n if (score !== undefined && score > 0) {\n // This node is backward more so flip it around\n path.sequence[nodeIndex] = flip(node);\n path.indexSequence[nodeIndex] = -path.indexSequence[nodeIndex];\n }\n });\n }\n // invert the sequence within the nodes and mark them as \"switched\"\n toSwitch.forEach((value, key) => {\n if (value > 0) {\n const node = nodeByName(key);\n node.seq = getReverseComplement(node.seq);\n node.switched = true;\n }\n });\n}\nfunction addToSwitchScore(scores, nodeName, delta) {\n scores.set(nodeName, (scores.get(nodeName) ?? 0) + delta);\n}\n// calculates the concrete values for the nodes' x-coordinates\nfunction generateNodeXCoords() {\n let currentX = 0;\n let nextX = 20;\n let currentOrder = -1;\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n const extra = calculateExtraSpace();\n sortedNodes.forEach(node => {\n if (node.order >= 0) {\n if (node.order > currentOrder) {\n currentOrder = node.order;\n currentX = nextX + 10 * extra[node.order];\n }\n node.x = currentX;\n nextX = Math.max(nextX, currentX + 40 + node.pixelWidth);\n }\n });\n}\n// calculates additional horizontal space needed between two nodes\n// two neighboring nodes have to be moved further apart if there is a lot going on in between them\n// -> edges turning to vertical orientation should not overlap\nfunction calculateExtraSpace() {\n const leftSideEdges = [];\n const rightSideEdges = [];\n const fallAngleAdjustment = [];\n const extra = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n leftSideEdges.push(0);\n rightSideEdges.push(0);\n fallAngleAdjustment.push(0);\n }\n tracks.forEach(track => {\n for (let i = 1; i < track.path.length; i += 1) {\n const seg = track.path[i];\n const prevSeg = track.path[i - 1];\n // Track is going to the same node, account for space taken up by edges looping around\n if (seg.order === prevSeg.order) {\n // repeat or translocation\n if (seg.isForward) {\n leftSideEdges[seg.order] = leftSideEdges[seg.order] + 1;\n }\n else {\n rightSideEdges[seg.order] = rightSideEdges[seg.order] + 1;\n }\n }\n else {\n // Track is going to a different node; account for space needed to limit rise/fall angle\n const yDifference = Math.abs((seg.y ?? 0) - (prevSeg.y ?? 0));\n //TODO: Extra space should also be accounted when there are too many tracks curving at the nodes\n fallAngleAdjustment[seg.order] = Math.max(yDifference / 17.5, fallAngleAdjustment[seg.order]);\n }\n }\n });\n extra.push(Math.max(0, leftSideEdges[0] - 1));\n for (let i = 1; i <= maxOrder; i += 1) {\n // Extra space uses space needed for edges(tracks looping), or space needed to limit rise/fall angle, whichever is larger\n extra.push(Math.max(Math.max(0, leftSideEdges[i] - 1) +\n Math.max(0, rightSideEdges[i - 1] - 1), fallAngleAdjustment[i]));\n }\n return extra;\n}\n// create and fill assignment-variable, which contains info about tracks and lanes for each order-value\nfunction generateLaneAssignment() {\n let segmentNumber;\n let currentNodeIndex;\n let currentNodeIsForward;\n let currentNode;\n let previousNode;\n let previousNodeIsForward;\n // For each horizontal order slot, for each track number, holds the\n // SegmentAssignment object for the visit of that track to that order slot.\n // When an order slot is visited multiple times, holds whatever\n // SegmentAssignment was created most recently.\n const prevSegmentPerOrderPerTrack = [];\n // Index into assignments[order] by node, so addToAssignment can find an\n // existing entry without scanning the whole order slot.\n const assignmentByOrderAndNode = [];\n // create empty variables\n for (let i = 0; i <= maxOrder; i += 1) {\n assignments[i] = [];\n assignmentByOrderAndNode[i] = new Map();\n prevSegmentPerOrderPerTrack[i] = [];\n for (let j = 0; j < tracks.length; j += 1) {\n prevSegmentPerOrderPerTrack[i][j] = null;\n }\n }\n tracks.forEach((track, trackNo) => {\n // Trace along each track and create Segment objects in the track's path\n // field, and SegmentAssignment objects in NodeAssignment objects in all\n // the order slots that are visited by the track. Set up all the\n // cross-reverencing indexes and work out when we need segments to let\n // tracks pass nodes and turn around, but leave all the assigned lane\n // values empty for now.\n // add info for start of track\n currentNodeIndex = Math.abs(track.indexSequence[0]);\n currentNodeIsForward = isForwardIndex(track.indexSequence[0]);\n currentNode = nodes[currentNodeIndex];\n track.path = [];\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, 0, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber = 1;\n for (let i = 1; i < track.sequence.length; i += 1) {\n previousNode = currentNode;\n previousNodeIsForward = currentNodeIsForward;\n currentNodeIndex = Math.abs(track.indexSequence[i]);\n currentNodeIsForward = isForwardIndex(track.indexSequence[i]);\n currentNode = nodes[currentNodeIndex];\n if (currentNode.order > previousNode.order) {\n if (!previousNodeIsForward) {\n // backward to forward at previous node\n track.path.push({\n order: previousNode.order,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(previousNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n for (let j = previousNode.order + 1; j < currentNode.order; j += 1) {\n // forward without nodes\n track.path.push({\n order: j,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(j, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n if (!currentNodeIsForward) {\n // forward to backward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n // current Node forward\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n else if (currentNode.order < previousNode.order) {\n if (previousNodeIsForward) {\n // turnaround from fw to bw at previous node\n track.path.push({\n order: previousNode.order,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(previousNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n for (let j = previousNode.order - 1; j > currentNode.order; j -= 1) {\n // bachward without nodes\n track.path.push({\n order: j,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(j, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n if (currentNodeIsForward) {\n // backward to forward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n // backward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n else {\n if (currentNodeIsForward !== previousNodeIsForward) {\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: !currentNodeIsForward,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n }\n });\n // Now sweep left to right across order slots and assign vertical lanes to all the segments.\n for (let i = 0; i <= maxOrder; i += 1) {\n generateSingleLaneAssignment(assignments[i], i); // this is where the lanes get assigned\n }\n}\nfunction addToAssignment(order, nodeIndex, trackNo, segmentID, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode) {\n const segment = {\n trackID: trackNo,\n segmentID,\n compareToFromSame: prevSegmentPerOrderPerTrack[order][trackNo] ?? null,\n };\n // A null node means the track is passing through / turning around here rather\n // than visiting a node, so such segments never share a NodeAssignment.\n const existing = nodeIndex === null\n ? undefined\n : assignmentByOrderAndNode[order].get(nodeIndex);\n if (existing === undefined) {\n const assignment = {\n type: 'single',\n node: nodeIndex,\n tracks: [segment],\n };\n assignments[order].push(assignment);\n if (nodeIndex !== null) {\n assignmentByOrderAndNode[order].set(nodeIndex, assignment);\n }\n }\n else {\n // add to existing node in assignment\n existing.type = 'multiple';\n existing.tracks.push(segment);\n }\n prevSegmentPerOrderPerTrack[order][trackNo] = segment;\n}\n// looks at assignment and sets idealY and idealLane by looking at where the tracks come from\nfunction getIdealLanesAndCoords(assignment, order) {\n let index;\n assignment.forEach(node => {\n node.idealLane = 0;\n node.tracks.forEach(track => {\n if (track.segmentID === 0) {\n track.idealLane = track.trackID;\n track.idealY = null;\n }\n else {\n if (tracks[track.trackID].path[track.segmentID - 1].order ===\n order - 1) {\n track.idealLane =\n tracks[track.trackID].path[track.segmentID - 1].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[track.segmentID - 1].y;\n }\n else if (track.segmentID < tracks[track.trackID].path.length - 1 &&\n tracks[track.trackID].path[track.segmentID + 1].order === order - 1) {\n track.idealLane =\n tracks[track.trackID].path[track.segmentID + 1].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[track.segmentID + 1].y;\n }\n else {\n index = track.segmentID - 1;\n while (index >= 0 &&\n tracks[track.trackID].path[index].order !== order - 1) {\n index -= 1;\n }\n if (index < 0) {\n track.idealLane = track.trackID;\n track.idealY = null;\n }\n else {\n track.idealLane =\n tracks[track.trackID].path[index].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[index].y;\n }\n }\n }\n node.idealLane += track.idealLane;\n });\n node.idealLane /= node.tracks.length;\n });\n}\n// assigns the optimal lanes for a single horizontal position (=order)\n// first an ideal lane is calculated for each track (which is ~ the lane of its predecessor)\n// then the nodes are sorted by their average ideal lane\n// and the whole construct is then moved up or down if necessary\nfunction generateSingleLaneAssignment(assignment, order) {\n let currentLane = 0;\n const potentialAdjustmentValues = new Set();\n let currentY = 20;\n let prevNameIsNull = false;\n let prevTrack = -1;\n getIdealLanesAndCoords(assignment, order);\n assignment.sort(compareByIdealLane);\n assignment.forEach(node => {\n if (node.node !== null) {\n nodes[node.node].topLane = currentLane;\n if (prevNameIsNull)\n currentY -= 10;\n nodes[node.node].y = currentY;\n nodes[node.node].contentHeight = 0;\n prevNameIsNull = false;\n }\n else {\n if (prevNameIsNull)\n currentY -= 25;\n else if (currentY > 20)\n currentY -= 10;\n prevNameIsNull = true;\n }\n node.tracks.sort(compareByIdealLane);\n node.tracks.forEach(track => {\n track.lane = currentLane;\n if (track.trackID === prevTrack && node.node === null && prevNameIsNull) {\n currentY += 10;\n }\n tracks[track.trackID].path[track.segmentID].lane = currentLane;\n tracks[track.trackID].path[track.segmentID].y = currentY;\n if (track.idealY != null) {\n potentialAdjustmentValues.add(track.idealY - currentY);\n }\n currentLane += 1;\n currentY += tracks[track.trackID].width;\n if (node.node !== null) {\n nodes[node.node].contentHeight += tracks[track.trackID].width;\n }\n prevTrack = track.trackID;\n });\n currentY += 25;\n });\n adjustVertically(assignment, potentialAdjustmentValues);\n}\n// moves all tracks at a single horizontal location (=order) up/down to minimize lane changes\nfunction adjustVertically(assignment, potentialAdjustmentValues) {\n let verticalAdjustment = 0;\n let minAdjustmentCost = Number.MAX_SAFE_INTEGER;\n potentialAdjustmentValues.forEach(moveBy => {\n if (getVerticalAdjustmentCost(assignment, moveBy) < minAdjustmentCost) {\n minAdjustmentCost = getVerticalAdjustmentCost(assignment, moveBy);\n verticalAdjustment = moveBy;\n }\n });\n assignment.forEach(node => {\n if (node.node !== null) {\n nodes[node.node].y += verticalAdjustment;\n }\n node.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n seg.y = seg.y + verticalAdjustment;\n });\n });\n}\n// Budge down all nodes and out-of-node tracks below this node by this amount\nfunction adjustVertically3(node, adjustBy) {\n if (node.order < 0 || assignments[node.order] === undefined)\n return;\n assignments[node.order].forEach(assignmentNode => {\n if (assignmentNode.node !== null) {\n const aNode = nodes[assignmentNode.node];\n if (aNode !== node && aNode.y > node.y) {\n aNode.y += adjustBy;\n assignmentNode.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n seg.y = seg.y + adjustBy;\n });\n }\n }\n else {\n // track-segment not within a node\n assignmentNode.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n if (seg.y >= node.y) {\n seg.y = seg.y + adjustBy;\n }\n });\n }\n });\n if (nodesPerOrder[node.order].length > 0) {\n nodesPerOrder[node.order].forEach(nodeIndex => {\n if (nodes[nodeIndex] !== node && nodes[nodeIndex].y > node.y) {\n nodes[nodeIndex].y += adjustBy;\n }\n });\n }\n}\n// calculates cost of vertical adjustment as vertical distance * width of track\nfunction getVerticalAdjustmentCost(assignment, moveBy) {\n let result = 0;\n assignment.forEach(node => {\n node.tracks.forEach(track => {\n if (track.idealY != null && tracks[track.trackID].type !== 'read') {\n result +=\n Math.abs(track.idealY -\n moveBy -\n tracks[track.trackID].path[track.segmentID].y) * tracks[track.trackID].width;\n }\n });\n });\n return result;\n}\nfunction compareByIdealLane(a, b) {\n if (a.idealLane !== undefined) {\n if (b.idealLane !== undefined) {\n if (a.idealLane < b.idealLane)\n return -1;\n else if (a.idealLane > b.idealLane)\n return 1;\n return 0;\n }\n return -1;\n }\n if (b.idealLane !== undefined) {\n return 1;\n }\n return 0;\n}\nfunction compareNodesByOrder(a, b) {\n if (!a) {\n if (!b)\n return 0;\n return -1;\n }\n if (!b)\n return 1;\n if (a.order !== undefined) {\n if (b.order !== undefined) {\n if (a.order < b.order)\n return -1;\n else if (a.order > b.order)\n return 1;\n if (a.y !== undefined && b.y !== undefined) {\n if (a.y < b.y)\n return -1;\n else if (a.y > b.y)\n return 1;\n }\n return 0;\n }\n return -1;\n }\n if (b.order !== undefined)\n return 1;\n return 0;\n}\nfunction addTrackFeatures() {\n let nodeStart;\n let nodeEnd;\n let feature = {};\n bed.forEach(line => {\n let i = 0;\n while (i < tracks.length && tracks[i].name !== line.track)\n i += 1;\n if (i < tracks.length) {\n nodeStart = 0;\n tracks[i].path.forEach(node => {\n if (node.node !== null) {\n feature = {};\n nodeEnd = nodeStart + nodes[node.node].sequenceLength - 1;\n if (nodeStart >= line.start && nodeStart <= line.end) {\n feature.start = 0;\n }\n if (nodeStart < line.start && nodeEnd >= line.start) {\n feature.start = line.start - nodeStart;\n }\n if (nodeEnd <= line.end && nodeEnd >= line.start) {\n feature.end = nodeEnd - nodeStart;\n if (nodeEnd < line.end)\n feature.continue = true;\n }\n if (nodeEnd > line.end && nodeStart <= line.end) {\n feature.end = line.end - nodeStart;\n }\n if (feature.start !== undefined) {\n feature.type = line.type;\n feature.name = line.name;\n if (node.features === undefined)\n node.features = [];\n node.features.push(feature);\n }\n nodeStart = nodeEnd + 1;\n }\n });\n }\n });\n}\nfunction calculateTrackWidth() {\n // flag: if vg returns freq of 0 for all tracks, we will increase width manually\n let allAreFour = true;\n const NARROW_WIDTH = 4;\n const WIDE_WIDTH = config.trackWidth;\n tracks.forEach(track => {\n if (track.freq !== undefined) {\n // custom track width\n track.width = Math.round((Math.log(track.freq) + 1) * NARROW_WIDTH);\n }\n else {\n // default track width\n track.width = WIDE_WIDTH;\n if (track.type !== undefined && track.type === 'read') {\n track.width = NARROW_WIDTH;\n }\n }\n if (track.width !== NARROW_WIDTH) {\n allAreFour = false;\n }\n });\n if (allAreFour) {\n tracks.forEach(track => {\n if (track.freq !== undefined) {\n track.width = WIDE_WIDTH;\n }\n });\n }\n}\nfunction getReadXStart(read) {\n const seg = read.path[0];\n const node = nodes[seg.node];\n const offset = read.firstNodeOffset ?? 0;\n // read starts in forward direction, or backward from the node's far end\n return clampedXCoordinateOfBaseWithinNode(node, seg.isForward ? offset : node.sequenceLength - offset);\n}\nfunction getReadXEnd(read) {\n const seg = read.path[read.path.length - 1];\n const node = nodes[seg.node];\n const cover = read.finalNodeCoverLength ?? 0;\n // read ends in forward direction, or backward from the node's far end\n return clampedXCoordinateOfBaseWithinNode(node, seg.isForward ? cover : node.sequenceLength - cover);\n}\n// returns the x coordinate (in pixels) of (the left side) of the given base\n// position within the given node, or null if the position is past its end\nexport function getXCoordinateOfBaseWithinNode(node, base) {\n if (base > node.sequenceLength)\n return null; // equality is allowed\n return clampedXCoordinateOfBaseWithinNode(node, base);\n}\n// Same, but a base outside the node maps to the nearest node edge. Callers that\n// have to produce a coordinate no matter what use this: falling back to 0 would\n// put the shape at the far left of the whole image instead.\nexport function clampedXCoordinateOfBaseWithinNode(node, base) {\n const [nodeLeftX, nodeRightX] = nodePixelCoordinatesInX(node);\n if (node.sequenceLength === 0) {\n return nodeLeftX;\n }\n const clamped = Math.min(Math.max(base, 0), node.sequenceLength);\n return nodeLeftX + (clamped / node.sequenceLength) * (nodeRightX - nodeLeftX);\n}\n// transforms the info in the tracks' path attribute into actual coordinates\n// and saves them in shapes.rectangles and shapes.curves\nfunction generateSVGShapesFromPath() {\n let xStart;\n let xEnd;\n let yStart;\n let yEnd;\n let trackColor;\n let trackAlpha;\n let highlight;\n let dummy;\n let reversalFlag;\n for (let i = 0; i <= maxOrder; i += 1) {\n extraLeft.push(0);\n extraRight.push(0);\n }\n // generate x coords where each order starts and ends\n const orderStartX = [];\n const orderEndX = [];\n nodes.forEach(node => {\n if (node.x !== undefined) {\n orderStartX[node.order] = node.x;\n if (orderEndX[node.order] === undefined) {\n orderEndX[node.order] = node.x + node.pixelWidth;\n }\n else {\n orderEndX[node.order] = Math.max(orderEndX[node.order], node.x + node.pixelWidth);\n }\n }\n });\n // Helps generation of verticalRectangles, correct increments of extraRight and extraLeft\n tracks.sort(compareTrackByInitialOrdering);\n tracks.forEach(track => {\n highlight = 'plain';\n // Both are pure functions of (track, highlight); alpha doesn't even look at\n // highlight. Recomputing them per path segment was pure overhead.\n trackColor = generateTrackColor(track, highlight);\n trackAlpha = generateTrackAlpha(track);\n let colorHighlight = highlight;\n const colorForCurrentHighlight = () => {\n if (colorHighlight !== highlight) {\n colorHighlight = highlight;\n trackColor = generateTrackColor(track, highlight);\n }\n return trackColor;\n };\n // start of path\n yStart = track.path[0].y;\n if (track.type !== 'read') {\n if (track.sequence[0].startsWith('-')) {\n // The track starts with an inversed node\n xStart = orderEndX[track.path[0].order] + 20;\n }\n else {\n // The track starts with a forward node\n xStart = orderStartX[track.path[0].order] - 20;\n }\n }\n else {\n xStart = getReadXStart(track);\n }\n // middle of path\n for (let i = 0; i < track.path.length; i += 1) {\n if (track.path[i].y === yStart) {\n if (track.path[i].features !== undefined) {\n reversalFlag =\n i > 0 && track.path[i - 1].order === track.path[i].order;\n dummy = createFeatureRectangle(track.path[i], orderStartX[track.path[i].order], orderEndX[track.path[i].order], highlight, track, xStart, yStart, trackColor, reversalFlag);\n highlight = dummy.highlight;\n xStart = dummy.xStart;\n }\n }\n else {\n if (track.path[i - 1].isForward) {\n xEnd = orderEndX[track.path[i - 1].order];\n }\n else {\n xEnd = orderStartX[track.path[i - 1].order];\n }\n if (xEnd !== xStart) {\n trackColor = colorForCurrentHighlight();\n shapes.rectangles.push({\n xStart: Math.min(xStart, xEnd),\n yStart,\n xEnd: Math.max(xStart, xEnd),\n yEnd: yStart + track.width - 1,\n color: trackColor,\n alpha: trackAlpha,\n // TODO: This is not actually the index of the track!\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (track.path[i].order - 1 === track.path[i - 1].order) {\n // regular forward connection\n xStart = xEnd;\n xEnd = orderStartX[track.path[i].order];\n yEnd = track.path[i].y;\n trackColor = colorForCurrentHighlight();\n shapes.curves.push({\n xStart,\n yStart,\n xEnd: xEnd + 1,\n yEnd,\n width: track.width,\n color: trackColor,\n alpha: trackAlpha,\n laneChange: Math.abs(track.path[i].lane - track.path[i - 1].lane),\n id: track.id,\n name: track.name,\n type: track.type,\n nodeStart: track.path[i - 1]?.node,\n nodeEnd: track.path[i]?.node,\n });\n xStart = xEnd;\n yStart = yEnd;\n }\n else if (track.path[i].order + 1 === track.path[i - 1].order) {\n // regular backward connection\n xStart = xEnd;\n xEnd = orderEndX[track.path[i].order];\n yEnd = track.path[i].y;\n trackColor = colorForCurrentHighlight();\n shapes.curves.push({\n xStart: xStart + 1,\n yStart,\n xEnd,\n yEnd,\n width: track.width,\n color: trackColor,\n alpha: trackAlpha,\n laneChange: Math.abs(track.path[i].lane - track.path[i - 1].lane),\n id: track.id,\n name: track.name,\n type: track.type,\n nodeStart: track.path[i - 1]?.node,\n nodeEnd: track.path[i]?.node,\n });\n xStart = xEnd;\n yStart = yEnd;\n }\n else {\n // change of direction\n if (track.path[i - 1].isForward) {\n yEnd = track.path[i].y;\n generateTurnaround(1, xEnd, yStart, yEnd, track.width, trackColor, track.id, track.path[i].order, track.type, track.name);\n xStart = orderEndX[track.path[i].order];\n yStart = track.path[i].y;\n }\n else {\n yEnd = track.path[i].y;\n generateTurnaround(-1, xEnd, yStart, yEnd, track.width, trackColor, track.id, track.path[i].order, track.type, track.name);\n xStart = orderStartX[track.path[i].order];\n yStart = track.path[i].y;\n }\n }\n if (track.path[i].features !== undefined) {\n reversalFlag = track.path[i - 1].order === track.path[i].order;\n dummy = createFeatureRectangle(track.path[i], orderStartX[track.path[i].order], orderEndX[track.path[i].order], highlight, track, xStart, yStart, trackColor, reversalFlag);\n highlight = dummy.highlight;\n xStart = dummy.xStart;\n }\n }\n }\n // ending edges\n if (track.type !== 'read') {\n if (!track.path[track.path.length - 1].isForward) {\n // The track ends with an inversed node\n xEnd = orderStartX[track.path[track.path.length - 1].order] - 20;\n }\n else {\n // The track ends with a forward node\n xEnd = orderEndX[track.path[track.path.length - 1].order] + 20;\n }\n }\n else {\n xEnd = getReadXEnd(track);\n }\n shapes.rectangles.push({\n xStart: Math.min(xStart, xEnd),\n yStart,\n xEnd: Math.max(xStart, xEnd),\n yEnd: yStart + track.width - 1,\n color: trackColor,\n alpha: trackAlpha,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n });\n}\n// Sankey-mode synthesis: one synthetic \"read\" per (srcSigned \u2192 dstSigned) edge,\n// fed through the normal placeReads/generateSVGShapesFromPath pipeline so the\n// band gets the same lane assignment, loop topology, bezier shape, AND color\n// scheme as a real read \u2014 colors come from generateTrackColor's natural\n// `id % palette.length` rotation, no custom palette needed.\n//\n// Synthetic-track ids live above this base so they don't collide with real\n// track ids and so click/hover handlers can show count info instead of the\n// per-read info dialog.\nconst COARSENED_ID_BASE = 1_000_000_000;\nexport const isCoarsenedId = (id) => id >= COARSENED_ID_BASE;\n// Build one synthetic read per (srcSigned \u2192 dstSigned) edge in `source`\n// (real reads, or the alt haplotype tracks). The synthetic reads replace the\n// real ones BEFORE placeReads runs, so:\n// \u2022 placeReads sees N_edges reads instead of N_reads (huge node-height win),\n// \u2022 each band gets a lane and an \"right-down-left-up-right\" loop topology\n// for free (placeReads handles reversals and out-of-order jumps),\n// \u2022 generateSVGShapesFromPath emits exactly one TrackCurve per band using\n// the same bezier as a real read \u2014 no special draw path needed.\n//\n// The synthetic track's `sequence` is just the two signed node names. After\n// generateTrackIndexSequences runs, indexSequence is set and the rest of the\n// layout follows.\nfunction buildCoarsenedSyntheticBands(source, unit) {\n coarsenedEdgeMeta = new Map();\n const edges = new Map();\n // When ignoring strand, (+A\u2192+B) and (-B\u2192-A) refer to the same underlying\n // graph connection \u2014 collapse both into one canonical key. We pick the\n // lexicographically smaller of the two orientations so a stable canonical\n // form is chosen; whichever orientation is seen first determines the\n // visual band direction.\n const ignoreStrand = config.ignoreStrand;\n for (const item of source) {\n const seq = item.indexSequence;\n if (!seq || seq.length < 2)\n continue;\n for (let i = 0; i < seq.length - 1; i += 1) {\n const sSigned = seq[i];\n const dSigned = seq[i + 1];\n const sIdx = Math.abs(sSigned);\n const dIdx = Math.abs(dSigned);\n const srcNode = nodes[sIdx];\n const dstNode = nodes[dIdx];\n if (!srcNode || !dstNode)\n continue;\n let key;\n if (ignoreStrand) {\n const a = `${sSigned}>${dSigned}`;\n const b = `${-dSigned}>${-sSigned}`;\n key = a < b ? a : b;\n }\n else {\n key = `${sSigned}>${dSigned}`;\n }\n const existing = edges.get(key);\n if (existing === undefined) {\n edges.set(key, {\n sSigned,\n dSigned,\n sName: sSigned < 0 ? `-${srcNode.name}` : srcNode.name,\n dName: dSigned < 0 ? `-${dstNode.name}` : dstNode.name,\n count: 1,\n sourceTrackID: item.sourceTrackID,\n });\n }\n else {\n existing.count += 1;\n }\n }\n }\n // Square-root scaling on count gives heavy edges visible weight without\n // letting one massive edge dwarf everything else. Min stays near READ_WIDTH\n // so single-read edges still look like a normal read; max is generous so\n // hot edges read as obvious \"highways.\"\n let maxEdgeCount = 0;\n for (const e of edges.values()) {\n if (e.count > maxEdgeCount)\n maxEdgeCount = e.count;\n }\n const BAND_MIN_WIDTH = READ_WIDTH;\n const BAND_MAX_WIDTH = 60;\n const widthForCount = (c) => {\n if (maxEdgeCount <= 1)\n return BAND_MIN_WIDTH;\n return (BAND_MIN_WIDTH +\n (BAND_MAX_WIDTH - BAND_MIN_WIDTH) * Math.sqrt(c / maxEdgeCount));\n };\n const synthetic = [];\n let i = 0;\n for (const edge of edges.values()) {\n const id = COARSENED_ID_BASE + i;\n const label = `${edge.count.toLocaleString()} ${unit}${edge.count === 1 ? '' : 's'}: Node ${edge.sName} \u2192 Node ${edge.dName}`;\n coarsenedEdgeMeta.set(id, { count: edge.count, label });\n // Place each synthetic band so it touches *only* its endpoint nodes' edges:\n // \u2022 firstNodeOffset = src.sequenceLength \u2192 curve exits at src's right\n // pixel edge, no rectangle drawn inside src.\n // \u2022 finalNodeCoverLength = 0 \u2192 curve enters at dst's left pixel edge,\n // no rectangle drawn inside dst.\n // The crucial side effect is in placeReadSet's conflict check: at any\n // node B with an incoming edge (A\u2192B) and an outgoing edge (B\u2192C), the\n // incoming's finalNodeCoverLength=0 and the outgoing's firstNodeOffset=\n // B.length leave them with no horizontal overlap, so they share a single\n // lane. Node heights collapse from ~(N_in + N_out) lanes down to\n // ~max(N_in, N_out) lanes.\n //\n // Note: reverseReversedReads will swap firstNodeOffset/finalNodeCoverLength\n // for fully-reverse synthetics. The math works out: pre-flip values of\n // (srcLen, 0) become post-flip (newSrcLen, 0), which is the same pattern\n // \u2014 bands stay edge-only regardless of orientation.\n const srcLen = nodes[Math.abs(edge.sSigned)]?.sequenceLength ?? 0; // node may be absent for a dangling edge\n synthetic.push({\n id,\n sourceTrackID: edge.sourceTrackID,\n type: 'read',\n // Human-readable so the name surfaces sensibly in any UI that uses it\n // (right-click \u2192 add to group, etc.) \u2014 and so trackTooltipText fallback\n // shows something meaningful.\n name: label,\n sequence: [edge.sName, edge.dName],\n indexSequence: [],\n path: [],\n // Pre-set width so placeReads / placeReadSet allocate the right vertical\n // space and assignReadsToNodes (patched to honor pre-set widths) leaves\n // it alone.\n width: widthForCount(edge.count),\n firstNodeOffset: srcLen,\n finalNodeCoverLength: 0,\n sequenceNew: [\n { nodeName: edge.sName, mismatches: [] },\n { nodeName: edge.dName, mismatches: [] },\n ],\n mapping_quality: 0,\n is_secondary: false,\n sample_name: null,\n read_group: null,\n score: edge.count,\n });\n i += 1;\n }\n // Single-line debug summary so the user can verify node sizing.\n let tallestName = '?';\n let tallestH = 0;\n for (const n of nodes) {\n if (!n)\n continue;\n const h = n.contentHeight;\n if (h > tallestH) {\n tallestH = h;\n tallestName = n.name;\n }\n }\n if (DEBUG) {\n console.log(`[coarsened] ${unit}s_in=${source.length} edges_out=${synthetic.length} ` +\n `tallestNodeBeforePlace=${tallestName}(${tallestH.toFixed(1)})`);\n }\n return synthetic;\n}\nfunction createFeatureRectangle(node, nodeXStart, nodeXEnd, highlight, track, rectXStart, yStart, trackColor, reversalFlag) {\n let currentHighlight = highlight;\n let c;\n let co;\n let featureXStart;\n let featureXEnd;\n nodeXStart -= 8;\n nodeXEnd += 8;\n const nodeWidth = nodes[node.node].sequenceLength;\n node.features.sort((a, b) => a.start - b.start);\n node.features.forEach(feature => {\n if (currentHighlight !== feature.type) {\n // finish incoming rectangle\n c = generateTrackColor(track, currentHighlight);\n if (node.isForward) {\n featureXStart =\n nodeXStart +\n Math.round((feature.start * (nodeXEnd - nodeXStart + 1)) / nodeWidth);\n // overwrite narrow post-inversion rectangle if highlight starts near beginning of node\n if (reversalFlag && featureXStart < nodeXStart + 8) {\n featureXEnd =\n nodeXStart +\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n co = generateTrackColor(track, feature.type);\n shapes.featureRectangles.push({\n xStart: featureXStart,\n yStart,\n xEnd: featureXEnd,\n yEnd: yStart + track.width - 1,\n color: co,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (featureXStart > rectXStart + 1) {\n shapes.featureRectangles.push({\n xStart: rectXStart,\n yStart,\n xEnd: featureXStart - 1,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n }\n else {\n featureXStart =\n nodeXEnd -\n Math.round((feature.start * (nodeXEnd - nodeXStart + 1)) / nodeWidth);\n // overwrite narrow post-inversion rectangle if highlight starts near beginning of node\n if (reversalFlag && featureXStart > nodeXEnd - 8) {\n featureXEnd =\n nodeXEnd -\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n co = generateTrackColor(track, feature.type);\n shapes.featureRectangles.push({\n xStart: featureXEnd,\n yStart,\n xEnd: featureXStart,\n yEnd: yStart + track.width - 1,\n color: co,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (rectXStart > featureXStart + 1) {\n shapes.featureRectangles.push({\n xStart: featureXStart + 1,\n yStart,\n xEnd: rectXStart,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n }\n rectXStart = featureXStart;\n currentHighlight = feature.type;\n }\n if (feature.end < nodeWidth - 1 || feature.continue === undefined) {\n // finish internal rectangle\n c = generateTrackColor(track, currentHighlight);\n if (node.isForward) {\n featureXEnd =\n nodeXStart +\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n shapes.featureRectangles.push({\n xStart: rectXStart,\n yStart,\n xEnd: featureXEnd,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n else {\n featureXEnd =\n nodeXEnd -\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n shapes.featureRectangles.push({\n xStart: featureXEnd,\n yStart,\n xEnd: rectXStart,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n rectXStart = featureXEnd + 1;\n currentHighlight = 'plain';\n }\n });\n return { xStart: rectXStart, highlight: currentHighlight };\n}\nconst MIN_BEND_WIDTH = 7;\n// Emit the vertical rectangles + rounded corners for a track turning around at\n// one end of an order slot. The two directions are mirror images of each other:\n// a forward-to-reverse turn bulges out to the right of the node and consumes an\n// `extraRight` slot, a reverse-to-forward turn bulges out to the left and\n// consumes an `extraLeft` slot. `dir` is +1 for right, -1 for left.\nfunction generateTurnaround(dir, x, yStart, yEnd, trackWidth, trackColor, trackID, order, type, trackName) {\n const extra = dir === 1 ? extraRight : extraLeft;\n const offset = 10 * extra[order];\n // The turn's apex, and the outward direction from it.\n const apex = x + dir * (offset + 5);\n const radius = MIN_BEND_WIDTH;\n const stem = Math.min(MIN_BEND_WIDTH, trackWidth);\n const yTop = Math.min(yStart, yEnd);\n const yBottom = Math.max(yStart, yEnd);\n // The incoming/outgoing stubs run from the node's edge out to the apex. The\n // one-pixel asymmetry between the two directions is carried over verbatim\n // from the original pair of functions.\n const stubNear = dir === 1 ? x : apex - 1;\n const stubFar = dir === 1 ? apex : x;\n const horizontal = (segTop) => {\n shapes.verticalRectangles.push({\n xStart: stubNear,\n yStart: segTop,\n xEnd: stubFar,\n yEnd: segTop + trackWidth - 1,\n color: trackColor,\n id: trackID,\n name: trackName,\n type,\n });\n };\n // elongate the incoming and outgoing rectangles a bit past the node\n horizontal(yStart);\n shapes.verticalRectangles.push({\n xStart: dir === 1 ? apex + radius : apex - radius - stem,\n yStart: yTop + trackWidth + radius - 1,\n xEnd: dir === 1 ? apex + radius + stem - 1 : apex - radius - 1,\n yEnd: yBottom - radius + 1,\n color: trackColor,\n id: trackID,\n name: trackName,\n type,\n });\n horizontal(yEnd);\n const inner = apex + dir * radius;\n const outer = apex + dir * (radius + stem);\n // bottom 90 degree bend\n let d = `M ${apex} ${yBottom}`;\n d += ` Q ${inner} ${yBottom} ${inner} ${yBottom - radius}`;\n d += ` H ${outer}`;\n d += ` Q ${outer} ${yBottom + trackWidth} ${apex} ${yBottom + trackWidth}`;\n d += ' Z ';\n shapes.corners.push({ path: d, color: trackColor, id: trackID, type });\n // top 90 degree bend\n d = `M ${apex} ${yTop}`;\n d += ` Q ${outer} ${yTop} ${outer} ${yTop + trackWidth + radius}`;\n d += ` H ${inner}`;\n d += ` Q ${inner} ${yTop + trackWidth} ${apex} ${yTop + trackWidth}`;\n d += ' Z ';\n shapes.corners.push({ path: d, color: trackColor, id: trackID, type });\n extra[order] += 1;\n}\nexport function nodePixelCoordinatesInX(node) {\n // Add and subtract 4 to account for stroke width - TODO: figure out what the 4 means\n const nodeLeftX = node.x - 4;\n const nodeRightX = node.x + node.pixelWidth + 4;\n return [nodeLeftX, nodeRightX];\n}\n// calculate node widths depending on sequence lengths and chosen calculation method\nfunction generateNodeWidth() {\n // Promote the input's optional sequenceLength; Node declares it required, so\n // widen to InputNode to make the still-possibly-undefined read explicit.\n nodes.forEach((node) => {\n if (node.sequenceLength === undefined) {\n node.sequenceLength = node.seq.length;\n }\n });\n switch (config.nodeWidthOption) {\n case 'compressed':\n nodes.forEach(node => {\n node.width = 1 + Math.log(node.sequenceLength) / Math.log(2);\n node.pixelWidth = Math.round((node.width - 1) * 8.401);\n });\n break;\n case 'small':\n nodes.forEach(node => {\n node.width = node.sequenceLength / 100;\n node.pixelWidth = Math.round((node.width - 1) * 8.401);\n });\n break;\n case 'fixed':\n // when there's no reads in the node, it should be a little wider\n nodes.forEach(node => {\n node.width = 10;\n node.pixelWidth = Math.round(node.width * 8.401);\n });\n break;\n case 'normal': {\n // The sequence is drawn in a monospace font, so one character's width,\n // which the caller measures, sizes every node.\n const charWidth = config.charWidth;\n nodes.forEach(node => {\n node.width = node.sequenceLength;\n const len = node.seq?.length ?? 1;\n node.pixelWidth = Math.round(charWidth * len);\n });\n break;\n }\n default:\n throw new Error(`${config.nodeWidthOption} not implemented`);\n }\n}\n// remove redundant nodes\n// two nodes A and B can be merged if all tracks leaving A go directly into B\n// and all tracks entering B come directly from A\n// (plus no inversions involved)\nfunction mergeNodes() {\n let nodeName;\n let nodeName2;\n // One set per node index, including the hole at 0 (Array.from materializes\n // holes, unlike forEach/map).\n const predSets = Array.from(nodes, () => new Set());\n const succSets = Array.from(nodes, () => new Set());\n let tracksAndReads;\n if (config.showReads && reads.length > 0)\n tracksAndReads = tracks.concat(reads);\n else\n tracksAndReads = tracks;\n tracksAndReads.forEach(track => {\n for (let i = 0; i < track.sequence.length; i += 1) {\n if (!isReverse(track.sequence[i])) {\n // forward Node\n if (i > 0) {\n nodeName = track.sequence[i - 1];\n predSets[nodeMap.get(track.sequence[i])].add(nodeName);\n if (isReverse(nodeName)) {\n // add 2 predecessors, to make sure there is no node merging in this case\n predSets[nodeMap.get(track.sequence[i])].add(forward(nodeName));\n }\n }\n else if (track.type === 'haplotype') {\n predSets[nodeMap.get(track.sequence[i])].add('None');\n }\n if (i < track.sequence.length - 1) {\n nodeName = track.sequence[i + 1];\n succSets[nodeMap.get(track.sequence[i])].add(nodeName);\n if (isReverse(nodeName)) {\n // add 2 successors, to make sure there is no node merging in this case\n succSets[nodeMap.get(track.sequence[i])].add(forward(nodeName));\n }\n }\n else if (track.type === 'haplotype') {\n succSets[nodeMap.get(track.sequence[i])].add('None');\n }\n }\n else {\n // reverse Node\n nodeName = forward(track.sequence[i]);\n if (i > 0) {\n nodeName2 = track.sequence[i - 1];\n if (isReverse(nodeName2)) {\n succSets[nodeMap.get(nodeName)].add(forward(nodeName2));\n }\n else {\n // add 2 successors, to make sure there is no node merging in this case\n succSets[nodeMap.get(nodeName)].add(nodeName2);\n succSets[nodeMap.get(nodeName)].add(reverse(nodeName2));\n }\n }\n else if (track.type === 'haplotype') {\n succSets[nodeMap.get(nodeName)].add('None');\n }\n if (i < track.sequence.length - 1) {\n nodeName2 = track.sequence[i + 1];\n if (isReverse(nodeName2)) {\n predSets[nodeMap.get(nodeName)].add(forward(nodeName2));\n }\n else {\n predSets[nodeMap.get(nodeName)].add(nodeName2);\n predSets[nodeMap.get(nodeName)].add(reverse(nodeName2));\n }\n }\n else if (track.type === 'haplotype') {\n predSets[nodeMap.get(nodeName)].add('None');\n }\n }\n }\n });\n // convert sets to arrays\n const pred = predSets.map(s => Array.from(s));\n const succ = succSets.map(s => Array.from(s));\n // update reads which pass through merging nodes\n if (config.showReads && reads.length > 0) {\n // sort nodes by order, then by y-coordinate\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n // iterate over all nodes and calculate their position within the new merged node\n // Both maps carry an entry for each orientation of every node name (\"7\"\n // and \"-7\"), because the read update below looks up whichever orientation\n // the read happens to visit.\n const mergeOffset = new Map();\n // maps to leftmost node of a node's \"merging cascade\"\n const mergeOrigin = new Map();\n const offsetOf = (nodeName) => {\n const offset = mergeOffset.get(nodeName);\n if (offset === undefined) {\n throw new Error(`No merge offset recorded for node ${nodeName}`);\n }\n return offset;\n };\n const originOf = (nodeName) => {\n const origin = mergeOrigin.get(nodeName);\n if (origin === undefined) {\n throw new Error(`No merge origin recorded for node ${nodeName}`);\n }\n return origin;\n };\n sortedNodes.forEach(node => {\n const predecessor = mergeableWithPred(nodeMap.get(node.name), pred, succ);\n if (predecessor) {\n // Nodes are visited in order, so the predecessor of a merge cascade is\n // always recorded before the node that follows it.\n const offset = offsetOf(predecessor) + nodeByName(predecessor).sequenceLength;\n mergeOffset.set(node.name, offset);\n mergeOffset.set(reverse(node.name), offset);\n mergeOrigin.set(node.name, originOf(predecessor));\n mergeOrigin.set(reverse(node.name), originOf(predecessor));\n }\n else {\n mergeOffset.set(node.name, 0);\n mergeOffset.set(reverse(node.name), 0);\n mergeOrigin.set(node.name, node.name);\n mergeOrigin.set(reverse(node.name), node.name);\n }\n });\n reads.forEach(read => {\n const sequenceNew = read.sequenceNew ?? [];\n read.firstNodeOffset =\n (read.firstNodeOffset ?? 0) + offsetOf(read.sequence[0]);\n read.finalNodeCoverLength =\n (read.finalNodeCoverLength ?? 0) +\n offsetOf(read.sequence[read.sequence.length - 1]);\n for (let i = read.sequence.length - 1; i >= 0; i -= 1) {\n const nodeName = forward(read.sequence[i]);\n const predecessor = mergeableWithPred(nodeMap.get(nodeName), pred, succ);\n if (predecessor &&\n mergeableWithSucc(nodeMap.get(predecessor), pred, succ)) {\n if (i > 0) {\n read.sequence.splice(i, 1);\n // adjust position of mismatches\n const predLength = nodeByName(predecessor).sequenceLength;\n sequenceNew[i].mismatches.forEach(mismatch => {\n mismatch.pos += predLength;\n });\n // append mismatches to previous entry's mismatches\n sequenceNew[i - 1].mismatches = sequenceNew[i - 1].mismatches.concat(sequenceNew[i].mismatches);\n sequenceNew.splice(i, 1);\n }\n else {\n read.sequence[0] = originOf(read.sequence[0]);\n const firstEntry = sequenceNew[0];\n const firstOffset = offsetOf(firstEntry.nodeName);\n firstEntry.mismatches.forEach(mismatch => {\n mismatch.pos += firstOffset;\n });\n firstEntry.nodeName = originOf(firstEntry.nodeName);\n }\n }\n }\n });\n }\n // update node sequences + sequence lengths\n for (let i = 0; i < nodes.length; i += 1) {\n if (mergeableWithSucc(i, pred, succ) && !mergeableWithPred(i, pred, succ)) {\n let donor = i;\n while (mergeableWithSucc(donor, pred, succ)) {\n donor = nodeMap.get(forward(succ[donor][0]));\n nodes[i].sequenceLength += nodes[donor].sequenceLength;\n nodes[i].seq += nodes[donor].seq;\n }\n }\n }\n // actually merge the nodes by removing the corresponding nodes from track data\n tracks.forEach(track => {\n for (let i = track.sequence.length - 1; i >= 0; i -= 1) {\n nodeName = forward(track.sequence[i]);\n const nodeIndex = nodeMap.get(nodeName);\n if (mergeableWithPred(nodeIndex, pred, succ)) {\n track.sequence.splice(i, 1);\n }\n }\n });\n // remove the nodes from node-array\n for (let i = nodes.length - 1; i >= 0; i -= 1) {\n if (mergeableWithPred(i, pred, succ)) {\n nodes.splice(i, 1);\n }\n }\n}\nfunction mergeableWithPred(index, pred, succ) {\n if (pred[index].length !== 1)\n return false;\n if (pred[index][0] === 'None')\n return false;\n const predecessor = forward(pred[index][0]);\n const predecessorIndex = nodeMap.get(predecessor);\n if (succ[predecessorIndex].length !== 1)\n return false;\n if (succ[predecessorIndex][0] === 'None')\n return false;\n return predecessor;\n}\nfunction mergeableWithSucc(index, pred, succ) {\n if (succ[index].length !== 1)\n return false;\n if (succ[index][0] === 'None')\n return false;\n const successor = forward(succ[index][0]);\n const successorIndex = nodeMap.get(successor);\n if (pred[successorIndex].length !== 1)\n return false;\n if (pred[successorIndex][0] === 'None')\n return false;\n return true;\n}\nfunction filterReads(readList) {\n const focusNames = config.focusReadNames\n ? new Set(config.focusReadNames)\n : null;\n return readList.filter(read => read.is_secondary !== true &&\n (read.mapping_quality ?? 0) >= config.mappingQualityCutoff &&\n (focusNames === null ||\n (read.name !== undefined && focusNames.has(read.name))));\n}\n", "function groupBy(items, key) {\n const groups = new Map();\n for (const item of items) {\n const k = key(item);\n const group = groups.get(k);\n if (group === undefined) {\n groups.set(k, [item]);\n }\n else {\n group.push(item);\n }\n }\n return groups;\n}\nfunction compareCurvesByXYStartValue(a, b) {\n if (a.xStart < b.xStart)\n return 1;\n else if (a.xStart > b.xStart)\n return -1;\n else if (a.yStart > b.yStart)\n return 1;\n else if (a.yStart < b.yStart)\n return -1;\n return 0;\n}\n// The curves of one track type in draw order, each with its `path` set. Curves\n// between the same pair of nodes form a group whose control points fan out, so\n// a bundle of tracks changing lanes together stays parallel rather than\n// crossing at one x.\nexport function curvePaths(curves, type) {\n const groupedCurves = groupBy(curves.filter(curve => curve.type === type), curve => `${curve.nodeStart},${curve.nodeEnd}`);\n groupedCurves.forEach(curveGroup => {\n // Control point adjustment range: 30% to 70% of the original x values\n let adjustValue = 0.3;\n let adjustIncrement = 0.4 / curveGroup.length;\n // Ignore Beziar Curve skewing when a group is not too big\n if (curveGroup.length <= 5) {\n adjustValue = 0.5;\n adjustIncrement = 0;\n }\n curveGroup.sort(compareCurvesByXYStartValue);\n curveGroup.forEach(curve => {\n let xAdjusted;\n // NextAdjusted is used to try to draw a parallel curve on the way back\n let xNextAdjusted;\n if (curve.yStart < curve.yEnd) {\n xAdjusted =\n curve.xStart + (curve.xEnd - curve.xStart) * (1 - adjustValue);\n adjustValue += adjustIncrement;\n xNextAdjusted =\n curve.xStart + (curve.xEnd - curve.xStart) * (1 - adjustValue);\n }\n else {\n xAdjusted = curve.xStart + (curve.xEnd - curve.xStart) * adjustValue;\n adjustValue += adjustIncrement;\n xNextAdjusted = curve.xStart + (curve.xEnd - curve.xStart) * adjustValue;\n }\n let d = `M ${curve.xStart} ${curve.yStart}`;\n d += ` C ${xAdjusted} ${curve.yStart} ${xAdjusted} ${curve.yEnd} ${curve.xEnd} ${curve.yEnd}`;\n d += ` V ${curve.yEnd + curve.width}`;\n d += ` C ${xNextAdjusted} ${curve.yEnd + curve.width} ${xNextAdjusted} ${curve.yStart + curve.width} ${curve.xStart} ${curve.yStart + curve.width}`;\n d += ' Z';\n curve.path = d;\n });\n });\n // One flat list ordered by group, then by the within-group sort\n const groupKeys = Array.from(groupedCurves.keys());\n groupKeys.sort(function (a, b) {\n const aParts = a.split(',').map(Number);\n const bParts = b.split(',').map(Number);\n const a0 = aParts[0] ?? 0;\n const a1 = aParts[1] ?? 0;\n const b0 = bParts[0] ?? 0;\n const b1 = bParts[1] ?? 0;\n if (a0 < b0) {\n return -1;\n }\n else if (a0 > b0) {\n return 1;\n }\n else {\n if (a1 < b1) {\n return -1;\n }\n else if (a1 > b1) {\n return 1;\n }\n else {\n return 0;\n }\n }\n });\n const flattenedGroups = [];\n for (const key of groupKeys) {\n const group = groupedCurves.get(key);\n if (group !== undefined) {\n flattenedGroups.push(...group);\n }\n }\n return flattenedGroups;\n}\n// A node's outline: a rounded box 9 units outside the node's x extent and its\n// content height, the tracks through it inside.\nexport function nodeOutlinePath(node) {\n // top left arc\n let d = `M ${node.x - 9} ${node.y} Q ${node.x - 9} ${node.y - 9} ${node.x} ${node.y - 9}`;\n let x = node.x;\n let y = node.y - 9;\n // top straight\n if (node.width > 1) {\n x += node.pixelWidth;\n d += ` L ${x} ${y}`;\n }\n // top right arc\n d += ` Q ${x + 9} ${y} ${x + 9} ${y + 9}`;\n x += 9;\n y += 9;\n // right straight\n if (node.contentHeight > 0) {\n y += node.contentHeight - 0;\n d += ` L ${x} ${y}`;\n }\n // bottom right arc\n d += ` Q ${x} ${y + 9} ${x - 9} ${y + 9}`;\n x -= 9;\n y += 9;\n // bottom straight\n if (node.width > 1) {\n x -= node.pixelWidth;\n d += ` L ${x} ${y}`;\n }\n // bottom left arc\n d += ` Q ${x - 9} ${y} ${x - 9} ${y - 9}`;\n x -= 9;\n y -= 9;\n // left straight\n if (node.contentHeight > 0) {\n y -= node.contentHeight - 0;\n d += ` L ${x} ${y}`;\n }\n return d;\n}\n", "import { layoutTubeMap } from '@gmod/tubemap-core'\n\nimport { isBackbone } from '../anchoredNodes'\nimport { pathOrigin } from '../pathAnchoring'\nimport { pathCssColor, pathGreyCssColor } from '../pathColors'\nimport {\n canonicalStrand,\n readColor,\n stepName,\n tubeMapReads,\n} from '../tubeMap/reads'\n\nimport type { Coarsened } from '../tubeMap/coarsen'\nimport type { Graph, LayoutResult, NodeSegment } from '../types'\nimport type {\n InputNode,\n InputTrack,\n LayoutNode,\n TubeMapLayout,\n} from '@gmod/tubemap-core'\n\n// The sequenceTubeMap drawing of a graph's paths: every P or W line a tube,\n// every node a box the tubes pass through, laid out by @gmod/tubemap-core. The\n// canvas draws none of it; TubeMapOverlay draws the shapes, and nodePositions\n// is each box's centreline, for the hit test, the fit and the labels.\n//\n// Two x axes. `own` is the tube map's: columns in node order, a node as wide as\n// log2 of its length, which is the compact picture sequenceTubeMap draws.\n// `reference` pins each column to the reference bp its backbone node covers,\n// so the tubes line up with the linear view's other tracks; the overlay makes\n// room for the curves between columns when it draws (tubeMapWarp.ts).\n\n// A column of the tube map: the nodes at one `order`, the tube x they were\n// drawn over, and under the reference axis the reference bp they stand for.\nexport interface TubeMapColumn {\n order: number\n x0: number\n x1: number\n bp0: number\n bp1: number\n}\n\nexport interface TubeMapDrawing {\n layout: TubeMapLayout\n // each path's tube colour, in the order the graph states its paths\n pathColors: string[]\n // tube y minus this is the drawing's y, so the drawing starts at 0\n yOffset: number\n // set on the reference axis, sorted by order\n columns?: TubeMapColumn[]\n // set when the cut was folded before layout (coarsen.ts): the drawing's node\n // ids are the coarse graph's, not the cut's\n coarse?: Coarsened\n}\n\n// Strand of every visit a path makes to a segment, in walk order, so the k-th\n// time a path reaches a segment reads the k-th entry. Visits name a path\n// without the range suffix `odgi extract` appends (pathOrigin).\nexport function visitStrands(graph: Graph) {\n const strands = new Map<string, ('+' | '-')[]>()\n for (const [segment, visits] of graph.pathVisits ?? []) {\n for (const visit of visits) {\n const key = `${visit.path}\\t${segment}`\n const list = strands.get(key)\n if (list) {\n list.push(visit.strand)\n } else {\n strands.set(key, [visit.strand])\n }\n }\n }\n return strands\n}\n\n// A tube map track per path, the reference first: the layout straightens\n// track 0 and orders every other node around it. A step reads its node in\n// reverse where the path walks the segment against the strand the node is\n// drawn in.\nexport function tubeMapTracks(graph: Graph): InputTrack[] {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const strands = visitStrands(graph)\n const paths = (graph.paths ?? []).map((path, index) => ({ path, index }))\n const reference = paths.findIndex(\n p => pathOrigin(p.path.name).name === graph.referencePath,\n )\n if (reference > 0) {\n paths.unshift(...paths.splice(reference, 1))\n }\n return paths.flatMap(({ path, index }) => {\n const visitName = pathOrigin(path.name).name\n const seen = new Map<string, number>()\n const sequence = path.nodeIds.flatMap(id => {\n const node = nodeById.get(id)\n if (!node) {\n return []\n }\n const k = seen.get(node.name) ?? 0\n seen.set(node.name, k + 1)\n const strand =\n strands.get(`${visitName}\\t${node.name}`)?.[k] ?? canonicalStrand(node)\n return [stepName(node, strand)]\n })\n return sequence.length > 0\n ? [\n {\n id: index,\n name: path.name,\n sequence,\n sourceTrackID: 0,\n type: 'haplotype' as const,\n },\n ]\n : []\n })\n}\n\nfunction tubeMapNodes(graph: Graph): InputNode[] {\n return graph.nodes.map(n => ({\n name: n.id,\n seq: '',\n sequenceLength: Math.max(1, n.length),\n }))\n}\n\nexport function hasTubeMapPaths(graph: Graph) {\n return (graph.paths?.length ?? 0) > 0\n}\n\n// Tubes narrow as paths are added, so ninety haplotypes stack about as tall as\n// forty at full width rather than filling a screen and a half each.\nconst STACK_PX = 600\nconst MIN_TUBE_PX = 3\nconst MAX_TUBE_PX = 15\n\nexport function tubeWidth(trackCount: number) {\n return Math.max(\n MIN_TUBE_PX,\n Math.min(MAX_TUBE_PX, Math.floor(STACK_PX / Math.max(1, trackCount))),\n )\n}\n\nfunction runTubeMap(graph: Graph) {\n const paths = graph.paths ?? []\n const tracks = tubeMapTracks(graph)\n const reads = graph.reads\n ? tubeMapReads(graph, graph.reads, paths.length)\n : []\n const tubeColor = reads.length > 0 ? pathGreyCssColor : pathCssColor\n const pathColors = paths.map((_, i) => tubeColor(i, paths.length))\n const layout = layoutTubeMap(tubeMapNodes(graph), tracks, reads, {\n nodeWidthOption: 'compressed',\n trackWidth: tubeWidth(tracks.length),\n trackColor: track =>\n track.type === 'read' ? readColor(track) : pathColors[track.id]!,\n })\n return layout ? { layout, pathColors } : undefined\n}\n\n// Drawn nodes only: an unreached one has no x.\nfunction drawnNodes(layout: TubeMapLayout) {\n const nodes: LayoutNode[] = []\n layout.nodes.forEach(node => {\n if (node.order >= 0) {\n nodes.push(node)\n }\n })\n return nodes\n}\n\nfunction centreline(node: LayoutNode, yOffset: number, x0: number, x1: number) {\n const y = node.y + node.contentHeight / 2 - yOffset\n return [\n { x: x0, y },\n { x: x1, y },\n ]\n}\n\nfunction extentOf(layout: TubeMapLayout, yOffset: number) {\n return {\n minY: layout.bounds.minY - yOffset,\n maxY: layout.bounds.maxY - yOffset,\n }\n}\n\nexport function tubeMapLayout(graph: Graph): LayoutResult | undefined {\n const run = hasTubeMapPaths(graph) ? runTubeMap(graph) : undefined\n if (!run) {\n return undefined\n }\n const { layout, pathColors } = run\n const yOffset = layout.bounds.minY\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const node of drawnNodes(layout)) {\n nodePositions[node.name] = centreline(\n node,\n yOffset,\n node.x,\n node.x + node.pixelWidth,\n )\n }\n const { minY, maxY } = extentOf(layout, yOffset)\n return {\n nodePositions,\n tubeMap: { layout, yOffset, pathColors },\n extent: {\n minX: layout.bounds.minX,\n maxX: layout.bounds.maxX,\n minY,\n maxY,\n },\n }\n}\n\n// Each column's reference bp: the span of its backbone nodes, whose merged\n// length the layout summed, or a point where the previous column ended for a\n// column of inserted sequence alone. Clamped to run left to right, so an\n// inversion the reference walks backwards cannot fold the axis over.\nfunction tubeMapColumns(graph: Graph, layout: TubeMapLayout): TubeMapColumn[] {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const byOrder = new Map<number, LayoutNode[]>()\n for (const node of drawnNodes(layout)) {\n const list = byOrder.get(node.order)\n if (list) {\n list.push(node)\n } else {\n byOrder.set(node.order, [node])\n }\n }\n const columns: TubeMapColumn[] = []\n let end = -Infinity\n for (const order of [...byOrder.keys()].sort((a, b) => a - b)) {\n const nodes = byOrder.get(order)!\n let bp0 = Infinity\n let bp1 = -Infinity\n for (const node of nodes) {\n const graphNode = nodeById.get(node.name)\n if (graphNode && isBackbone(graphNode)) {\n bp0 = Math.min(bp0, graphNode.stable.start)\n bp1 = Math.max(bp1, graphNode.stable.start + node.sequenceLength)\n }\n }\n if (bp0 === Infinity) {\n bp0 = bp1 = end\n }\n if (bp0 < end) {\n bp0 = end\n bp1 = Math.max(end, bp1)\n }\n end = Math.max(end, bp1)\n columns.push({\n order,\n x0: Math.min(...nodes.map(n => n.x)),\n x1: Math.max(...nodes.map(n => n.x + n.pixelWidth)),\n bp0,\n bp1,\n })\n }\n // columns before the first backbone node take its start\n const first = columns.find(c => c.bp0 !== -Infinity)\n for (const column of columns) {\n if (column.bp0 === -Infinity) {\n column.bp0 = column.bp1 = first?.bp0 ?? 0\n }\n }\n return columns\n}\n\nfunction pathsReachBackbone(graph: Graph) {\n return graph.nodes.some(isBackbone) && hasTubeMapPaths(graph)\n}\n\nexport function tubeMapReferenceLayout(graph: Graph): LayoutResult | undefined {\n const run = pathsReachBackbone(graph) ? runTubeMap(graph) : undefined\n if (!run) {\n return undefined\n }\n const { layout, pathColors } = run\n const columns = tubeMapColumns(graph, layout)\n const byOrder = new Map(columns.map(c => [c.order, c]))\n const yOffset = layout.bounds.minY\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const node of drawnNodes(layout)) {\n const column = byOrder.get(node.order)!\n nodePositions[node.name] = centreline(node, yOffset, column.bp0, column.bp1)\n }\n return {\n nodePositions,\n tubeMap: { layout, yOffset, columns, pathColors },\n referenceAxis: true,\n pixelRows: true,\n extent: extentOf(layout, yOffset),\n }\n}\n", "import { panSNSample } from '../pansn'\nimport { pathOrigin } from '../pathAnchoring'\n\nimport type { Graph, GraphPath } from '../types'\n\n// Each haplotype walk on its own bp axis, so what a walk carries through the\n// window is its bar length. Reference-anchored rows cannot state that: a\n// haplotype's private copies of a repeat consume no reference and collapse to\n// a mark, which is why the KIV-2 array reads as a knot in every node layout\n// and as a ladder here.\n//\n// A base-level graph does not revisit reference nodes through a repeat array,\n// so a copy count is not a visit count; it is the sequence a walk spends\n// between the reference nodes flanking the window, divided by the unit. Runs\n// distinguish sequence the reference walk also carries from sequence it does\n// not, so an expansion is the off-reference stretch of a row.\n\nexport interface WalkRun {\n // bp offset from the start of this walk's slice\n start: number\n bp: number\n onReference: boolean\n // lowest reference bp an on-reference run covers; the run covers `bp` of\n // reference contiguously. In a tandem array that is the reference copy the\n // graph threads this copy through, which need not be the one it most\n // resembles.\n referenceStart?: number\n // the walk crosses that reference from its end back to its start\n reversed?: true\n}\n\nexport interface WalkRow {\n name: string\n label: string\n sample: string\n haplotype?: number\n bp: number\n offReferenceBp: number\n // false when the walk does not reach both flanking reference nodes, in which\n // case the whole walk is measured and the bar says so\n complete: boolean\n runs: WalkRun[]\n}\n\nexport interface WalkRows {\n // reference bp the rows' bars start at, so a row's x is origin + run.start\n origin: number\n // repeat unit in bp when a repeat annotation supplied one; the bars tile by it\n unit?: number\n reference: WalkRow\n // every other walk, longest first\n rows: WalkRow[]\n}\n\nfunction sampleOf(path: GraphPath) {\n return path.sample ?? panSNSample(path.name)\n}\n\nfunction labelOf(path: GraphPath) {\n return path.haplotype !== undefined && path.sample !== undefined\n ? `${path.sample}#${path.haplotype}`\n : sampleOf(path)\n}\n\n// Each walk is cut at the nearest reference nodes IT visits on either side of\n// the region, so a walk that skips one flanking node at a SNP is still measured\n// between flanks rather than whole.\nfunction sliceBetween(\n nodeIds: string[],\n span: Map<string, { start: number; end: number }>,\n region: { start: number; end: number } | undefined,\n flanked = true,\n) {\n // A cut that stops at the window carries no flanking reference for anyone,\n // so every walk is whole and the slice is the walk.\n if (!region || !flanked) {\n return { ids: nodeIds, complete: true }\n }\n let i0 = -1\n let i1 = -1\n let bestEnd = -Infinity\n let bestStart = Infinity\n nodeIds.forEach((id, i) => {\n const s = span.get(id)\n if (s) {\n if (s.end <= region.start && s.end > bestEnd) {\n bestEnd = s.end\n i0 = i\n }\n if (s.start >= region.end && s.start < bestStart) {\n bestStart = s.start\n i1 = i\n }\n }\n })\n if (i0 < 0 || i1 < 0) {\n return { ids: nodeIds, complete: false }\n }\n const ids = nodeIds.slice(Math.min(i0, i1) + 1, Math.max(i0, i1))\n return { ids: i0 < i1 ? ids : ids.reverse(), complete: true }\n}\n\nexport function walkRows(\n graph: Graph,\n region?: { start: number; end: number },\n unit?: number,\n): WalkRows | undefined {\n const paths = graph.paths ?? []\n // `referencePath` is the anchor name, which pathOrigin has already stripped\n // of the range suffix odgi leaves on a P record's name\n const reference =\n paths.find(p => pathOrigin(p.name).name === graph.referencePath) ?? paths[0]\n if (!reference || paths.length < 2) {\n return undefined\n }\n const lengthOf = new Map(graph.nodes.map(n => [n.id, n.length]))\n const referenceStart =\n graph.anchorPaths?.find(p => p.name === pathOrigin(reference.name).name)\n ?.start ?? 0\n\n const cut = region && region.end > region.start ? region : undefined\n const span = new Map<string, { start: number; end: number }>()\n let pos = referenceStart\n for (const id of reference.nodeIds) {\n const len = lengthOf.get(id) ?? 0\n if (!span.has(id)) {\n span.set(id, { start: pos, end: pos + len })\n }\n pos += len\n }\n\n // Whether the reference reaches past the region on both sides, i.e. whether\n // a flanking node exists for any walk to be cut at.\n const flanked =\n cut === undefined ||\n ([...span.values()].some(s => s.end <= cut.start) &&\n [...span.values()].some(s => s.start >= cut.end))\n\n const rowOf = (path: GraphPath): WalkRow => {\n const { ids, complete } = sliceBetween(path.nodeIds, span, cut, flanked)\n const runs: WalkRun[] = []\n let bp = 0\n let offReferenceBp = 0\n // which way the last run steps through the reference, 0 while it holds\n // one node\n let step = 0\n for (const id of ids) {\n const len = lengthOf.get(id) ?? 0\n const s = span.get(id)\n const last = runs.at(-1)\n const at = last?.referenceStart\n const forward =\n s && at !== undefined && step >= 0 && at + last!.bp === s.start\n const backward = s && at !== undefined && step <= 0 && s.end === at\n if (!s && last && !last.onReference) {\n last.bp += len\n } else if (forward || backward) {\n last!.bp += len\n if (!forward) {\n last!.referenceStart = s.start\n last!.reversed = true\n }\n step = forward ? 1 : -1\n } else {\n runs.push({\n start: bp,\n bp: len,\n onReference: s !== undefined,\n referenceStart: s?.start,\n })\n step = 0\n }\n bp += len\n if (!s) {\n offReferenceBp += len\n }\n }\n return {\n name: path.name,\n label: labelOf(path),\n sample: sampleOf(path),\n haplotype: path.haplotype,\n bp,\n offReferenceBp,\n complete,\n runs,\n }\n }\n\n // A cut can hand one walk back in pieces, and a piece reached only through\n // context meets no reference node, so nothing places it against the window.\n // It is dropped when another piece of the same walk does meet the reference.\n const meetsReference = (p: GraphPath) => p.nodeIds.some(id => span.has(id))\n const placedWalks = new Set(\n paths.filter(p => p !== reference && meetsReference(p)).map(p => p.name),\n )\n const origin = cut ? cut.start : referenceStart\n return {\n origin,\n unit,\n reference: rowOf(reference),\n rows: paths\n .filter(\n p => p !== reference && (meetsReference(p) || !placedWalks.has(p.name)),\n )\n .map(rowOf)\n .sort(\n (a, b) =>\n Number(b.complete) - Number(a.complete) ||\n b.bp - a.bp ||\n a.label.localeCompare(b.label),\n ),\n }\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { walkRows } from './walkRows'\nimport { backboneNodes, backbonePositions } from '../anchoredNodes'\n\nimport type { WalkRows } from './walkRows'\nimport type { Graph, LayoutResult, RowLabel } from '../types'\n\n// The reference walk as the backbone on row 0, at its bp, and a row per other\n// walk below it. The rows themselves are not nodes: a node the renderer draws\n// once cannot sit on nine rows, so WalkRowsOverlay draws each walk's bar from\n// `walkRows`, and this layout only reserves the rows and states how far the\n// bars reach so the fit and the pane height include them.\n// The fit leaves this much past the longest bar for its readout and the legend.\nconst READOUT_ROOM = 1.3\n\n// How far the bars reach, so the fit and the pane height include them. The\n// model reads it again for the bars it actually draws, which a repeat pick or\n// a sample filter can narrow after this layout ran.\nexport function walkRowsExtent(walks: WalkRows) {\n let longest = walks.reference.bp\n for (const row of walks.rows) {\n longest = Math.max(longest, row.bp)\n }\n return {\n maxX: walks.origin + longest * READOUT_ROOM,\n maxY: walks.rows.length * ROW_HEIGHT_PX,\n }\n}\n\nexport function walkRowLayout(\n graph: Graph,\n region?: { start: number; end: number },\n): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n const walks = walkRows(graph, region)\n if (backbone.length === 0 || !walks) {\n return undefined\n }\n const nodePositions = backbonePositions(backbone)\n const rowLabels: RowLabel[] = [\n { label: walks.reference.label, y: 0 },\n ...walks.rows.map((row, i) => ({\n label: row.label,\n y: (i + 1) * ROW_HEIGHT_PX,\n })),\n ]\n return {\n nodePositions,\n rowLabels,\n referenceAxis: true,\n pixelRows: true,\n extent: walkRowsExtent(walks),\n }\n}\n", "import { isBackbone, isOffReference } from './anchoredNodes'\nimport { anchoredLayout } from './layout/anchoredLayout'\nimport { orderedLayout } from './layout/orderedLayout'\nimport { sampleRowLayout } from './layout/sampleRowLayout'\nimport {\n hasTubeMapPaths,\n tubeMapLayout,\n tubeMapReferenceLayout,\n} from './layout/tubeMapLayout'\nimport { walkRowLayout } from './layout/walkRowLayout'\n\nimport type { Graph, LayoutResult } from './types'\n\n// The force layout's menu label. jbrowse-components' check-menu-labels scans\n// this src/ for string literals, so a documented **Layout \u2192 \u2026** path is held\n// to what the dropdown renders.\nexport const FORCE_LAYOUT_LABEL = 'Force-directed layout'\n\n// One table drives the persisted enum, the toolbar dropdown, and the dispatch in\n// the model, so adding a layout is one entry rather than several edits that can\n// disagree.\n//\n// `run` returning undefined means \"this mode can't draw this graph\" and hands\n// off to the remote Bandage FMMM engine. 'auto' uses it when a GFA has no\n// rank-0 backbone \u2014 neither declared in rGFA tags nor derivable from P/W lines\n// (pathAnchoring.ts) \u2014 and 'force' is simply the mode that always takes it.\nexport interface LayoutMode {\n value: string\n label: string\n description: string\n // `region` is the interval the cut was made for, absent for a whole-file\n // import. The reference-anchored modes scale their rows and their allele\n // floor against it rather than against the backbone they were handed, which\n // a long-range allele's far anchor stretches well past the window\n // (referenceSpan). `keepRows` is the sample rows on screen, which only the\n // sample-row layout reads (LayoutResult.sampleRows).\n run: (\n graph: Graph,\n region?: { start: number; end: number },\n keepRows?: readonly string[],\n ) => LayoutResult | undefined\n // whether this mode can draw this graph at all; the dropdown greys out the\n // rest rather than hiding them, so the reason a mode is unavailable stays\n // visible instead of the menu silently changing shape between graphs\n available: (graph: Graph) => boolean\n // whether a cut for this mode carries a window-width margin each side, to\n // pan over before the next cut. A mode that draws a picture of the window\n // \u2014 force, ordered, and walk rows, whose bars are lengths through it \u2014 is\n // cut to the window alone.\n cutMargins: boolean\n // whether this mode measures each haplotype's walk whole, so a GBZ cut\n // follows every snarl that leaves the window rather than breaking a walk\n // where it leaves the window's snarls: at a duplication array the lighter\n // cut hands one walk back in pieces\n wholeWalks: boolean\n // whether `run` draws this graph itself, as opposed to handing off to the\n // engine. Not the same question as `available` and not derivable from it:\n // 'force' is available for every graph and draws none of them itself. The two\n // coincide for the anchored modes, which is stated by sharing the predicate\n // rather than by restating it.\n drawsLocally: (graph: Graph) => boolean\n}\n\nconst hasBackbone = (graph: Graph) => graph.nodes.some(isBackbone)\nconst hasWalks = (graph: Graph) =>\n hasBackbone(graph) && (graph.paths?.length ?? 0) > 1\nconst hasTubeMapBackbone = (graph: Graph) =>\n hasBackbone(graph) && hasTubeMapPaths(graph)\nconst hasAlleles = (graph: Graph) =>\n hasBackbone(graph) && graph.nodes.some(isOffReference)\n\n// `satisfies` rather than a `: LayoutMode[]` annotation: the annotation widened\n// every `value` to string, which collapsed LayoutModeValue to string and left the\n// persisted enum unable to state which values it accepts.\nexport const LAYOUT_MODES = [\n {\n value: 'auto',\n label: 'Anchored',\n description:\n 'x is reference bp, one row per stable rank. Needs rGFA tags or a reference path.',\n run: anchoredLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n },\n {\n value: 'samplerows',\n label: 'Sample rows',\n description:\n 'x is reference bp, one row per contributing assembly. Needs rGFA tags or a reference path.',\n run: sampleRowLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasAlleles,\n drawsLocally: hasAlleles,\n },\n {\n value: 'walkrows',\n label: 'Walk rows',\n description:\n \"x is each walk's own bp: one bar per haplotype, sequence the reference also carries in blue and sequence it does not in purple, so a repeat expansion reads as bar length. Needs W or P lines.\",\n run: walkRowLayout,\n cutMargins: false,\n wholeWalks: true,\n available: hasWalks,\n drawsLocally: hasWalks,\n },\n {\n value: 'ordered',\n label: 'Ordered',\n description:\n 'x is reference order, not bp: every node gets room, bubbles read as lenses. Needs rGFA tags or a reference path.',\n run: orderedLayout,\n cutMargins: false,\n wholeWalks: false,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n },\n {\n value: 'tubemap',\n label: 'Tube map',\n description:\n 'sequenceTubeMap: every path a coloured tube through boxed nodes, columns in node order and node widths log-scaled. Needs P or W lines.',\n run: tubeMapLayout,\n cutMargins: false,\n wholeWalks: false,\n available: hasTubeMapPaths,\n drawsLocally: hasTubeMapPaths,\n },\n {\n value: 'tubemapref',\n label: 'Tube map on reference',\n description:\n 'The tube map with each column at the reference bp it covers, so the tubes line up with the tracks around them. Needs P or W lines and a reference path.',\n run: tubeMapReferenceLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasTubeMapBackbone,\n drawsLocally: hasTubeMapBackbone,\n },\n {\n value: 'force',\n label: FORCE_LAYOUT_LABEL,\n description: 'OGDF FMMM, via the external Bandage engine.',\n run: () => undefined,\n cutMargins: false,\n wholeWalks: false,\n available: () => true,\n drawsLocally: () => false,\n },\n] as const satisfies readonly LayoutMode[]\n\nexport type LayoutModeValue = (typeof LAYOUT_MODES)[number]['value']\n\nexport const LAYOUT_MODE_VALUES = LAYOUT_MODES.map(m => m.value)\n\n// Whether the drawing a mode produces for a graph comes from the FMMM engine.\n// The settings that only the engine reads \u2014 layout quality, bubble spread \u2014\n// are shown against this, so a control that cannot do anything to the drawing\n// on screen says so instead of appearing to work.\nexport function modeUsesLayoutEngine(value: string, graph: Graph) {\n return !layoutModeByValue(value).drawsLocally(graph)\n}\n\nexport function layoutModeByValue(value: string) {\n // An unknown value can only come from a snapshot written by a build that had\n // a mode this one doesn't. It falls back to 'auto' rather than to the model's\n // own default of 'force', because 'auto' declines any graph it cannot draw and\n // hands off to force anyway, so this is the fallback that covers both.\n return LAYOUT_MODES.find(m => m.value === value) ?? LAYOUT_MODES[0]\n}\n", "import { SATURATED_PATH_COUNT } from './bubbleLine'\n\nimport type { MinigraphBubble } from './bubbleLine'\n\n// What kind of variation a bubble is, read off the numbers `gfatools bubble`\n// states: the reference interval it replaces, its shortest and longest route,\n// how many routes, and whether one of them inverts. The reference route is one\n// of the routes and its length is the interval, so the shortest and longest\n// against the interval say whether the alternatives add sequence, remove it,\n// or swap it. Whether it is a repeat is not in those numbers: a\n// presence/absence insertion carried by many haplotypes has the same shape as\n// an expanding array, so that comes from the session's repeat annotation.\nexport type BubbleKind =\n | 'snp'\n | 'substitution'\n | 'insertion'\n | 'deletion'\n | 'inversion'\n | 'repeat'\n | 'superbubble'\n | 'complex'\n\nexport interface BubbleClass {\n kind: BubbleKind\n label: string\n}\n\n// An annotated tandem repeat array over the cut (repeats/repeatFeatures).\n// Its refName is not compared: the arrays are read over the same window as\n// the bubbles, which spell the reference their own way (PanSN).\nexport interface RepeatSpan {\n start: number\n end: number\n name?: string\n}\n\n// How far past its reference length an array's alleles may run and still be\n// copies of it: KIV-2's 40 kb array carries 175 kb alleles\nconst REPEAT_GROWTH = 10\n\n// The array covering at least half the bubble's reference interval, or the\n// point of an insertion, and long enough to account for its alleles: a\n// microsatellite at an insertion point does not make the insertion a repeat.\nfunction coveringRepeat(b: MinigraphBubble, repeats: readonly RepeatSpan[]) {\n const span = b.end - b.start\n const growth = b.longestAlleleLength - b.shortestAlleleLength\n return repeats.find(\n r =>\n growth <= REPEAT_GROWTH * (r.end - r.start) &&\n (span === 0\n ? r.start <= b.start && b.start <= r.end\n : Math.min(b.end, r.end) - Math.max(b.start, r.start) >= span / 2),\n )\n}\n\nconst SUPERBUBBLE_SEGMENTS = 40\n\nexport function formatBp(bp: number) {\n if (bp >= 10_000) {\n return `${(bp / 1000).toFixed(0)} kb`\n }\n if (bp >= 1000) {\n return `${(bp / 1000).toFixed(1)} kb`\n }\n return `${bp} bp`\n}\n\nfunction formatBpRange(shortest: number, longest: number) {\n const low = formatBp(shortest)\n const high = formatBp(longest)\n const unit = high.slice(high.indexOf(' '))\n return low.endsWith(unit)\n ? `${low.slice(0, -unit.length)}\u2013${high}`\n : `${low}\u2013${high}`\n}\n\nfunction routes(count: number) {\n if (count >= SATURATED_PATH_COUNT) {\n return '\u22652.1B routes'\n }\n if (count >= 100_000) {\n return `${(count / 1000).toFixed(0)}k routes`\n }\n return `${count} routes`\n}\n\nexport function classifyBubble(\n b: MinigraphBubble,\n repeats: readonly RepeatSpan[] = [],\n): BubbleClass {\n const c = classifyShape(b, repeats)\n return b.partial ? { ...c, label: `${c.label}, partial` } : c\n}\n\nfunction classifyShape(\n b: MinigraphBubble,\n repeats: readonly RepeatSpan[],\n): BubbleClass {\n const refSpan = b.end - b.start\n const {\n shortestAlleleLength: shortest,\n longestAlleleLength: longest,\n pathCount,\n segmentCount,\n inversion,\n } = b\n const range = formatBpRange(shortest, longest)\n if (segmentCount >= SUPERBUBBLE_SEGMENTS) {\n return {\n kind: 'superbubble',\n label: `${range} superbubble, ${segmentCount} segments, ${routes(pathCount)}${inversion ? ', inv' : ''}`,\n }\n }\n if (inversion) {\n return { kind: 'inversion', label: `${formatBp(longest)} inv` }\n }\n // routes of one length are substitutions inside an array, not copies\n const repeat = longest !== shortest ? coveringRepeat(b, repeats) : undefined\n if (repeat) {\n const name = repeat.name ? ` (${repeat.name})` : ''\n return {\n kind: 'repeat',\n label: `${range} repeat array${name}, ${routes(pathCount)}`,\n }\n }\n const alleles = pathCount > 2 ? `, ${pathCount} alleles` : ''\n if (shortest === longest && shortest === refSpan) {\n return refSpan <= 1\n ? { kind: 'snp', label: 'SNP' }\n : { kind: 'substitution', label: `${formatBp(refSpan)} sub` }\n }\n if (refSpan === 0 || shortest === refSpan) {\n return {\n kind: 'insertion',\n label: `\u2264${formatBp(longest - refSpan)} ins${alleles}`,\n }\n }\n if (longest === refSpan) {\n return {\n kind: 'deletion',\n label: `${formatBp(refSpan - shortest)} del${alleles}`,\n }\n }\n return {\n kind: 'complex',\n label: `${formatBp(refSpan)} ref \u2192 ${range}, ${pathCount} alleles`,\n }\n}\n\n// One hue per kind, the deletion arc's near-black and the reference blue kept\n// clear of, so a glyph is not mistaken for either.\nexport const BUBBLE_KIND_COLORS: Record<BubbleKind, string> = {\n snp: '#5b6b7a',\n substitution: '#5b6b7a',\n insertion: '#2f8fd6',\n deletion: '#c94040',\n inversion: '#e07b00',\n repeat: '#8e3fbf',\n superbubble: '#8e3fbf',\n complex: '#8e3fbf',\n}\n\n// The kind as a short name, for a place that has no room for the label\nexport const BUBBLE_KIND_NAMES: Record<BubbleKind, string> = {\n snp: 'SNP',\n substitution: 'substitution',\n insertion: 'insertion',\n deletion: 'deletion',\n inversion: 'inversion',\n repeat: 'repeat array',\n superbubble: 'superbubble',\n complex: 'site',\n}\n\nexport function bubbleSegmentIds(b: MinigraphBubble) {\n return b.segments.split(',').filter(Boolean)\n}\n", "// Minimal GFA (Graphical Fragment Assembly) text parser shared by the graph\n// pangenome plugins (GraphGenomeView, TubeMapView). Handles GFA1/GFA2 segment,\n// link, edge, path and walk records plus header tags.\n\n// Only the first two colons delimit a tag; everything after them is the value.\n// A Z value may contain colons of its own, and rGFA stable names routinely do\n// (`SN:Z:chr1:1-1000` from a sliced reference), where splitting on every colon\n// silently truncates the name to `chr1` and mis-anchors the whole layout.\nfunction parseTag(tag: string, tags: Record<string, string | number>) {\n const nameEnd = tag.indexOf(':')\n const typeEnd = tag.indexOf(':', nameEnd + 1)\n if (nameEnd !== -1 && typeEnd !== -1) {\n const name = tag.slice(0, nameEnd)\n const type = tag.slice(nameEnd + 1, typeEnd)\n const val = tag.slice(typeEnd + 1)\n if (type === 'i' || type === 'f') {\n tags[name] = +val\n } else if (type === 'Z') {\n tags[name] = val\n }\n }\n}\n\nfunction parseTags(fields: string[]) {\n const tags: Record<string, string | number> = {}\n for (const field of fields) {\n parseTag(field, tags)\n }\n return tags\n}\n\nexport interface GFANode {\n id: string\n length: number\n sequence: string\n tags: Record<string, string | number>\n}\n\n// rGFA (minigraph) anchors every segment to a reference coordinate: SN is the\n// stable sequence name, SO the offset on it, and SR the stable rank, where 0 is\n// the reference backbone and higher ranks are the sequence that diverges from\n// it. Plain GFA (pggb, odgi, Minigraph-Cactus) carries none of these, which is\n// why coordinates there exist only inside the P/W lines.\nexport interface StableCoordinate {\n refName: string\n start: number\n rank: number\n // How the anchoring path reads this segment. Only path-derived coordinates\n // set it: an rGFA segment is by construction forward on the sequence SN\n // names, so there is nothing there for it to say.\n strand?: '+' | '-'\n}\n\nexport function stableCoordinate(node: GFANode): StableCoordinate | undefined {\n const { SN, SO, SR } = node.tags\n return typeof SN === 'string' &&\n typeof SO === 'number' &&\n typeof SR === 'number'\n ? { refName: SN, start: SO, rank: SR }\n : undefined\n}\n\nexport interface GFALink {\n source: string\n target: string\n strand1?: string\n strand2?: string\n cigar: string\n tags: Record<string, string | number>\n}\n\nexport interface GFAPath {\n name: string\n path: string\n rest: string[]\n}\n\nexport interface GFAWalkSegment {\n id: string\n strand: string\n}\n\nexport interface GFAWalk {\n sample: string\n haplotype: number\n contig: string\n start: number\n end: number\n segments: GFAWalkSegment[]\n tags: Record<string, string | number>\n}\n\nconst GT = 62 // '>'\nconst LT = 60 // '<'\n\n// A W record's body is `>s1<s2>s3\u2026`, and on a Minigraph-Cactus graph one record\n// is the whole walk of one haplotype across a chromosome \u2014 hundreds of\n// thousands of steps in a single field.\n//\n// So the ids are cut out by index rather than accumulated a character at a\n// time. `current += ch` builds and discards one string per character of every\n// id, and it dominated the parse: a file whose single W record walks 200k steps\n// went from ~55 ms to ~24 ms by slicing instead. `pnpm bench` keeps the number.\nfunction parseWalkBody(body: string) {\n const segments: GFAWalkSegment[] = []\n let strand = '+'\n let start = 0\n // An empty run \u2014 a leading delimiter, or two in a row \u2014 names no segment.\n function push(end: number) {\n if (end > start) {\n segments.push({ id: body.slice(start, end), strand })\n }\n }\n for (let i = 0; i < body.length; i++) {\n const ch = body.charCodeAt(i)\n if (ch === GT || ch === LT) {\n push(i)\n strand = ch === GT ? '+' : '-'\n start = i + 1\n }\n }\n push(body.length)\n return segments\n}\n\nexport interface GFAGraph {\n nodes: GFANode[]\n links: GFALink[]\n paths: GFAPath[]\n walks: GFAWalk[]\n header: Record<string, string | number>[]\n id: string\n}\n\nexport function parseGFA(file: string) {\n const graph: GFAGraph = {\n nodes: [],\n links: [],\n paths: [],\n walks: [],\n header: [],\n id: '',\n }\n\n // \\r is stripped rather than split on, so a CRLF file does not leave it on\n // whichever field happens to be last: a trailing Z tag would carry it into a\n // stable name and a trailing cigar into the CIGAR string.\n //\n // Stripped per line inside the loop rather than with a .map() over the split,\n // which would hold a second copy of every line in the file at once.\n for (const rawLine of file.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine\n if (line.startsWith('H')) {\n const [, ...rest] = line.split('\\t')\n graph.header.push(parseTags(rest))\n } else if (line.startsWith('S')) {\n const [, name, ...rest] = line.split('\\t')\n let len: number\n let seq: string\n let tagfields: string[]\n let gfa1 = false\n // A truncated record has no field to read; `*` is what the spec writes for\n // a segment whose sequence is absent, so treating a missing one the same\n // way yields a 0 bp segment instead of throwing partway through the file.\n const first = rest[0] ?? '*'\n // GFA2 puts an integer length in this field, GFA1 the sequence. Testing\n // the digits rather than the coerced number keeps a GFA2 `S sid 0 *` from\n // being read as a GFA1 sequence of \"0\".\n if (/^\\d+$/.test(first)) {\n len = +first\n seq = rest[1] ?? '*'\n tagfields = rest.slice(2)\n } else {\n gfa1 = true\n seq = first\n // `*` is GFA1's \"no sequence here\"; its length is whatever LN says\n // below, and 0 if the file states none. Counting the placeholder's own\n // character instead reports every such segment as 1 bp.\n len = seq === '*' ? 0 : seq.length\n tagfields = rest.slice(1)\n }\n const tags = parseTags(tagfields)\n if (gfa1 && 'LN' in tags) {\n len = +tags.LN\n }\n graph.nodes.push({ id: name!, length: len, sequence: seq, tags })\n } else if (line.startsWith('E')) {\n // From here down, a record cut off before the fields that make it a\n // link, walk or path states nothing drawable and is dropped; the file\n // around it still reads. A link missing only its overlap is kept.\n const [, , source, target, , , , , cigar, ...rest] = line.split('\\t')\n if (source && target) {\n graph.links.push({\n source: source.slice(0, -1),\n target: target.slice(0, -1),\n strand1: source.at(-1),\n strand2: target.at(-1),\n cigar: cigar ?? '*',\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('L')) {\n const [, source, strand1, target, strand2, cigar, ...rest] =\n line.split('\\t')\n if (source && target && strand2) {\n graph.links.push({\n source,\n target,\n strand1,\n strand2,\n cigar: cigar ?? '*',\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('W')) {\n const [, sample, hap, contig, start, end, body, ...rest] =\n line.split('\\t')\n if (sample && hap && contig && start && end && body) {\n graph.walks.push({\n sample,\n haplotype: +hap,\n contig,\n start: start === '*' ? -1 : +start,\n end: end === '*' ? -1 : +end,\n segments: parseWalkBody(body),\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('P')) {\n const [, name, path, ...rest] = line.split('\\t')\n if (name && path) {\n graph.paths.push({ name, path, rest })\n }\n }\n }\n return graph\n}\n", "import { stableCoordinate } from '../gfa-core/index'\nimport { pathOrigin, surveyPaths } from '../pathAnchoring'\n\nimport type { GFAGraph, GFANode } from '../gfa-core/index'\nimport type { PathSteps } from '../pathAnchoring'\nimport type { Graph, GraphEdge, GraphNode, GraphPath } from '../types'\n\n// Single pass over every reference a GFA makes to a segment, collecting the two\n// things the drawn graph needs from them.\n//\n// `canonical` is the one orientation each segment is drawn in, which is what\n// Bandage calls single-node mode. A segment is the same sequence read either\n// way (`L 1 + 2 +` is the same edge as `L 2 - 1 -`), so `1+` and `1-` are one\n// node, not two: drawing both doubles every segment, and since the L lines only\n// wire one of the pair, the other copy has no edges at all and scatters loose\n// across the layout. Links carry the topology, so their orientation wins; a\n// segment only paths mention takes the orientation they walk it in.\n//\n// `traversals` is how many paths cross each segment. Assembly GFAs carry read\n// depth in a dp/RC/FC/KC tag, but a pangenome GFA (odgi, vg) carries none \u2014\n// there the meaningful depth is how many samples go through the segment, i.e.\n// core versus accessory sequence.\nfunction surveySegments(gfaGraph: GFAGraph) {\n const canonical = new Map<string, '+' | '-'>()\n const traversals = new Map<string, number>()\n function claim(id: string, strand: '+' | '-') {\n if (!canonical.has(id)) {\n canonical.set(id, strand)\n }\n }\n function traverse(id: string, strand: '+' | '-') {\n claim(id, strand)\n traversals.set(id, (traversals.get(id) ?? 0) + 1)\n }\n for (const link of gfaGraph.links) {\n claim(link.source, link.strand1 === '-' ? '-' : '+')\n claim(link.target, link.strand2 === '-' ? '-' : '+')\n }\n for (const gfaPath of gfaGraph.paths) {\n for (const segment of gfaPath.path.split(',')) {\n traverse(segment.slice(0, -1), segment.endsWith('-') ? '-' : '+')\n }\n }\n for (const walk of gfaGraph.walks) {\n for (const seg of walk.segments) {\n traverse(seg.id, seg.strand === '-' ? '-' : '+')\n }\n }\n return { canonical, traversals }\n}\n\n// P and W state the same thing in different shapes: a P body is a comma-joined\n// `<id><strand>` list whose start offset lives in the record's *name*, a W\n// record arrives already split into steps and states its start as a field.\n// Normalized here so the coordinate walk sees one kind of path.\nfunction anchorablePaths(gfaGraph: GFAGraph): PathSteps[] {\n return [\n ...gfaGraph.paths.map(p => ({\n ...pathOrigin(p.name),\n steps: p.path.split(',').map(segment => ({\n id: segment.slice(0, -1),\n strand: segment.endsWith('-') ? ('-' as const) : ('+' as const),\n })),\n })),\n ...gfaGraph.walks.map(w => ({\n name: `${w.sample}#${w.haplotype}#${w.contig}`,\n // `*` parses to -1, meaning the record declines to say; 0 is then the\n // only offset that can be assumed, and it makes the walk self-relative\n // rather than wrong\n start: w.start === -1 ? 0 : w.start,\n steps: w.segments.map(s => ({\n id: s.id,\n strand: s.strand === '-' ? ('-' as const) : ('+' as const),\n })),\n })),\n ]\n}\n\n// Carriage stated as a tag rather than derived from a walk. `RgfaTabixAdapter`\n// synthesizes its S-lines from a segs.bed whose sixth column is a list of GFA\n// tags, and `scripts/pggb_gfa_to_bed.py` in jbrowse-components puts every\n// haplotype that visits a segment there as `SM:Z:K12.1,Sakai.1`. That is the\n// only way carriage reaches an INDEXED graph: the cut has no P or W lines to\n// walk, so `pathAnchoring` has no visits to derive it from.\n//\n// It does not compete with that walk. `pathAnchoring.anchorNode` rebuilds\n// `samples` from the visits whenever there are any, so a graph loaded as a file\n// keeps the authoritative path-derived set and this is what a tabix cut falls\n// back to.\nfunction carriedSamples(gfaNode: GFANode) {\n const sm = gfaNode.tags.SM\n if (typeof sm !== 'string') {\n return undefined\n }\n const samples = sm\n .split(',')\n .map(s => s.trim())\n .filter(Boolean)\n return samples.length > 0 ? samples : undefined\n}\n\nconst positive = (v: unknown) =>\n typeof v === 'number' && v > 0 ? v : undefined\n\n// The depth a segment states, or undefined. DP is a depth in either case it is\n// written. RC, FC and KC are read, fragment and k-mer counts, so depth is the\n// count over the length: taken raw, a 100 kb node at 5x outweighed a 1 kb node\n// at 50x tenfold in every width drawn from it.\nfunction statedDepth({ tags, length }: GFANode) {\n const count = positive(tags.RC) ?? positive(tags.FC) ?? positive(tags.KC)\n return (\n positive(tags.dp) ??\n positive(tags.DP) ??\n (count !== undefined && length > 0 ? count / length : undefined)\n )\n}\n\nfunction makeNode(\n gfaNode: GFANode,\n strand: '+' | '-',\n depth: number,\n): GraphNode {\n return {\n id: `${gfaNode.id}${strand}`,\n name: gfaNode.id,\n length: gfaNode.length,\n depth,\n stable: stableCoordinate(gfaNode),\n samples: carriedSamples(gfaNode),\n }\n}\n\nexport function convertGFAToGraph(gfaGraph: GFAGraph, name = 'Imported GFA') {\n const nodes: GraphNode[] = []\n const edges: GraphEdge[] = []\n\n const { canonical, traversals } = surveySegments(gfaGraph)\n // segments no link, path, or walk mentions are drawn forward-strand\n const nodeId = (segmentId: string) =>\n `${segmentId}${canonical.get(segmentId) ?? '+'}`\n\n for (const gfaNode of gfaGraph.nodes) {\n const depth =\n statedDepth(gfaNode) ?? Math.max(traversals.get(gfaNode.id) ?? 1, 1)\n nodes.push(makeNode(gfaNode, canonical.get(gfaNode.id) ?? '+', depth))\n }\n\n for (const link of gfaGraph.links) {\n edges.push({\n from: nodeId(link.source),\n to: nodeId(link.target),\n fromStrand: link.strand1 === '-' ? '-' : '+',\n toStrand: link.strand2 === '-' ? '-' : '+',\n })\n }\n\n const paths: GraphPath[] = []\n const edgeToPathsMap = new Map<string, Set<string>>()\n\n // A path that walks the graph on the reverse strand visits an edge's nodes in\n // the opposite order to the L line that declared it, so the key is unordered.\n const edgeKey = (a: string, b: string) => [a, b].sort().join('--')\n\n function recordPathEdges(nodeIds: string[], name: string) {\n for (let i = 0; i < nodeIds.length - 1; i++) {\n const key = edgeKey(nodeIds[i]!, nodeIds[i + 1]!)\n if (!edgeToPathsMap.has(key)) {\n edgeToPathsMap.set(key, new Set())\n }\n edgeToPathsMap.get(key)!.add(name)\n }\n }\n\n for (const gfaPath of gfaGraph.paths) {\n // path segments arrive as `<id><strand>`; the node they land on is the\n // segment's canonical orientation, whichever way this path reads it\n const nodeIds = gfaPath.path\n .split(',')\n .map(segment => nodeId(segment.slice(0, -1)))\n paths.push({ name: gfaPath.name, nodeIds } satisfies GraphPath)\n recordPathEdges(nodeIds, gfaPath.name)\n }\n\n for (const walk of gfaGraph.walks) {\n const nodeIds = walk.segments.map(seg => nodeId(seg.id))\n // The full PanSN name, contig included, which is also what `anchorablePaths`\n // above calls the same record. Dropping the contig made a path's name an\n // ASSEMBLY's name, and one assembly walks one W record per contig: two walks\n // of `HG00438#1` then shared an identity, so `edgeToPathsMap` merged them,\n // `pathColors` (keyed by name) kept only the last colour written while the\n // node stripes kept the first, and the key drew one haplotype twice in two\n // colours. That is the ordinary Minigraph-Cactus shape, not a corner case.\n //\n // The legend narrows it back: `labelTiers` tries sample, then sample and\n // haplotype, then the whole name, so a graph with one contig per haplotype\n // is labelled exactly as before and only a colliding one widens.\n const name = `${walk.sample}#${walk.haplotype}#${walk.contig}`\n paths.push({\n name,\n nodeIds,\n sample: walk.sample,\n haplotype: walk.haplotype,\n contig: walk.contig,\n } satisfies GraphPath)\n recordPathEdges(nodeIds, name)\n }\n\n for (const edge of edges) {\n const pathIds = edgeToPathsMap.get(edgeKey(edge.from, edge.to))\n if (pathIds && pathIds.size > 0) {\n edge.pathIds = [...pathIds]\n }\n }\n\n // Only walked when the file has paths to walk, so an rGFA \u2014 which has none at\n // all \u2014 pays nothing for it. `lengthOf` reads the segment table rather than\n // the nodes just built, because a path may name a segment no S line declared.\n const lengths = new Map(gfaGraph.nodes.map(n => [n.id, n.length]))\n const anchoring =\n gfaGraph.paths.length > 0 || gfaGraph.walks.length > 0\n ? surveyPaths(anchorablePaths(gfaGraph), id => lengths.get(id) ?? 0)\n : undefined\n\n return {\n name,\n nodes,\n edges,\n paths: paths.length > 0 ? paths : undefined,\n anchorPaths: anchoring?.anchorPaths,\n pathVisits: anchoring?.pathVisits,\n // rGFA's own tags win wherever they are present; pathAnchoring only ever\n // fills in for a graph that carries none.\n anchoredBy: nodes.some(n => n.stable) ? 'tags' : undefined,\n } satisfies Graph\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Graph, NodeSegment } from '../types'\n\ntype Positions = Record<string, NodeSegment[]>\n\n// Turn a finished force layout so the reference runs left to right: the\n// rotation that best maps backbone midpoints onto their reference coordinate\n// (a one-dimensional orthogonal Procrustes fit, weighted by segment length),\n// then a reflection so the off-reference mass sits below the axis. A rigid\n// transform, so the drawing FMMM made is unchanged; only its frame moves.\n//\n// About the length-weighted centroid, and translated back, so a drawing that is\n// already oriented comes back where it was rather than recentred on the origin.\nexport function orientToReference(graph: Graph, positions: Positions) {\n const backbone = graph.nodes.filter(\n node => isBackbone(node) && (positions[node.id]?.length ?? 0) > 0,\n )\n if (backbone.length < 2) {\n return positions\n }\n const points = backbone.map(node => {\n const segments = positions[node.id]!\n const mid = segments[Math.floor(segments.length / 2)]!\n return {\n x: mid.x,\n y: mid.y,\n r: node.stable!.start + node.length / 2,\n w: node.length,\n }\n })\n const weight = points.reduce((sum, p) => sum + p.w, 0)\n const cx = points.reduce((sum, p) => sum + p.x * p.w, 0) / weight\n const cy = points.reduce((sum, p) => sum + p.y * p.w, 0) / weight\n const cr = points.reduce((sum, p) => sum + p.r * p.w, 0) / weight\n // cross-covariance of (x, y) against (r, 0); only the first row is non-zero\n let sxr = 0\n let syr = 0\n for (const p of points) {\n sxr += p.w * (p.x - cx) * (p.r - cr)\n syr += p.w * (p.y - cy) * (p.r - cr)\n }\n // the rotation taking the direction (sxr, syr) onto +x\n const angle = -Math.atan2(syr, sxr)\n const c = Math.cos(angle)\n const s = Math.sin(angle)\n\n const out: Positions = {}\n for (const [id, segments] of Object.entries(positions)) {\n out[id] = segments.map(p => {\n const x = p.x - cx\n const y = p.y - cy\n return { x: cx + c * x - s * y, y: cy + s * x + c * y }\n })\n }\n\n let below = 0\n let above = 0\n for (const node of graph.nodes) {\n if (isBackbone(node)) {\n continue\n }\n for (const p of out[node.id] ?? []) {\n if (p.y > cy) {\n below++\n } else {\n above++\n }\n }\n }\n if (above > below) {\n for (const segments of Object.values(out)) {\n for (const p of segments) {\n p.y = 2 * cy - p.y\n }\n }\n }\n return out\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { buildNeighbors } from '../referenceSpan'\nimport { drawnNodeLength } from './drawnScale'\n\nimport type { LayoutScaling } from './drawnScale'\nimport type { Graph, GraphNode } from '../types'\n\n// A node as the engine takes it: an optional `x`/`y` is where FMMM starts the\n// node's chain (`addToOgdfGraph`, graphlayout.cpp).\nexport type LayoutNode = GraphNode & { x?: number; y?: number }\n\nexport interface Seed {\n x: number\n y: number\n}\n\n// y per BFS step off the backbone. FMMM keeps only the coarse shape of these\n// (OGDF applies the initial placement at its coarsest multilevel graph), so\n// the gap needs to say \"below the line\", not to place a lane.\nconst LANE_GAP = 30\n\n// Initial positions for a reference-anchored graph: the backbone end to end\n// along x at drawn length, each off-reference node at the midpoint of the node\n// a BFS from the backbone reached it from, a lane lower per step. Two alleles\n// off one anchor share a seed; FMMM separates them. Nodes no backbone reaches\n// spread along x below everything rather than piling on one point.\n//\n// Lengths come from `scaling.nodes`, not the graph's bp: under a compressing\n// spread a node's `length` crosses the RPC as drawn units times 1000 with a\n// matching `nodeLengthPerMegabase`, and a seed from bp would misplace every\n// chain against the one the engine builds.\nexport function referenceSeeds(\n graph: Graph,\n scaling: LayoutScaling,\n): Map<string, Seed> {\n const { opts } = scaling\n const drawn = new Map(\n scaling.nodes.map(node => [node.id, drawnNodeLength(opts, node.length)]),\n )\n const drawnOf = (id: string) => drawn.get(id) ?? opts.minimumNodeLength\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort((a, b) => a.stable.start - b.stable.start)\n\n const seeds = new Map<string, Seed>()\n let x = 0\n for (const node of backbone) {\n seeds.set(node.id, { x, y: 0 })\n x += drawnOf(node.id) + opts.edgeLength\n }\n\n const adjacent = buildNeighbors(graph)\n\n const depth = new Map(backbone.map(node => [node.id, 0]))\n // for-of over an array that grows as it goes: the iterator reads the live\n // length, so nodes pushed here are visited\n const queue = backbone.map(node => node.id)\n for (const id of queue) {\n const at = seeds.get(id)!\n const next = depth.get(id)! + 1\n for (const neighbour of adjacent.get(id) ?? []) {\n if (depth.has(neighbour)) {\n continue\n }\n depth.set(neighbour, next)\n seeds.set(neighbour, { x: at.x + drawnOf(id) / 2, y: LANE_GAP * next })\n queue.push(neighbour)\n }\n }\n\n let unreached = 0\n for (const node of graph.nodes) {\n if (!seeds.has(node.id)) {\n seeds.set(node.id, {\n x: (unreached++ * x) / graph.nodes.length,\n y: LANE_GAP * 3,\n })\n }\n }\n return seeds\n}\n\nexport function seededNodes(\n graph: Graph,\n scaling: LayoutScaling,\n): LayoutNode[] {\n const seeds = referenceSeeds(graph, scaling)\n return scaling.nodes.map(node => ({ ...node, ...seeds.get(node.id) }))\n}\n", "import { isBackbone } from './anchoredNodes'\nimport { spreadFor } from './bubbleSpreads'\nimport { convertGFAToGraph } from './gfa/gfaConverter'\nimport { parseGFA } from './gfa-core/index'\nimport { drawnNodeLength, layoutScaling } from './layout/drawnScale'\nimport { mergeRuns, splitRuns } from './layout/mergeRuns'\nimport { orientToReference } from './layout/orientToReference'\nimport { seededNodes } from './layout/referenceSeeds'\nimport { anchorGraph } from './pathAnchoring'\n\nimport type { BubbleSpread } from './bubbleSpreads'\nimport type { LayoutScaling } from './layout/drawnScale'\nimport type { LayoutNode } from './layout/referenceSeeds'\nimport type { Graph, LayoutResult, NodeSegment } from './types'\n\n// The graph a view draws, from GFA text to layouts, with no host in it: the\n// GraphGenomeView model and a standalone page both drive this.\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\n// A general GFA states its coordinates only in its P/W lines, so the walk that\n// recovers them happens before anything reads `stable`; otherwise the anchored\n// layouts see an unanchored graph and hand off to force. `maxNodes` is checked\n// here because it is upstream of everything that scales with the node count.\nexport function loadGraph(\n text: string,\n name: string,\n opts: { referencePath?: string; maxNodes?: number } = {},\n) {\n const graph = anchorGraph(\n convertGFAToGraph(parseGFA(text), name),\n opts.referencePath,\n )\n if (graph.nodes.length === 0) {\n throw new Error(`No graph segments in ${name}`)\n }\n if (opts.maxNodes !== undefined && graph.nodes.length > opts.maxNodes) {\n throw new Error(\n `Graph too large to draw: ${graph.nodes.length.toLocaleString()} nodes (limit ${opts.maxNodes.toLocaleString()}). Zoom in to a smaller region, or raise maxGraphNodes on this view.`,\n )\n }\n return graph\n}\n\n// What the Bandage engine is handed. A node may carry an `x`/`y` seed, which\n// the engine reads as where its chain starts (referenceSeeds.ts).\nexport interface EngineRequest {\n graph: { nodes: LayoutNode[]; edges: Graph['edges'] }\n options: Record<string, unknown>\n}\n\nexport type LayoutEngine = (\n request: EngineRequest,\n) => Promise<{ result: LayoutResult; duration: number }>\n\nexport interface EngineSettings {\n quality: number\n linearLayout: boolean\n bubbleSpread: BubbleSpread\n}\n\n// `scaling.nodes` rather than the graph's own: under a compressing drawn-length\n// law a node's `length` crosses this boundary as a drawn length, which is all\n// the engine ever reads it as. An anchored graph also carries a seed per node,\n// the backbone along x, and asks the engine not to rotate components: FMMM\n// then keeps the reference's coarse shape instead of curling it into a C\n// (ADR-042).\nexport function engineRequest(\n graph: Graph,\n scaling: LayoutScaling,\n settings: EngineSettings,\n): EngineRequest {\n const anchored = graph.nodes.some(isBackbone)\n return {\n graph: {\n nodes: anchored ? seededNodes(graph, scaling) : scaling.nodes,\n edges: graph.edges,\n },\n options: {\n quality: settings.quality,\n linearLayout: settings.linearLayout,\n ...scaling.opts,\n ...(anchored ? { rotateComponents: false } : {}),\n },\n }\n}\n\n// What a force layout of this graph is a function of, as a cache key: the\n// settings `engineRequest` reads, whether it seeds, and the reference path the\n// seeds follow.\nexport function engineKey(graph: Graph, settings: EngineSettings) {\n const anchored = graph.nodes.some(isBackbone)\n return `${settings.quality}|${settings.linearLayout}|${settings.bubbleSpread}|${anchored}|${graph.referencePath ?? ''}`\n}\n\n// The engine lays out the runs, not the nodes: a base-level cut is thousands\n// of nodes in unbranching chains, and one chain per run is the same drawing at\n// a third of the time. Members take their share of the run's polyline by drawn\n// length, so the picture is per node again before anything else sees it, and\n// it is turned so the reference reads left to right.\nexport async function forceLayout(\n graph: Graph,\n settings: EngineSettings,\n engine: LayoutEngine,\n) {\n const spread = spreadFor(settings.bubbleSpread)\n const merged = mergeRuns(graph)\n const { result, duration } = await engine(\n engineRequest(merged.graph, layoutScaling(merged.graph, spread), settings),\n )\n const scaling = layoutScaling(graph, spread)\n const drawn = new Map(\n scaling.nodes.map(n => [n.id, drawnNodeLength(scaling.opts, n.length)]),\n )\n const positions = splitRuns(\n result.nodePositions,\n merged.runs,\n id => drawn.get(id) ?? 0,\n )\n return {\n result: {\n ...result,\n nodePositions: graph.nodes.some(isBackbone)\n ? orientToReference(graph, positions)\n : positions,\n },\n duration,\n }\n}\n\nexport function layoutExtent(\n nodePositions: Record<string, NodeSegment[]>,\n): Bounds {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const segments of Object.values(nodePositions)) {\n for (const seg of segments) {\n minX = Math.min(minX, seg.x)\n minY = Math.min(minY, seg.y)\n maxX = Math.max(maxX, seg.x)\n maxY = Math.max(maxY, seg.y)\n }\n }\n return { minX, minY, maxX, maxY }\n}\n\n// Gap between the drawing and the edge of the pane, on all four sides.\nexport const FIT_PADDING = 40\n\n// The floor keeps a scale positive and finite, and has to clear the smallest\n// scale a real layout asks for: fitting a chromosome-scale rGFA (250 Mbp) into\n// ~720 px needs ~3e-6.\nconst MIN_ZOOM = 1e-6\nconst MAX_ZOOM = 100\n\nexport function clampZoom(zoom: number) {\n return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom))\n}\n\n// Centre the leftover, but only when there IS leftover. A row layout fits on x\n// alone, so its drawing is routinely taller than the pane, and centring an\n// overflow would push the top row, the reference backbone, off the top.\nexport function fittedTranslateY(\n bounds: { minY: number; h: number },\n usableHeight: number,\n yScale: number,\n padTop = FIT_PADDING,\n) {\n const leftoverY = usableHeight - bounds.h * yScale\n return padTop - bounds.minY * yScale + Math.max(0, leftoverY) / 2\n}\n\n// The transform that fits `bounds` in the pane, or undefined when there is\n// nothing to fit or nothing to fit into. Each axis constrains the scale only\n// when it has extent, since an anchored window holding only backbone puts every\n// node on row 0. A row layout (`pixelRows`) fits on x alone and keeps y in\n// screen px; an isotropic one fits on whichever axis binds. Below `minScale`\n// the drawing overflows x instead, its left end at the padding. `padLeft`\n// widens the left gap past the row labels pinned to that edge, `padTop` the\n// top one for the gene rows over a tube map.\nexport function fitTransform(\n bounds: { minX: number; minY: number; w: number; h: number },\n paneWidth: number,\n paneHeight: number,\n pixelRows: boolean,\n {\n minScale = 0,\n padLeft = FIT_PADDING,\n padTop = FIT_PADDING,\n padRight = FIT_PADDING,\n padBottom = FIT_PADDING,\n } = {},\n) {\n const usableWidth = paneWidth - padLeft - padRight\n const usableHeight = paneHeight - padTop - padBottom\n if (\n usableWidth <= 0 ||\n usableHeight <= 0 ||\n !(bounds.w > 0 || (!pixelRows && bounds.h > 0))\n ) {\n return undefined\n }\n const fitX = bounds.w > 0 ? usableWidth / bounds.w : Infinity\n const fitY = bounds.h > 0 ? usableHeight / bounds.h : Infinity\n const scale = clampZoom(\n Math.max(minScale, pixelRows ? fitX : Math.min(fitX, fitY)),\n )\n const leftoverX = usableWidth - bounds.w * scale\n return {\n scale,\n translateX: padLeft - bounds.minX * scale + Math.max(0, leftoverX) / 2,\n translateY: fittedTranslateY(\n bounds,\n usableHeight,\n pixelRows ? 1 : scale,\n padTop,\n ),\n }\n}\n\n// Extent of the drawing in layout units, which the fit and the pane height\n// read. On a reference-bp layout x is the cut window rather than how far the\n// drawing reaches: an allele anchored far outside it is a fact about the\n// graph, not a reason to draw the window at 6% of the frame. `extent` stands\n// in for the layout's own when an overlay's rows reach further (walk rows).\nexport function drawingBounds(\n layout: LayoutResult,\n opts: {\n region?: { start: number; end: number }\n extent?: LayoutResult['extent']\n } = {},\n) {\n let { minX, minY, maxX, maxY } = layoutExtent(layout.nodePositions)\n const { region } = opts\n if (layout.referenceAxis && region && region.end > region.start) {\n minX = region.start\n maxX = region.end\n }\n const extent = opts.extent ?? layout.extent\n if (extent) {\n minX = Math.min(minX, extent.minX ?? minX)\n minY = Math.min(minY, extent.minY ?? minY)\n maxX = Math.max(maxX, extent.maxX ?? maxX)\n maxY = Math.max(maxY, extent.maxY ?? maxY)\n }\n return { minX, minY, w: maxX - minX, h: maxY - minY }\n}\n", "import { abgrToCssRgba } from '@jbrowse/core/util/colorBits'\n\nimport { isBackbone } from './anchoredNodes'\nimport {\n FADED_ALPHA,\n LIFT_BACKDROP_COLOR,\n computeColorSchemeRange,\n getNodeColor,\n} from './renderer/GeometryBuilder'\nimport { fadeAbgr } from './renderer/colorBits'\n\nimport type { ResolvedColorScheme } from './colorSchemes'\nimport type { ReferenceRamp } from './renderer/GeometryBuilder'\nimport type { Graph, NodeSegment } from './types'\n\n// A force or ordered drawing inside a linear view has no bp axis, so a strip\n// along the top of the track draws each reference segment at its bp there, in\n// the colour its node has in the graph below, faded where the graph fades it\n// off a lifted walk. Under the reference-position ramp that is the hue a\n// reader matches between the two; the lit node gets a\n// leader from its span on the strip to where the graph drew it, as the\n// variant matrix ties a column to its variant.\n\nexport const REFERENCE_STRIP_PX = 10\n// the strip, and the gap under it the fit leaves the drawing\nexport const REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8\n// a triangle at a strip end: reference the graph draws runs past that edge\nconst OVERHANG_PX = 5\n\nexport interface StripBlock {\n node: string\n bp0: number\n bp1: number\n color: string\n}\n\nexport function referenceStripBlocks(\n graph: Graph,\n {\n colorScheme,\n referenceRamp,\n walkNodes,\n walkColors,\n }: {\n colorScheme: ResolvedColorScheme\n referenceRamp?: ReferenceRamp\n // the nodes lifted walks visit, and the reference walk's lane colours\n // when it is one of them: the strip then takes those, else grey\n walkNodes?: ReadonlySet<string>\n walkColors?: ReadonlyMap<string, number>\n },\n) {\n const range = { ...computeColorSchemeRange(graph), referenceRamp }\n const out: StripBlock[] = []\n graph.nodes.forEach((node, index) => {\n if (isBackbone(node)) {\n const own = walkNodes\n ? (walkColors?.get(node.id) ?? LIFT_BACKDROP_COLOR)\n : getNodeColor(node, index, colorScheme, range)\n out.push({\n node: node.id,\n bp0: node.stable.start,\n bp1: node.stable.start + node.length,\n color: abgrToCssRgba(\n walkNodes && !walkNodes.has(node.id)\n ? fadeAbgr(own, FADED_ALPHA)\n : own,\n ),\n })\n }\n })\n return out.sort((a, b) => a.bp0 - b.bp0)\n}\n\n// bp to screen x, as the linear view places it; a reversed block's is negative\nexport interface BpFrame {\n scale: number\n translateX: number\n}\n\nfunction span(frame: BpFrame, bp0: number, bp1: number): [number, number] {\n const a = bp0 * frame.scale + frame.translateX\n const b = bp1 * frame.scale + frame.translateX\n return a < b ? [a, b] : [b, a]\n}\n\n// A SNP's block is a sliver, so the pointer gets a few px either side of one\nconst HIT_SLOP_PX = 2\n\n// How much backbone the graph draws past each edge of the window, in bp\nexport function stripOverhang(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n width: number,\n) {\n const a = -frame.translateX / frame.scale\n const b = (width - frame.translateX) / frame.scale\n const lo = Math.min(a, b)\n const hi = Math.max(a, b)\n let below = 0\n let above = 0\n for (const block of blocks) {\n below += Math.max(0, Math.min(block.bp1, lo) - block.bp0)\n above += Math.max(0, block.bp1 - Math.max(block.bp0, hi))\n }\n // a reversed window puts low bp on the right\n return frame.scale >= 0\n ? { left: Math.round(below), right: Math.round(above) }\n : { left: Math.round(above), right: Math.round(below) }\n}\n\nexport function stripBlockAt(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n sx: number,\n sy: number,\n) {\n if (sy < 0 || sy > REFERENCE_STRIP_PX + HIT_SLOP_PX) {\n return undefined\n }\n let hit: string | undefined\n let best = HIT_SLOP_PX\n for (const b of blocks) {\n const [x0, x1] = span(frame, b.bp0, b.bp1)\n const dist = Math.max(0, x0 - sx, sx - x1)\n if (dist <= best) {\n best = dist\n hit = b.node\n }\n }\n return hit\n}\n\n// Where the graph drew a node, in screen px: the middle of its polyline\nexport function nodeAnchor(\n segments: readonly NodeSegment[] | undefined,\n toScreen: (p: NodeSegment) => { x: number; y: number },\n) {\n const p = segments?.[Math.floor(segments.length / 2)]\n return p ? toScreen(p) : undefined\n}\n\nexport interface StripLit {\n start: number\n end: number\n anchor?: { x: number; y: number }\n}\n\n// A triangle with its tip on the strip's edge, pointing off it; `inward` is\n// +1 from the left edge and -1 from the right\nfunction drawOverhang(\n ctx: CanvasRenderingContext2D,\n tipX: number,\n inward: number,\n) {\n const mid = REFERENCE_STRIP_PX / 2\n const baseX = tipX + inward * (OVERHANG_PX + 1)\n ctx.moveTo(tipX + inward, mid)\n ctx.lineTo(baseX, mid - OVERHANG_PX / 1.4)\n ctx.lineTo(baseX, mid + OVERHANG_PX / 1.4)\n ctx.closePath()\n}\n\nexport function drawReferenceStrip(\n ctx: CanvasRenderingContext2D,\n blocks: readonly StripBlock[],\n frame: BpFrame,\n {\n width,\n lit,\n darkMode,\n }: {\n width: number\n lit?: StripLit\n darkMode?: boolean\n },\n) {\n const ink = darkMode ? '#ffffff' : '#18181c'\n ctx.fillStyle = darkMode ? '#1f1f1f' : '#ffffff'\n ctx.fillRect(0, 0, width, REFERENCE_STRIP_ZONE_PX)\n for (const b of blocks) {\n const [x0, x1] = span(frame, b.bp0, b.bp1)\n if (x1 >= 0 && x0 <= width) {\n ctx.fillStyle = b.color\n ctx.fillRect(x0, 0, Math.max(x1 - x0, 1), REFERENCE_STRIP_PX)\n }\n }\n const overhang = stripOverhang(blocks, frame, width)\n if (overhang.left > 0 || overhang.right > 0) {\n ctx.beginPath()\n if (overhang.left > 0) {\n drawOverhang(ctx, 0, 1)\n }\n if (overhang.right > 0) {\n drawOverhang(ctx, width, -1)\n }\n ctx.fillStyle = ink\n ctx.fill()\n ctx.strokeStyle = darkMode ? '#1f1f1f' : '#ffffff'\n ctx.lineWidth = 1\n ctx.stroke()\n }\n if (lit) {\n const [x0, x1] = span(frame, lit.start, lit.end)\n const w = Math.max(x1 - x0, 3)\n const left = (x0 + x1) / 2 - w / 2\n ctx.strokeStyle = ink\n ctx.lineWidth = 1.5\n ctx.strokeRect(left, 0.75, w, REFERENCE_STRIP_PX - 1.5)\n if (lit.anchor) {\n ctx.beginPath()\n ctx.moveTo(left + w / 2, REFERENCE_STRIP_PX)\n ctx.lineTo(lit.anchor.x, lit.anchor.y)\n ctx.lineWidth = 1\n ctx.stroke()\n }\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { formatBp } from '../graphLabels'\n\nimport type { TubeMapFrame } from './draw'\nimport type { Graph } from '../types'\nimport type { TubeMapLayout } from '@gmod/tubemap-core'\n\n// The reference's boxes carry bp, so a reference coordinate maps into tube x\n// through the box that holds it. On the own axis a box is log-scaled, so bp\n// per tube px varies box to box; on the reference axis frame.ts warps the\n// result back onto the linear view's bp.\n\nexport interface Box {\n bp0: number\n bp1: number\n x0: number\n x1: number\n}\n\n// keyed by the graph's refName, `GRCh38#0#chr6`\nexport function referenceBoxes(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const byRefName = new Map<string, Box[]>()\n // sparse: an unreached node has no entry\n layout.nodes.forEach(node => {\n const graphNode = nodeById.get(node.name)\n if (node.order >= 0 && graphNode && isBackbone(graphNode)) {\n const { refName, start } = graphNode.stable\n const boxes =\n byRefName.get(refName) ?? byRefName.set(refName, []).get(refName)!\n boxes.push({\n bp0: start,\n bp1: start + node.sequenceLength,\n x0: node.x,\n x1: node.x + node.pixelWidth,\n })\n }\n })\n for (const boxes of byRefName.values()) {\n boxes.sort((a, b) => a.bp0 - b.bp0)\n }\n return byRefName\n}\n\nexport type ReferenceBoxes = ReturnType<typeof referenceBoxes>\n\n// A bp the cut has no box for (a gap in the backbone) takes the next box's\n// left edge. A bp on a boundary between boxes belongs to the box after it, or\n// to the one before for the end of a span, so a span stays off the curves\n// between.\nexport function tubeX(boxes: readonly Box[], bp: number, isEnd = false) {\n let lo = 0\n let hi = boxes.length - 1\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n const bp1 = boxes[mid]!.bp1\n if (isEnd ? bp1 < bp : bp1 <= bp) {\n lo = mid + 1\n } else {\n hi = mid\n }\n }\n const box = boxes[lo]!\n if (bp <= box.bp0) {\n return box.x0\n }\n const span = box.bp1 - box.bp0\n return span > 0\n ? box.x0 + ((Math.min(bp, box.bp1) - box.bp0) / span) * (box.x1 - box.x0)\n : box.x0\n}\n\nexport function tubeSpan(boxes: readonly Box[], start: number, end: number) {\n const a = tubeX(boxes, start)\n const b = tubeX(boxes, end, true)\n return { x0: Math.min(a, b), x1: Math.max(a, b) }\n}\n\n// The refName most of the cut's reference lies on, for the ruler\nexport function rulerBoxes(byRefName: ReferenceBoxes) {\n let best: Box[] | undefined\n for (const boxes of byRefName.values()) {\n if (!best || boxes.length > best.length) {\n best = boxes\n }\n }\n return best\n}\n\nconst TICK_PX = 5\nconst LABEL_GAP_PX = 10\nconst TARGET_TICK_PX = 110\n// narrower than this a box draws as one tick rather than a bracket\nconst MIN_BRACKET_PX = 3\n\nexport function rulerInk(darkMode?: boolean) {\n return darkMode ? '#b0b0b8' : '#55555c'\n}\n\ninterface Label {\n text: string\n at: number\n width: number\n}\n\nfunction niceStep(raw: number) {\n const pow = 10 ** Math.floor(Math.log10(raw))\n const m = raw / pow\n return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow\n}\n\n// In order, dropping any that would overlap one already placed\nfunction placeLabels(candidates: readonly Label[]) {\n const placed: Label[] = []\n for (const a of candidates) {\n if (\n placed.every(\n b => Math.abs(a.at - b.at) >= (a.width + b.width) / 2 + LABEL_GAP_PX,\n )\n ) {\n placed.push(a)\n }\n }\n return placed.map(({ text, at }) => ({ text, at }))\n}\n\nexport interface RulerMarks {\n // each box's span; the gaps between them stay blank, since the lane changes\n // drawn there cover no reference\n spans: { x0: number; x1: number }[]\n ticks: number[]\n labels: { text: string; at: number }[]\n}\n\n// On the reference axis the boxes sit on their bp, so one scale runs across\n// the ruler and its ticks fall on round positions.\nfunction scaleMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n): RulerMarks | undefined {\n const first = boxes[0]\n const last = boxes.at(-1)\n if (!first || !last) {\n return undefined\n }\n const from = x(first.x0)\n const to = x(last.x1)\n const bpSpan = last.bp1 - first.bp0\n if (to <= from || bpSpan <= 0) {\n return undefined\n }\n const step = niceStep((bpSpan / (to - from)) * TARGET_TICK_PX)\n const ticks: number[] = []\n const positions: Label[] = []\n for (\n let bp = Math.ceil(first.bp0 / step) * step;\n bp <= last.bp1;\n bp += step\n ) {\n const sx = Math.round(x(tubeX(boxes, bp))) + 0.5\n if (sx >= 0 && sx <= width) {\n ticks.push(sx)\n const text = bp.toLocaleString('en-US')\n positions.push({ text, at: sx, width: measure(text) })\n }\n }\n return {\n spans: [{ x0: from, x1: to }],\n ticks,\n labels: placeLabels(positions),\n }\n}\n\n// On the own axis every box is as wide as the log of its length (tubemap-core's\n// compressed widths: 2 bp is 8 px, 100 bp 56, 1 kb 84, 22 kb 121), so no box is\n// to scale and no one scale runs across them. The ruler treats every box alike:\n// a bracket under it, a tick at each end, its length where that fits inside it,\n// and positions at box boundaries. Labels are placed in that order after the\n// two ends of what is on screen.\nfunction boxMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n): RulerMarks | undefined {\n const shown = boxes\n .map(box => ({ box, s0: x(box.x0), s1: x(box.x1) }))\n .filter(({ s0, s1 }) => s1 >= 0 && s0 <= width)\n const first = shown[0]\n const last = shown.at(-1)\n if (!first || !last) {\n return undefined\n }\n const label = (text: string, at: number) => ({\n text,\n at,\n width: measure(text),\n })\n const position = (bp: number, at: number) =>\n label(bp.toLocaleString('en-US'), at)\n const spans: RulerMarks['spans'] = []\n const ticks: number[] = []\n const lengths: Label[] = []\n const boundaries: Label[] = []\n for (const { box, s0, s1 } of shown) {\n spans.push({ x0: s0, x1: s1 })\n ticks.push(Math.round(s0) + 0.5)\n if (s1 - s0 >= MIN_BRACKET_PX) {\n ticks.push(Math.round(s1) + 0.5)\n }\n const length = label(formatBp(box.bp1 - box.bp0), (s0 + s1) / 2)\n if (length.width <= s1 - s0) {\n lengths.push(length)\n }\n if (box !== first.box) {\n boundaries.push(position(box.bp0, s0))\n }\n }\n const ends = [\n position(first.box.bp0, first.s0),\n position(last.box.bp1, last.s1),\n ]\n return {\n spans,\n ticks,\n labels: placeLabels([...ends, ...lengths, ...boundaries]),\n }\n}\n\nexport function rulerMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n referenceAxis = false,\n) {\n return (referenceAxis ? scaleMarks : boxMarks)(boxes, x, width, measure)\n}\n\n// Reference bp along the tubes: round positions on the reference axis, a\n// bracket per box on the own axis\nexport function drawTubeMapRuler(\n ctx: CanvasRenderingContext2D,\n boxes: readonly Box[],\n frame: TubeMapFrame,\n top: number,\n referenceAxis = false,\n) {\n const { x, width, darkMode } = frame\n ctx.font = '10px sans-serif'\n const marks = rulerMarks(\n boxes,\n x,\n width,\n text => ctx.measureText(text).width,\n referenceAxis,\n )\n if (!marks) {\n return\n }\n const ink = rulerInk(darkMode)\n const y = top + 0.5\n ctx.strokeStyle = ink\n ctx.fillStyle = ink\n ctx.lineWidth = 1\n ctx.textAlign = 'center'\n ctx.textBaseline = 'top'\n ctx.beginPath()\n for (const { x0, x1 } of marks.spans) {\n ctx.moveTo(Math.max(0, x0), y)\n ctx.lineTo(Math.min(width, x1), y)\n }\n for (const sx of marks.ticks) {\n ctx.moveTo(sx, top)\n ctx.lineTo(sx, top + TICK_PX)\n }\n ctx.stroke()\n for (const { text, at } of marks.labels) {\n ctx.fillText(text, at, top + TICK_PX + 2)\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Graph } from '../types'\nimport type { TubeMapLayout } from '@gmod/tubemap-core'\n\n// On its own axis a tube map keeps none of the linear view's bp: a box is as\n// wide as log2 of its length, and the lane changes between boxes take px that\n// cover no reference at all. A band from each reference box up to the bp it\n// covers on the linear view's ruler ties the two back together, the way the\n// LD display ties its matrix columns to their variants.\n\nexport interface ReferenceNode {\n node: string\n bp0: number\n bp1: number\n x0: number\n x1: number\n}\n\n// A reference node's band, in screen px: its bp span along the top of the\n// zone, its box's span along the bottom\nexport interface Connector {\n node: string\n top0: number\n top1: number\n bottom0: number\n bottom1: number\n}\n\nexport function referenceNodes(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const out: ReferenceNode[] = []\n // sparse: an unreached node has no entry\n layout.nodes.forEach(node => {\n const graphNode = nodeById.get(node.name)\n if (node.order >= 0 && graphNode && isBackbone(graphNode)) {\n out.push({\n node: node.name,\n bp0: graphNode.stable.start,\n bp1: graphNode.stable.start + node.sequenceLength,\n x0: node.x,\n x1: node.x + node.pixelWidth,\n })\n }\n })\n return out\n}\n\n// The bands whose box is on screen. A band's top may still run off the side,\n// which is the linear view saying that box is outside its window.\nexport function tubeMapConnectors(\n nodes: readonly ReferenceNode[],\n bpToScreen: (bp: number) => number,\n tubeToScreen: (tx: number) => number,\n width: number,\n): Connector[] {\n const out: Connector[] = []\n for (const n of nodes) {\n const bottom0 = tubeToScreen(n.x0)\n const bottom1 = tubeToScreen(n.x1)\n if (bottom1 >= 0 && bottom0 <= width) {\n out.push({\n node: n.node,\n top0: bpToScreen(n.bp0),\n top1: bpToScreen(n.bp1),\n bottom0,\n bottom1,\n })\n }\n }\n return out\n}\n\n// A SNP's band is a sliver, so the pointer gets a few px either side of one\nconst HIT_SLOP_PX = 3\n\nexport function connectorAt(\n connectors: readonly Connector[],\n zoneBottom: number,\n sx: number,\n sy: number,\n) {\n if (zoneBottom <= 0 || sy < 0 || sy > zoneBottom) {\n return undefined\n }\n const t = sy / zoneBottom\n let hit: string | undefined\n let best = HIT_SLOP_PX\n for (const c of connectors) {\n const a = c.top0 + (c.bottom0 - c.top0) * t\n const b = c.top1 + (c.bottom1 - c.top1) * t\n const dist = Math.max(0, Math.min(a, b) - sx, sx - Math.max(a, b))\n if (dist < best) {\n best = dist\n hit = c.node\n }\n }\n return hit\n}\n\nfunction band(ctx: CanvasRenderingContext2D, c: Connector, bottom: number) {\n ctx.moveTo(c.top0, 0)\n ctx.lineTo(c.top1, 0)\n ctx.lineTo(c.bottom1, bottom)\n ctx.lineTo(c.bottom0, bottom)\n ctx.closePath()\n}\n\nfunction sides(ctx: CanvasRenderingContext2D, c: Connector, bottom: number) {\n ctx.moveTo(c.top0, 0)\n ctx.lineTo(c.bottom0, bottom)\n ctx.moveTo(c.top1, 0)\n ctx.lineTo(c.bottom1, bottom)\n}\n\n// One fill and one stroke for every band, so where hundreds of them converge\n// on the ruler the ink composites once rather than going black. The lit band\n// takes the tube map's own highlight.\nexport function drawTubeMapConnectors(\n ctx: CanvasRenderingContext2D,\n connectors: readonly Connector[],\n bottom: number,\n {\n highlightNode,\n darkMode,\n }: { highlightNode?: string | null; darkMode?: boolean },\n) {\n if (bottom <= 0 || connectors.length === 0) {\n return\n }\n const ink = darkMode ? '255,255,255' : '0,0,0'\n ctx.beginPath()\n for (const c of connectors) {\n band(ctx, c, bottom)\n }\n ctx.fillStyle = `rgba(${ink},0.07)`\n ctx.fill()\n ctx.beginPath()\n for (const c of connectors) {\n sides(ctx, c, bottom)\n }\n ctx.strokeStyle = `rgba(${ink},0.35)`\n ctx.lineWidth = 0.5\n ctx.stroke()\n const lit = connectors.find(c => c.node === highlightNode)\n if (lit) {\n ctx.beginPath()\n band(ctx, lit, bottom)\n ctx.fillStyle = 'rgba(255,192,203,0.5)'\n ctx.fill()\n ctx.beginPath()\n sides(ctx, lit, bottom)\n ctx.strokeStyle = '#ff0000'\n ctx.lineWidth = 1\n ctx.stroke()\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Deviation } from './coarsen'\nimport type { TubeMapFrame } from './draw'\nimport type { Graph } from '../types'\nimport type { LayoutNode, TubeMapLayout } from '@gmod/tubemap-core'\n\n// A folded variant on the tube of the walk that carries it, in tube\n// coordinates. Placed by bp through the box that holds it, since tubemap-core\n// may merge the coarsened nodes again and a box can stand for several.\nexport interface DeviationMark {\n x0: number\n x1: number\n y: number\n height: number\n}\n\ninterface Box {\n bp0: number\n bp1: number\n index: number\n node: LayoutNode\n}\n\nfunction boxesOf(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const boxes: Box[] = []\n layout.nodes.forEach((node, index) => {\n const g = nodeById.get(node.name)\n if (node.order >= 0 && g && isBackbone(g)) {\n const bp0 = g.stable.start\n boxes.push({ bp0, bp1: bp0 + node.sequenceLength, index, node })\n }\n })\n return boxes.sort((a, b) => a.bp0 - b.bp0)\n}\n\n// The box holding `bp` that this walk passes through; an insertion on a\n// boundary between two boxes sits on whichever the walk visits\nfunction boxAt(\n boxes: readonly Box[],\n bp: number,\n visited: Map<number, number>,\n) {\n let lo = 0\n let hi = boxes.length - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (boxes[mid]!.bp0 <= bp) {\n lo = mid\n } else {\n hi = mid - 1\n }\n }\n return [boxes[lo], boxes[lo - 1]].find(\n box => box && visited.has(box.index) && box.bp0 <= bp && bp <= box.bp1,\n )\n}\n\nexport function deviationMarks(\n graph: Graph,\n layout: TubeMapLayout,\n deviations: Map<string, Deviation[]>,\n) {\n const boxes = boxesOf(graph, layout)\n const marks: DeviationMark[] = []\n for (const track of layout.tracks) {\n const devs = track.name ? deviations.get(track.name) : undefined\n if (devs?.length) {\n // a walk that loops through a box twice marks its first pass\n const visited = new Map<number, number>()\n for (const segment of track.path) {\n if (\n segment.node !== null &&\n segment.y !== undefined &&\n !visited.has(segment.node)\n ) {\n visited.set(segment.node, segment.y)\n }\n }\n for (const d of devs) {\n const box = boxAt(boxes, d.start, visited)\n if (box) {\n const { node, bp0, bp1 } = box\n const at = (bp: number) =>\n node.x + ((Math.min(bp, bp1) - bp0) / (bp1 - bp0)) * node.pixelWidth\n marks.push({\n x0: at(d.start),\n x1: at(d.end),\n y: visited.get(box.index)!,\n height: track.width,\n })\n }\n }\n }\n }\n return marks\n}\n\n// Dark on light and light on dark, since the tubes themselves carry every hue\nexport function deviationInk(darkMode?: boolean) {\n return darkMode ? 'rgba(255,255,255,0.9)' : 'rgba(0,0,0,0.8)'\n}\n\n// A tick at least a px wide across the tube\nexport function drawDeviationMarks(\n ctx: CanvasRenderingContext2D,\n marks: readonly DeviationMark[],\n { x, y, width, darkMode }: TubeMapFrame,\n) {\n if (marks.length === 0) {\n return\n }\n ctx.fillStyle = deviationInk(darkMode)\n ctx.beginPath()\n for (const m of marks) {\n const left = x(m.x0)\n const right = Math.max(x(m.x1), left + 1)\n if (right >= 0 && left <= width) {\n const top = y(m.y)\n ctx.rect(left, top, right - left, y(m.y + m.height) - top)\n }\n }\n ctx.fill()\n}\n", "import { forward, getXCoordinateOfBaseWithinNode } from '@gmod/tubemap-core'\n\nimport type { Track, TubeMapLayout } from '@gmod/tubemap-core'\n\n// Where a read differs from the nodes it runs through, in tube coordinates:\n// the marks sequenceTubeMap's drawMismatches puts on a read's tube. Each mark\n// spans the bases it covers and sits on the tube of the read that carries it\n// (`y` is the tube's top, `height` its width), so a painter only maps them\n// through the frame. `nodeY` is the top of the node, which a hover draws a\n// leader up to.\ninterface MarkBase {\n readId: number\n y: number\n height: number\n nodeY: number\n}\n\nexport type TubeMapMismatch =\n | (MarkBase & {\n kind: 'insertion'\n x: number\n seq?: string\n // at the read's first or last base, where an aligner clips rather than\n // inserts; sequenceTubeMap hides these unless soft clips are shown\n softClip: boolean\n })\n | (MarkBase & { kind: 'deletion'; x0: number; x1: number })\n | (MarkBase & { kind: 'substitution'; x0: number; x1: number; seq: string })\n\nfunction readMismatches(read: Track, layout: TubeMapLayout) {\n const marks: TubeMapMismatch[] = []\n const entries = read.sequenceNew ?? []\n entries.forEach((entry, i) => {\n const nodeIndex = layout.nodeMap.get(forward(entry.nodeName))\n const node = nodeIndex === undefined ? undefined : layout.nodes[nodeIndex]\n // Node merging can drop a visit, so the segment for entry i is at or after\n // path[i], and the walk stops at the path's end.\n let pathIndex = i\n while (\n pathIndex < read.path.length &&\n read.path[pathIndex]!.node !== nodeIndex\n ) {\n pathIndex += 1\n }\n const y = read.path[pathIndex]?.y\n if (node && y !== undefined) {\n const base = { readId: read.id, y, height: read.width, nodeY: node.y }\n // A base past a merged node's end has no x.\n const at = (pos: number) => getXCoordinateOfBaseWithinNode(node, pos)\n for (const mm of entry.mismatches) {\n const x0 = at(mm.pos)\n if (x0 !== null) {\n if (mm.type === 'insertion') {\n const softClip =\n (i === 0 && mm.pos === read.firstNodeOffset) ||\n (i === entries.length - 1 && mm.pos === read.finalNodeCoverLength)\n marks.push({\n ...base,\n kind: 'insertion',\n x: x0,\n seq: mm.seq,\n softClip,\n })\n } else if (mm.type === 'deletion' && mm.length !== undefined) {\n const x1 = at(mm.pos + mm.length)\n if (x1 !== null) {\n marks.push({ ...base, kind: 'deletion', x0, x1 })\n }\n } else if (mm.type === 'substitution' && mm.seq !== undefined) {\n const x1 = at(mm.pos + mm.seq.length)\n if (x1 !== null) {\n marks.push({ ...base, kind: 'substitution', x0, x1, seq: mm.seq })\n }\n }\n }\n }\n }\n })\n return marks\n}\n\nexport function tubeMapMismatches(layout: TubeMapLayout) {\n return layout.reads.flatMap(read => readMismatches(read, layout))\n}\n", "import { curvePaths, nodeOutlinePath } from '@gmod/tubemap-core'\n\nimport { tubeMapMismatches } from './mismatches'\n\nimport type { TubeMapTransform } from './frame'\nimport type { TubeMapMismatch } from './mismatches'\nimport type { TrackType, TubeMapLayout } from '@gmod/tubemap-core'\n\n// Paints a tube map layout on a 2D canvas, in the order sequenceTubeMap's d3\n// drawing stacks it: haplotype tubes, their lane changes and turnarounds, then\n// reads the same way, then the node boxes over all of them. Everything goes\n// through `x` and `y`, tube coordinates to screen px, so the one painter serves\n// the tube map's own axis (an affine map) and the reference axis (warp.ts).\n//\n// The package states curves, corners and node outlines as SVG path data. They\n// are parsed once per layout into commands whose points can be mapped, since\n// a Path2D built from the string could only be transformed affinely.\n\ntype Command =\n | { op: 'M' | 'L'; x: number; y: number }\n | { op: 'Q'; x1: number; y1: number; x: number; y: number }\n | {\n op: 'C'\n x1: number\n y1: number\n x2: number\n y2: number\n x: number\n y: number\n }\n | { op: 'H'; x: number }\n | { op: 'V'; y: number }\n | { op: 'Z' }\n\n// The absolute M/L/H/V/Q/C/Z subset tubemap-core writes\nexport function parsePath(d: string): Command[] {\n const tokens = d.trim().split(/[\\s,]+/)\n const commands: Command[] = []\n let i = 0\n const num = () => Number(tokens[i++])\n while (i < tokens.length) {\n const op = tokens[i++]\n if (op === 'M' || op === 'L') {\n commands.push({ op, x: num(), y: num() })\n } else if (op === 'H') {\n commands.push({ op, x: num() })\n } else if (op === 'V') {\n commands.push({ op, y: num() })\n } else if (op === 'Q') {\n commands.push({ op, x1: num(), y1: num(), x: num(), y: num() })\n } else if (op === 'C') {\n commands.push({\n op,\n x1: num(),\n y1: num(),\n x2: num(),\n y2: num(),\n x: num(),\n y: num(),\n })\n } else if (op === 'Z') {\n commands.push({ op })\n }\n }\n return commands\n}\n\ninterface Filled {\n color: string\n alpha: number\n // tube x extent, for culling\n x0: number\n x1: number\n}\n\ninterface Rect extends Filled {\n y0: number\n y1: number\n}\n\ninterface Shape extends Filled {\n commands: Command[]\n}\n\ninterface Layer {\n rects: Rect[]\n shapes: Shape[]\n}\n\nexport interface TubeMapPicture {\n bounds: TubeMapLayout['bounds']\n layers: Layer[]\n nodes: { name: string; commands: Command[]; x0: number; x1: number }[]\n mismatches: TubeMapMismatch[]\n}\n\nfunction extentOf(commands: Command[]) {\n let x0 = Infinity\n let x1 = -Infinity\n for (const c of commands) {\n if ('x' in c) {\n x0 = Math.min(x0, c.x)\n x1 = Math.max(x1, c.x)\n }\n if ('x1' in c) {\n x0 = Math.min(x0, c.x1)\n x1 = Math.max(x1, c.x1)\n }\n }\n return { x0, x1 }\n}\n\nfunction shapeOf(d: string, color: string, alpha: number): Shape {\n const commands = parsePath(d)\n return { commands, color, alpha, ...extentOf(commands) }\n}\n\nfunction layerOf(layout: TubeMapLayout, type: TrackType): Layer {\n const { shapes } = layout\n const rects = [...shapes.rectangles, ...shapes.verticalRectangles]\n .filter(r => r.type === type)\n .map(r => ({\n color: r.color,\n alpha: r.alpha ?? 1,\n x0: r.xStart,\n x1: r.xEnd + 1,\n y0: r.yStart,\n y1: r.yEnd + 1,\n }))\n const curves = curvePaths(shapes.curves, type).map(c =>\n shapeOf(c.path!, c.color, c.alpha ?? 1),\n )\n const corners = shapes.corners\n .filter(c => c.type === type)\n .map(c => shapeOf(c.path, c.color, 1))\n return { rects, shapes: [...curves, ...corners] }\n}\n\n// Everything a frame needs that does not depend on the transform\nexport function tubeMapPicture(layout: TubeMapLayout): TubeMapPicture {\n const nodes: TubeMapPicture['nodes'] = []\n layout.nodes.forEach(node => {\n if (node.order >= 0) {\n const commands = parsePath(nodeOutlinePath(node))\n nodes.push({ name: node.name, commands, ...extentOf(commands) })\n }\n })\n return {\n bounds: layout.bounds,\n layers: [layerOf(layout, 'haplotype'), layerOf(layout, 'read')],\n nodes,\n mismatches: tubeMapMismatches(layout).filter(\n m => m.kind !== 'insertion' || !m.softClip,\n ),\n }\n}\n\nfunction trace(\n ctx: CanvasRenderingContext2D,\n commands: Command[],\n x: (tx: number) => number,\n y: (ty: number) => number,\n) {\n let cx = 0\n let cy = 0\n for (const c of commands) {\n switch (c.op) {\n case 'M':\n cx = c.x\n cy = c.y\n ctx.moveTo(x(cx), y(cy))\n break\n case 'L':\n cx = c.x\n cy = c.y\n ctx.lineTo(x(cx), y(cy))\n break\n case 'H':\n cx = c.x\n ctx.lineTo(x(cx), y(cy))\n break\n case 'V':\n cy = c.y\n ctx.lineTo(x(cx), y(cy))\n break\n case 'Q':\n cx = c.x\n cy = c.y\n ctx.quadraticCurveTo(x(c.x1), y(c.y1), x(cx), y(cy))\n break\n case 'C':\n cx = c.x\n cy = c.y\n ctx.bezierCurveTo(x(c.x1), y(c.y1), x(c.x2), y(c.y2), x(cx), y(cy))\n break\n case 'Z':\n ctx.closePath()\n break\n }\n }\n}\n\nexport interface TubeMapFrame extends TubeMapTransform {\n width: number\n highlightNode?: string | null\n darkMode?: boolean\n}\n\n// One fill per colour per layer: a layer's shapes do not overlap in a way the\n// order within it would decide, and a fill per shape costs a draw call each.\nfunction fillByColor<T extends Filled>(\n ctx: CanvasRenderingContext2D,\n items: T[],\n visible: (item: T) => boolean,\n addTo: (item: T) => void,\n) {\n const byColor = new Map<string, T[]>()\n for (const item of items) {\n if (visible(item)) {\n const key = `${item.color}|${item.alpha}`\n const list = byColor.get(key)\n if (list) {\n list.push(item)\n } else {\n byColor.set(key, [item])\n }\n }\n }\n for (const list of byColor.values()) {\n ctx.fillStyle = list[0]!.color\n ctx.globalAlpha = list[0]!.alpha\n ctx.beginPath()\n for (const item of list) {\n addTo(item)\n }\n ctx.fill()\n }\n ctx.globalAlpha = 1\n}\n\n// Zoomed out, a box is a few px of outline and a cut's boxes ink over the\n// tubes between them, so an outline fades as its box narrows\nconst FULL_OUTLINE_PX = 12\nconst MIN_OUTLINE_ALPHA = 0.15\n\nfunction outlineAlpha(screenWidth: number) {\n return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX))\n}\n\nexport function drawTubeMap(\n ctx: CanvasRenderingContext2D,\n picture: TubeMapPicture,\n frame: TubeMapFrame,\n) {\n const { x, y, width } = frame\n const visible = (item: { x0: number; x1: number }) =>\n x(item.x1) >= 0 && x(item.x0) <= width\n for (const layer of picture.layers) {\n fillByColor(ctx, layer.rects, visible, r => {\n const left = x(r.x0)\n const top = y(r.y0)\n ctx.rect(left, top, x(r.x1) - left, y(r.y1) - top)\n })\n fillByColor(ctx, layer.shapes, visible, s => {\n trace(ctx, s.commands, x, y)\n })\n }\n const stroke = frame.darkMode ? '#d0d0d0' : '#000000'\n const fill = frame.darkMode ? 'rgba(40,40,40,0.4)' : 'rgba(255,255,255,0.4)'\n ctx.lineWidth = 2 * Math.max(0.25, Math.min(1, frame.yScale))\n for (const node of picture.nodes) {\n if (visible(node)) {\n const lit = node.name === frame.highlightNode\n ctx.beginPath()\n trace(ctx, node.commands, x, y)\n ctx.fillStyle = lit ? 'rgba(255,192,203,0.5)' : fill\n ctx.strokeStyle = lit ? '#ff0000' : stroke\n ctx.fill()\n ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0))\n ctx.stroke()\n ctx.globalAlpha = 1\n }\n }\n drawMismatches(ctx, picture.mismatches, frame)\n}\n\n// sequenceTubeMap's marks, which it draws at 12px and hides once zoomed out\n// below half size\nconst MISMATCH_FONT_PX = 12\nconst MIN_MISMATCH_FONT_PX = 6\n\nexport function mismatchesLegible(yScale: number) {\n return MISMATCH_FONT_PX * yScale >= MIN_MISMATCH_FONT_PX\n}\n\nexport function mismatchOnScreen(\n m: TubeMapMismatch,\n { x, width }: Pick<TubeMapFrame, 'x' | 'width'>,\n) {\n const [x0, x1] = m.kind === 'insertion' ? [m.x, m.x] : [m.x0, m.x1]\n return x(x1) >= 0 && x(x0) <= width\n}\n\nfunction drawMismatches(\n ctx: CanvasRenderingContext2D,\n marks: readonly TubeMapMismatch[],\n frame: TubeMapFrame,\n) {\n const { x, y, yScale, darkMode } = frame\n if (!mismatchesLegible(yScale) || marks.length === 0) {\n return\n }\n const fontPx = MISMATCH_FONT_PX * yScale\n ctx.fillStyle = 'grey'\n ctx.beginPath()\n for (const m of marks) {\n if (m.kind === 'deletion' && mismatchOnScreen(m, frame)) {\n const left = x(m.x0)\n const top = y(m.y)\n ctx.rect(left, top, x(m.x1) - left, y(m.y + m.height) - top)\n }\n }\n ctx.fill()\n ctx.font = `${fontPx}px monospace`\n ctx.textBaseline = 'alphabetic'\n ctx.fillStyle = darkMode ? '#ffffff' : '#000000'\n for (const m of marks) {\n if (!mismatchOnScreen(m, frame)) {\n continue\n }\n if (m.kind === 'substitution') {\n ctx.fillText(m.seq, x(m.x0) + 1, y(m.y + m.height))\n } else if (m.kind === 'insertion') {\n ctx.fillText('*', x(m.x) - 3, y(m.y + m.height))\n }\n }\n}\n", "import { tubeSpan } from './axis'\n\nimport type { ReferenceBoxes } from './axis'\nimport type { TubeMapFrame } from './draw'\nimport type { GeneModel } from '../genes/genePins'\n\n// A gene over the tube map, in tube x, mapped through the reference's boxes\n// (axis.ts)\nexport interface TubeMapGene {\n gene: GeneModel\n x0: number\n x1: number\n exons: { x0: number; x1: number }[]\n}\n\n// A gene takes the boxes of the refName it equals, as in genePins\nexport function tubeMapGenes(\n byRefName: ReferenceBoxes,\n genes: readonly GeneModel[],\n): TubeMapGene[] {\n const out: TubeMapGene[] = []\n for (const gene of genes) {\n const boxes = byRefName.get(gene.refName)\n const first = boxes?.[0]\n const last = boxes?.at(-1)\n if (!boxes || !first || !last) {\n continue\n }\n const start = Math.max(gene.start, first.bp0)\n const end = Math.min(gene.end, last.bp1)\n if (end <= start) {\n continue\n }\n out.push({\n gene,\n ...tubeSpan(boxes, start, end),\n exons: gene.exons\n .filter(e => e.end > start && e.start < end)\n .map(e =>\n tubeSpan(boxes, Math.max(e.start, start), Math.min(e.end, end)),\n ),\n })\n }\n return out\n}\n\nexport const GENE_ROW_PX = 22\nconst MAX_GENE_ROWS = 4\nconst EXON_PX = 8\nconst NAME_FONT = '11px sans-serif'\nconst NAME_GAP_PX = 8\n\n// The rows the genes need at least: as many as overlap at one point, which\n// holds at every zoom. A name wider than its gene can want more, and shares\n// the top row.\nexport function tubeMapGeneRows(genes: readonly TubeMapGene[]) {\n const edges = genes\n .flatMap(g => [\n { x: g.x0, step: 1 },\n { x: g.x1, step: -1 },\n ])\n .sort((a, b) => a.x - b.x || a.step - b.step)\n let depth = 0\n let most = 0\n for (const { step } of edges) {\n depth += step\n most = Math.max(most, depth)\n }\n return Math.min(MAX_GENE_ROWS, most)\n}\n\n// Rows of genes above the tubes, bottom row nearest them, packed so neither\n// glyphs nor names overlap. Packed per frame, since the reference axis warps x\n// by zoom. The rows go up to `top`; past that, genes share the top row.\nexport function drawTubeMapGenes(\n ctx: CanvasRenderingContext2D,\n genes: readonly TubeMapGene[],\n frame: TubeMapFrame,\n bottom: number,\n top = 0,\n) {\n const { x, width, darkMode } = frame\n const rows = Math.max(1, Math.floor((bottom - top) / GENE_ROW_PX))\n const rowEnds: number[] = []\n const ink = darkMode ? '#e0e0e6' : '#1c1c22'\n ctx.font = NAME_FONT\n ctx.textAlign = 'center'\n ctx.textBaseline = 'alphabetic'\n ctx.fillStyle = ink\n ctx.strokeStyle = ink\n ctx.lineWidth = 1\n const placed = genes\n .map(g => ({ g, s0: x(g.x0), s1: x(g.x1) }))\n .filter(({ s0, s1 }) => s1 >= 0 && s0 <= width)\n .sort((a, b) => a.s0 - b.s0)\n for (const { g, s0, s1 } of placed) {\n const arrow = g.gene.strand > 0 ? ' \u2192' : g.gene.strand < 0 ? '\u2190 ' : ''\n const label =\n g.gene.strand < 0 ? `${arrow}${g.gene.name}` : `${g.gene.name}${arrow}`\n const mid = (Math.max(s0, 0) + Math.min(s1, width)) / 2\n const half = ctx.measureText(label).width / 2\n const left = Math.min(s0, mid - half)\n const right = Math.max(s1, mid + half)\n let row = rowEnds.findIndex(end => end + NAME_GAP_PX <= left)\n if (row === -1) {\n row = Math.min(rowEnds.length, rows - 1)\n }\n rowEnds[row] = Math.max(rowEnds[row] ?? -Infinity, right)\n const glyphY = bottom - row * GENE_ROW_PX - EXON_PX / 2\n ctx.beginPath()\n ctx.moveTo(s0, glyphY + 0.5)\n ctx.lineTo(s1, glyphY + 0.5)\n ctx.stroke()\n for (const e of g.exons) {\n const e0 = x(e.x0)\n ctx.fillRect(e0, glyphY - EXON_PX / 2, Math.max(1, x(e.x1) - e0), EXON_PX)\n }\n ctx.fillText(label, mid, glyphY - EXON_PX / 2 - 2)\n }\n}\n", "import { clampZoom } from './pipeline'\n\nimport type { Bounds } from './pipeline'\nimport type { AxisScale } from './util/geometry'\n\n// The pan and zoom arithmetic a pane runs, in layout units, with no host in it.\n\nexport interface PaneTransform {\n scale: number\n translateX: number\n translateY: number\n}\n\n// The zoom as a pair of axis scales. `pixelRows` rides along rather than being\n// derived downstream: the deletion bow is capped in a row layout and not in\n// the isotropic one, and `scaleY !== scaleX` coincides at one zoom level.\nexport function axisScaleOf(scale: number, pixelRows: boolean): AxisScale {\n return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows }\n}\n\n// What a pane of `width` x `height` css px shows, in layout units.\nexport function viewportOf(\n t: { translateX: number; translateY: number },\n axis: AxisScale,\n width: number,\n height: number,\n): Bounds {\n return {\n minX: -t.translateX / axis.scaleX,\n minY: -t.translateY / axis.scaleY,\n maxX: (width - t.translateX) / axis.scaleX,\n maxY: (height - t.translateY) / axis.scaleY,\n }\n}\n\n// The window a geometry build covers: the pane plus `panes` whole panes on\n// every side, so an ordinary pan never blanks its margins for the debounce and\n// never rebuilds at all.\nexport function padded(v: Bounds, panes: number): Bounds {\n const w = (v.maxX - v.minX) * panes\n const h = (v.maxY - v.minY) * panes\n return {\n minX: v.minX - w,\n minY: v.minY - h,\n maxX: v.maxX + w,\n maxY: v.maxY + h,\n }\n}\n\nexport function contains(outer: Bounds, inner: Bounds) {\n return (\n inner.minX >= outer.minX &&\n inner.maxX <= outer.maxX &&\n inner.minY >= outer.minY &&\n inner.maxY <= outer.maxY\n )\n}\n\n// Zoom by `factor` about a screen point. y only follows when it is on the same\n// scale: a row layout's y is screen px, so moving it by the ratio would slide\n// the rows off under the cursor while their pitch stayed put.\nexport function zoomAbout(\n t: PaneTransform,\n factor: number,\n centerX: number,\n centerY: number,\n pixelRows: boolean,\n): PaneTransform {\n const scale = clampZoom(t.scale * factor)\n const ratio = scale / t.scale\n return {\n scale,\n translateX: centerX - (centerX - t.translateX) * ratio,\n translateY: pixelRows\n ? t.translateY\n : centerY - (centerY - t.translateY) * ratio,\n }\n}\n\n// Where a screen point falls in layout units.\nexport function screenToLayout(\n x: number,\n y: number,\n t: { translateX: number; translateY: number },\n axis: AxisScale,\n) {\n return {\n x: (x - t.translateX) / axis.scaleX,\n y: (y - t.translateY) / axis.scaleY,\n }\n}\n", "import type { SubgraphRegion } from '../GetSubgraph'\n\n// The members of a LinearGenomeView a hosted pane reads and drives.\n// Structural, because this plugin takes no runtime dependency on the LGV\n// plugin (see hoverSync/index.tsx).\nexport interface HostBlock {\n refName: string\n assemblyName: string\n start: number\n end: number\n offsetPx: number\n reversed?: boolean\n displayedRegionIndex?: number\n}\n\nexport interface LinearHost {\n id: string\n initialized: boolean\n bpPerPx: number\n offsetPx: number\n width: number\n displayedRegions: readonly { start: number; end: number }[]\n dynamicBlocks: { contentBlocks: readonly HostBlock[] }\n coarseDynamicBlocks: readonly HostBlock[]\n horizontalScroll: (distance: number) => number\n zoomTo: (bpPerPx: number, offset?: number) => number\n // whether the view's body is mounted, which an off-screen view's is not\n effectiveBodyMounted?: boolean\n hasVisibleContent?: boolean\n}\n\nexport function isLinearHost(view: unknown): view is LinearHost {\n if (typeof view !== 'object' || view === null) {\n return false\n }\n const v = view as Record<string, unknown>\n return (\n typeof v.horizontalScroll === 'function' &&\n typeof v.zoomTo === 'function' &&\n 'coarseDynamicBlocks' in v &&\n 'dynamicBlocks' in v\n )\n}\n\nfunction widest(blocks: readonly HostBlock[]) {\n return blocks.reduce<HostBlock | undefined>(\n (best, b) => (best && best.end - best.start >= b.end - b.start ? best : b),\n undefined,\n )\n}\n\nexport interface HostWindow {\n refName: string\n assemblyName: string\n start: number\n end: number\n bpPerPx: number\n // the view's width in bp, which the margin is sized by: a block at a contig\n // end is clipped, the zoom is not\n span: number\n regionStart: number\n regionEnd: number\n}\n\n// x is reference bp on an anchored layout, so the graph shows what the linear\n// view shows when one bp is as many px in both and the cut's refName sits at\n// the same screen x. Undefined when the linear view shows no block of that\n// refName, which is a re-cut's job rather than the frame's. A reversed block\n// runs bp right to left, which only the tube map's connectors can draw.\nexport function hostFrame(view: LinearHost, region: SubgraphRegion) {\n const block = widest(\n view.dynamicBlocks.contentBlocks.filter(\n b =>\n b.refName === region.refName && b.assemblyName === region.assemblyName,\n ),\n )\n const { bpPerPx } = view\n if (!block || !(bpPerPx > 0)) {\n return undefined\n }\n const left = block.offsetPx - view.offsetPx\n return block.reversed\n ? { scale: -1 / bpPerPx, translateX: left + block.end / bpPerPx }\n : { scale: 1 / bpPerPx, translateX: left - block.start / bpPerPx }\n}\n\n// Read off the LIVE blocks even when the coarse ones woke the caller: those\n// name where the window was half a second ago (SyntenyFollow/CLAUDE.md).\nexport function hostWindow(view: LinearHost): HostWindow | undefined {\n const block = widest(view.dynamicBlocks.contentBlocks)\n const displayed =\n block?.displayedRegionIndex === undefined\n ? undefined\n : view.displayedRegions[block.displayedRegionIndex]\n return block && view.bpPerPx > 0\n ? {\n refName: block.refName,\n assemblyName: block.assemblyName,\n start: block.start,\n end: block.end,\n bpPerPx: view.bpPerPx,\n span: view.bpPerPx * view.width,\n regionStart: displayed?.start ?? block.start,\n regionEnd: displayed?.end ?? block.end,\n }\n : undefined\n}\n\n// The window plus one window-width each side, clamped to the displayed\n// region, with the margins narrowed until the whole cut fits under `capBp`.\nexport function hostCut(\n window: HostWindow,\n capBp: number,\n margins = true,\n): SubgraphRegion {\n const visible = window.end - window.start\n const margin = margins\n ? Math.max(0, Math.min(window.span, (capBp - visible) / 2))\n : 0\n let start = Math.max(window.regionStart, Math.floor(window.start - margin))\n let end = Math.min(window.regionEnd, Math.ceil(window.end + margin))\n // Rounding outward can put a cut a base or two past the cap, which the load\n // refuses whole; trim it back rather than lose the cut.\n const over = end - start - capBp\n if (over > 0) {\n start += Math.floor(over / 2)\n end -= Math.ceil(over / 2)\n }\n return {\n refName: window.refName,\n assemblyName: window.assemblyName,\n start,\n end,\n }\n}\n\n// With margins, a cut holds while the window is inside it; without, only the\n// window itself does, since the drawing is the window.\nexport function cutHolds(\n cut: SubgraphRegion | undefined,\n window: HostWindow,\n margins = true,\n) {\n if (\n cut?.refName !== window.refName ||\n cut.assemblyName !== window.assemblyName\n ) {\n return false\n }\n // to the base, since a cut trimmed to its cap can end a base inside the window\n if (margins) {\n return cut.start <= window.start + 1 && window.end <= cut.end + 1\n }\n const exact = hostCut(window, Infinity, false)\n return (\n Math.abs(cut.start - exact.start) < 1 && Math.abs(cut.end - exact.end) < 1\n )\n}\n", "import type { RepeatCall } from './repeatFeatures'\n\n// Which genotyped allele belongs to which haplotype walk. A VCF states a\n// sample's alleles in genotype order, which is the genotyper's own order and\n// carries no correspondence to the assembly's haplotype numbering \u2014 TRGTdb's\n// ABCA7 record, for one, lists them shortest first. So the walks and the\n// alleles are paired by length instead: the pairing whose total difference is\n// smallest, which for lengths on a line is the non-crossing one.\n//\n// A walk that does not reach both flanking reference nodes is measured whole\n// rather than across the array, and an allele no read spanned is a copy of the\n// one that was called. Neither states a length for this array, so they pair\n// last and take no verdict \u2014 pairing them first let a walk that stops inside\n// the array take the allele that fit one spanning it, and the spanning walk\n// then read as a disagreement.\n\nexport interface WalkCall extends RepeatCall {\n // undefined where the walk and the allele are not both measurements\n agrees?: boolean\n}\n\n// Every walk/call pair over the 94 HPRC samples TRGT genotyped at the ABCA7\n// VNTR is either within 8% of its allele or more than 14% from it, so any\n// threshold across that gap separates them the same way.\nexport const CALL_TOLERANCE = 0.1\n\nfunction agrees(walkBp: number, callBp: number) {\n return Math.abs(callBp - walkBp) <= CALL_TOLERANCE * Math.max(walkBp, callBp)\n}\n\n// Pair two ascending lists of lengths, leaving out the surplus of whichever is\n// longer. `out[i]` is the index in `calls` that `walks[i]` took.\nfunction nearest(walks: number[], calls: number[]) {\n const W = walks.length\n const C = calls.length\n const cost = Array.from({ length: W + 1 }, () => new Array(C + 1).fill(0))\n const paired = Array.from({ length: W + 1 }, () =>\n new Array<boolean>(C + 1).fill(false),\n )\n for (let i = W - 1; i >= 0; i--) {\n for (let j = C - 1; j >= 0; j--) {\n const together = Math.abs(walks[i]! - calls[j]!) + cost[i + 1]![j + 1]!\n const dropWalk = W - i > C - j ? cost[i + 1]![j]! : Infinity\n const dropCall = C - j > W - i ? cost[i]![j + 1]! : Infinity\n cost[i]![j] = Math.min(together, dropWalk, dropCall)\n paired[i]![j] = cost[i]![j] === together\n }\n }\n const out = new Array<number | undefined>(W).fill(undefined)\n for (let i = 0, j = 0; i < W && j < C;) {\n if (paired[i]![j]) {\n out[i++] = j++\n } else if (W - i > C - j) {\n i++\n } else {\n j++\n }\n }\n return out\n}\n\nfunction byLength<T>(items: T[], bp: (item: T) => number) {\n return items\n .map((item, i) => ({ item, i }))\n .sort((a, b) => bp(a.item) - bp(b.item))\n}\n\nconst backed = (call: RepeatCall) => call.spanningReads !== 0\n\nfunction scored(row: { bp: number; complete: boolean }, call: RepeatCall) {\n return {\n ...call,\n agrees: row.complete && backed(call) ? agrees(row.bp, call.bp) : undefined,\n }\n}\n\n// A phased genotype names its walk's haplotype outright. The rest go by\n// length: the walks that span the array against the alleles reads spanned,\n// then everything else against what those two left. That decides the\n// ambiguous cases on merit: which walk carries the one allele a lone spanning\n// read supports, and which allele a walk that stops inside the array is drawn\n// against.\nfunction pairSample<\n R extends { bp: number; complete: boolean; haplotype?: number },\n>(rows: R[], calls: RepeatCall[]) {\n const out = new Map<number, WalkCall>()\n const taken = new Set<RepeatCall>()\n rows.forEach((row, i) => {\n const call = calls.find(\n c => c.haplotype !== undefined && c.haplotype === row.haplotype,\n )\n if (call) {\n taken.add(call)\n out.set(i, scored(row, call))\n }\n })\n const unphased = calls.filter(call => call.haplotype === undefined)\n const ranked = byLength(rows, r => r.bp)\n const stages = [\n [ranked.filter(r => r.item.complete), unphased.filter(backed)],\n [ranked, unphased],\n ] as const\n for (const [pool, group] of stages) {\n const free = pool.filter(r => !out.has(r.i))\n const open = byLength(\n group.filter(call => !taken.has(call)),\n call => call.bp,\n )\n nearest(\n free.map(r => r.item.bp),\n open.map(c => c.item.bp),\n ).forEach((pick, k) => {\n if (pick !== undefined) {\n const row = free[k]!\n const call = open[pick]!.item\n taken.add(call)\n out.set(row.i, scored(row.item, call))\n }\n })\n }\n return out\n}\n\nexport function withCalls<\n R extends {\n sample: string\n bp: number\n complete: boolean\n haplotype?: number\n },\n>(rows: R[], calls: Record<string, RepeatCall[]> | undefined) {\n if (!calls) {\n return rows as (R & { call?: WalkCall })[]\n }\n const paired = new Map<R, WalkCall>()\n const bySample = new Map<string, R[]>()\n for (const row of rows) {\n bySample.set(row.sample, [...(bySample.get(row.sample) ?? []), row])\n }\n for (const [sample, group] of bySample) {\n const called = calls[sample]\n if (called) {\n for (const [i, call] of pairSample(group, called)) {\n paired.set(group[i]!, call)\n }\n }\n }\n return rows.map(row => ({ ...row, call: paired.get(row) }))\n}\n", "import { bubbleSegmentIds, classifyBubble, formatBp } from './classifyBubble'\nimport { isBackbone } from '../anchoredNodes'\nimport { svgPath } from '../util/geometry'\n\nimport type { BubbleRoute, MinigraphBubble } from './bubbleLine'\nimport type { BubbleKind, RepeatSpan } from './classifyBubble'\nimport type { Graph, NodeSegment } from '../types'\n\n// A bubble drawn over the graph itself: a wide translucent stroke along the\n// nodes inside it, in layout coordinates, so the same halo follows the nodes\n// through the force layout's loops as through the ordered layout's lenses. The\n// two backbone nodes a bubble hangs between are not inside it, and are left\n// out so neighbouring halos do not touch.\nexport interface BubbleHalo {\n bubble: MinigraphBubble\n kind: BubbleKind\n label: string\n // an SVG path in layout units; a drawing transform puts it on screen\n path: string\n // where the name goes, in layout units: over the middle of its nodes, at the\n // height of the highest\n labelAt: NodeSegment\n members: number\n // the ids of those nodes, so a lifted walk can say which bubbles it enters\n nodeIds: string[]\n // the bubble is the whole drawing, as a popped bubble's own row is: its\n // label still names it, but a halo around everything marks nothing\n whole: boolean\n // each route the walks take through the bubble, the reference's own\n // included, named for the haplotypes that take it; a route with no steps, a\n // deletion, has nowhere to carry a chip\n routes: RouteLabel[]\n}\n\nexport interface RouteLabel {\n route: BubbleRoute\n at: NodeSegment\n text: string\n}\n\nconst WHOLE_FRACTION = 0.9\nconst NAMED_WALKS = 2\n// a SNP's routes are a dot each; chips are for loops a reader can see\nconst MIN_CHIPPED_BP = 1000\n\nexport function bubbleHalos(\n graph: Graph,\n bubbles: MinigraphBubble[],\n positions: Record<string, NodeSegment[]>,\n walkLabel: (name: string) => string = name => name,\n repeats: readonly RepeatSpan[] = [],\n): BubbleHalo[] {\n const byName = new Map(graph.nodes.map(n => [n.name, n]))\n const halos: BubbleHalo[] = []\n for (const bubble of bubbles) {\n const parts: string[] = []\n let minX = Infinity\n let maxX = -Infinity\n let minY = Infinity\n const nodeIds: string[] = []\n const ends: NodeSegment[] = []\n for (const name of bubbleSegmentIds(bubble)) {\n const node = byName.get(name)\n const line = node && positions[node.id]\n if (!node || !line?.length) {\n continue\n }\n if (\n isBackbone(node) &&\n (node.stable.start < bubble.start || node.stable.start >= bubble.end)\n ) {\n ends.push(line[Math.floor(line.length / 2)]!)\n continue\n }\n nodeIds.push(node.id)\n // a one-point line still needs a segment to stroke as a dot\n parts.push(svgPath(line.length === 1 ? [line[0]!, line[0]!] : line))\n for (const p of line) {\n minX = Math.min(minX, p.x)\n maxX = Math.max(maxX, p.x)\n minY = Math.min(minY, p.y)\n }\n }\n if (nodeIds.length === 0) {\n continue\n }\n const labelAt = { x: (minX + maxX) / 2, y: minY }\n const anchor = ends.length\n ? {\n x: ends.reduce((s, p) => s + p.x, 0) / ends.length,\n y: ends.reduce((s, p) => s + p.y, 0) / ends.length,\n }\n : labelAt\n // A chip goes on the stretch that is the route's own: among the nodes the\n // fewest other routes share, the point farthest from the bubble's ends.\n // Routes through a repeat array share most of their copies, and the far\n // point of a shared copy would put every chip on one loop.\n const sharing = new Map<string, number>()\n for (const route of bubble.routes ?? []) {\n for (const id of new Set(route.steps)) {\n sharing.set(id, (sharing.get(id) ?? 0) + 1)\n }\n }\n const routes: RouteLabel[] = []\n for (const route of bubble.routes ?? []) {\n if (bubble.longestAlleleLength < MIN_CHIPPED_BP) {\n break\n }\n if (route.steps.length === 0) {\n continue\n }\n const rarest = Math.min(...route.steps.map(id => sharing.get(id)!))\n let at: NodeSegment | undefined\n let far = -1\n for (const id of route.steps) {\n if (sharing.get(id) !== rarest) {\n continue\n }\n for (const p of positions[id] ?? []) {\n const d = Math.hypot(p.x - anchor.x, p.y - anchor.y)\n if (d > far) {\n far = d\n at = p\n }\n }\n }\n if (at) {\n routes.push({ route, at, text: routeText(route, walkLabel) })\n }\n }\n halos.push({\n bubble,\n ...classifyBubble(bubble, repeats),\n path: parts.join(''),\n labelAt,\n members: nodeIds.length,\n nodeIds,\n whole: nodeIds.length >= WHOLE_FRACTION * graph.nodes.length,\n routes,\n })\n }\n return halos\n}\n\nfunction routeText(route: BubbleRoute, walkLabel: (name: string) => string) {\n const names = [...new Set(route.walks.map(walkLabel))].sort()\n const shown = names.slice(0, NAMED_WALKS).join(', ')\n const more =\n names.length > NAMED_WALKS ? ` +${names.length - NAMED_WALKS}` : ''\n return `${shown}${more} \u00B7 ${formatBp(route.bp)}`\n}\n", "import { SATURATED_PATH_COUNT } from './bubbleLine'\nimport { isBackbone } from '../anchoredNodes'\nimport { referenceOrder } from '../layout/orderedLayout'\n\nimport type { BubbleRoute, MinigraphBubble } from './bubbleLine'\nimport type { Graph, GraphNode } from '../types'\n\n// Bubbles from the graph alone, off the reference order the ordered layout is\n// also drawn from (referenceOrder): a backbone node no link reaches across is a\n// bubble boundary, and whatever sorts between two consecutive boundaries is a\n// bubble. Needs a reference, not an index, so it works on a GBZ cut, a pggb\n// file and the inside of a popped superbubble alike.\n//\n// Read off the order itself, not off the layers the drawing puts it in. A\n// layer is where a node is drawn, and one nothing leads into is drawn in the\n// first layer wherever it sorts. That is every node a walk enters the cut at,\n// the ordinary shape of a GBZ cut whose context stops short of a haplotype's\n// walk: its link into the backbone then crossed every layer between, and the\n// bubbles on either side of those boundaries fused into one.\n//\n// Route statistics come from the walks when the graph has paths (exact, and\n// immune to a repeat array whose copies the order cannot direct), and from a\n// DP over the ordered DAG otherwise. A reversed stretch comes out as one\n// bubble whose interior runs against the reference, not as an inversion flag.\n\ninterface Routes {\n min: number\n max: number\n n: number\n}\n\nexport function bubblesFromGraph(graph: Graph): MinigraphBubble[] {\n if (!graph.nodes.some(isBackbone)) {\n return []\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const order = referenceOrder(graph)\n const indexOf = new Map(order.map((id, i) => [id, i]))\n // how many links reach across each place in the order, as a running sum of\n // where each one starts and stops\n const reach = new Int32Array(order.length + 1)\n // successors as a set: a repeat's back edge is the same DAG edge as its\n // forward one, and counting it twice doubles every route through the copy\n const succ = new Map<string, Set<string>>()\n const linked = new Set<string>()\n for (const e of graph.edges) {\n const a = indexOf.get(e.from)\n const b = indexOf.get(e.to)\n if (a === undefined || b === undefined || a === b) {\n continue\n }\n linked.add(`${e.from}>${e.to}`).add(`${e.to}>${e.from}`)\n const [lo, hi] = a < b ? [a, b] : [b, a]\n reach[lo + 1]!++\n reach[hi]!--\n const [from, to] = a < b ? [e.from, e.to] : [e.to, e.from]\n ;(succ.get(from) ?? succ.set(from, new Set()).get(from)!).add(to)\n }\n for (let i = 1; i < reach.length; i++) {\n reach[i]! += reach[i - 1]!\n }\n // Boundaries are nodes: every backbone node no link reaches across, plus the\n // window's first and last backbone node whatever reaches across them, since a\n // repeat array whose copies reach the last backbone node would otherwise\n // never close.\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort((a, b) => a.stable.start - b.stable.start)\n const ends = new Set([backbone[0]!.id, backbone.at(-1)!.id])\n const boundaries = backbone\n .map(node => [node, indexOf.get(node.id)!] as const)\n .filter(([node, i]) => ends.has(node.id) || reach[i] === 0)\n .sort(([a, ia], [b, ib]) => ia - ib || a.stable.start - b.stable.start)\n const walkIndex = graph.paths?.map(p => {\n const at = new Map<string, number>()\n p.nodeIds.forEach((id, i) => {\n if (!at.has(id)) {\n at.set(id, i)\n }\n })\n return at\n })\n\n const bubbles: MinigraphBubble[] = []\n for (let i = 0; i + 1 < boundaries.length; i++) {\n const [start, i0] = boundaries[i]!\n const [end, i1] = boundaries[i + 1]!\n const interior = order.slice(i0 + 1, i1)\n if (interior.length === 0) {\n continue\n }\n\n const best = new Map<string, Routes>([[start.id, { min: 0, max: 0, n: 1 }]])\n for (const id of [start.id, ...interior]) {\n const cur = best.get(id)\n if (!cur) {\n continue\n }\n for (const t of succ.get(id) ?? []) {\n const ti = indexOf.get(t)!\n if (ti > i1) {\n continue\n }\n const add = t === end.id ? 0 : byId.get(t)!.length\n const prev = best.get(t)\n best.set(t, {\n min: Math.min(prev?.min ?? Infinity, cur.min + add),\n max: Math.max(prev?.max ?? -Infinity, cur.max + add),\n n: Math.min(SATURATED_PATH_COUNT, cur.n + (prev?.n ?? 0)),\n })\n }\n }\n const walked =\n walkIndex && walkRoutes(graph, byId, walkIndex, start.id, end.id)\n const crossed = walked?.n ? walked : undefined\n const routes = crossed ?? best.get(end.id) ?? { min: 0, max: 0, n: 0 }\n // A walk that enters the bubble and never reaches its other end left the\n // cut: a GBZ cut of a repeat array at 1 kb of context splits each\n // haplotype's walk into pieces, and the routes seen are then a floor.\n const walksLeave = walked !== undefined && walked.left > 0\n\n const refStart = start.stable.start + start.length\n const refEnd = end.stable.start\n // The reference route exists in the cut only if every backbone pair inside\n // is linked; a flank the hop reached from an allele is not.\n const chain = [\n start.id,\n ...interior.filter(id => isBackbone(byId.get(id)!)),\n end.id,\n ]\n const chainBroken = chain.some(\n (id, k) => k > 0 && !linked.has(`${chain[k - 1]}>${id}`),\n )\n const partial = chainBroken || walksLeave\n const fallback = chainBroken && !crossed\n bubbles.push({\n refName: start.stable.refName,\n start: refStart,\n end: refEnd,\n segmentCount: interior.length + 2,\n pathCount: routes.n,\n // a real inversion test needs each walk's direction relative to the\n // reference; the first visit's strand is just whichever path anchored the\n // node\n inversion: false,\n shortestAlleleLength: fallback ? refEnd - refStart : routes.min,\n longestAlleleLength: fallback ? refEnd - refStart : routes.max,\n segments: [start, ...interior.map(id => byId.get(id)!), end]\n .map(n => n.name)\n .join(','),\n shortestAllele: undefined,\n longestAllele: undefined,\n partial,\n routes: crossed?.routes,\n })\n }\n return bubbles\n}\n\n// For every walk that passes both boundary nodes, the bp between them and the\n// step sequence, so routes are distinct sequences and lengths are the true\n// haplotype lengths. `left` counts the walks that pass one boundary and end\n// before the other.\n//\n// `byId` is the caller's: this runs once per bubble, and a map of every node\n// built here made the whole pass quadratic, seven seconds at 15k nodes.\nfunction walkRoutes(\n graph: Graph,\n byId: Map<string, GraphNode>,\n walkIndex: Map<string, number>[],\n startId: string,\n endId: string,\n): (Routes & { left: number; routes: BubbleRoute[] }) | undefined {\n const seen = new Map<string, BubbleRoute>()\n let min = Infinity\n let max = -Infinity\n let left = 0\n graph.paths!.forEach((p, k) => {\n const i0 = walkIndex[k]!.get(startId)\n const i1 = walkIndex[k]!.get(endId)\n if (i0 === undefined || i1 === undefined) {\n if (i0 !== undefined || i1 !== undefined) {\n left++\n }\n return\n }\n // Start to end, whichever way the walk crosses: a contig on the reverse\n // strand takes the same route, and read end-first it keyed as a second one.\n const steps = p.nodeIds.slice(Math.min(i0, i1) + 1, Math.max(i0, i1))\n if (i1 < i0) {\n steps.reverse()\n }\n let bp = 0\n for (const id of steps) {\n bp += byId.get(id)?.length ?? 0\n }\n const key = steps.join(',')\n const route = seen.get(key) ?? { steps, bp, walks: [] }\n route.walks.push(p.name)\n seen.set(key, route)\n min = Math.min(min, bp)\n max = Math.max(max, bp)\n })\n return seen.size || left\n ? { min, max, n: seen.size, left, routes: [...seen.values()] }\n : undefined\n}\n", "import type { Graph } from '../types'\n\n// The graph inside one bubble: the segments the bubble row names, with the\n// links among them. The row's list includes the two backbone segments the\n// bubble hangs between, so the cut keeps its anchors and every layout that\n// needs a backbone still has one. A walk keeps the steps it takes inside the\n// bubble, so the bubbles derived inside it still read their routes and\n// lengths off the haplotypes, and carriage still sets node width.\nexport function bubbleSubgraph(graph: Graph, segmentIds: string[]): Graph {\n const keep = new Set(segmentIds)\n const nodes = graph.nodes.filter(n => keep.has(n.name))\n const ids = new Set(nodes.map(n => n.id))\n const paths = graph.paths\n ?.map(p => ({ ...p, nodeIds: p.nodeIds.filter(id => ids.has(id)) }))\n .filter(p => p.nodeIds.length > 0)\n return {\n name: graph.name,\n nodes,\n edges: graph.edges.filter(e => ids.has(e.from) && ids.has(e.to)),\n ...(paths?.length ? { paths } : {}),\n anchorPaths: graph.anchorPaths,\n pathVisits: graph.pathVisits,\n anchoredBy: graph.anchoredBy,\n referencePath: graph.referencePath,\n }\n}\n", "import { firstNodeAtOrAfter, isBackbone } from '../anchoredNodes'\nimport { polylineSlice } from '../layout/mergeRuns'\nimport { svgPath } from '../util/geometry'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph, NodeSegment } from '../types'\n\n// A gene as the pins need it: its name, its span on the reference, and the\n// exons of all its transcripts merged into one set of intervals.\nexport interface GeneModel {\n name: string\n refName: string\n start: number\n end: number\n strand: number\n exons: { start: number; end: number }[]\n}\n\n// A gene drawn onto the graph: its exons as stretches of the backbone nodes\n// that carry them, in layout units, and one point on the backbone to pin its\n// name to. Only the backbone has coordinates, so an allele shows no exon even\n// where a haplotype's own annotation would put one.\nexport interface GenePin {\n gene: GeneModel\n // an SVG path of the exon stretches, in layout units\n exons: string\n // where the name goes: the backbone point at the gene's midpoint, or the\n // nearest backbone point inside the gene where the midpoint is not in the cut\n at: NodeSegment\n // how much of the gene's span the cut's backbone covers, so a label can say\n // when a gene runs off the cut\n covered: number\n}\n\n// The backbone of one refName in offset order, for finding the nodes a gene\n// lies over without reading the rest. `reach` is the longest node: a node over\n// a gene's start begins no further before it than that.\ninterface RefNameBackbone {\n nodes: AnchoredNode[]\n reach: number\n}\n\n// A gene lands on the backbone refName it equals. An assembly's `chr6` lands\n// on none, so a host renames its genes onto the graph's `GRCh38#0#chr6` with\n// featuresOnBackbone once it knows the backbone lies on that assembly.\nexport function genePins(\n graph: Graph,\n genes: GeneModel[],\n positions: Record<string, NodeSegment[]>,\n): GenePin[] {\n // Per refName and sorted once, so a gene reads the few nodes it lies over.\n // Every gene used to read every backbone node on each frame of a node drag:\n // 199 ms for 100 genes over 15k nodes, now 12.\n const byRefName = new Map<string, RefNameBackbone>()\n for (const node of graph.nodes) {\n if (isBackbone(node)) {\n const { refName } = node.stable\n if (positions[node.id]?.length) {\n const entry =\n byRefName.get(refName) ??\n byRefName.set(refName, { nodes: [], reach: 0 }).get(refName)!\n entry.nodes.push(node)\n entry.reach = Math.max(entry.reach, node.length)\n }\n }\n }\n for (const { nodes } of byRefName.values()) {\n nodes.sort((a, b) => a.stable.start - b.stable.start)\n }\n const pins: GenePin[] = []\n for (const gene of genes) {\n const backbone = byRefName.get(gene.refName)\n if (!backbone) {\n continue\n }\n const parts: string[] = []\n let at: NodeSegment | undefined\n let atDistance = Infinity\n let covered = 0\n const mid = (gene.start + gene.end) / 2\n const { nodes, reach } = backbone\n for (\n let i = firstNodeAtOrAfter(nodes, gene.start - reach);\n i < nodes.length && nodes[i]!.stable.start < gene.end;\n i++\n ) {\n const node = nodes[i]!\n const nodeStart = node.stable.start\n const nodeEnd = nodeStart + node.length\n if (nodeEnd <= gene.start) {\n continue\n }\n const line = positions[node.id]!\n covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start)\n for (const exon of gene.exons) {\n const a = Math.max(exon.start, nodeStart)\n const b = Math.min(exon.end, nodeEnd)\n if (b <= a) {\n continue\n }\n const stretch = polylineSlice(\n line,\n (a - nodeStart) / node.length,\n (b - nodeStart) / node.length,\n )\n if (stretch.length === 1) {\n stretch.push({ ...stretch[0]! })\n }\n parts.push(svgPath(stretch))\n }\n const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd)\n const distance = Math.abs(pinBp - mid)\n if (distance < atDistance) {\n atDistance = distance\n const [p] = polylineSlice(\n line,\n (pinBp - nodeStart) / node.length,\n (pinBp - nodeStart) / node.length,\n )\n at = p\n }\n }\n if (at) {\n pins.push({\n gene,\n exons: parts.join(''),\n at,\n covered: covered / (gene.end - gene.start),\n })\n }\n }\n return pins\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { pathOrigin } from '../pathAnchoring'\n\nimport type { Graph, GraphPath, PathOrigin, PathVisit } from '../types'\n\n// A cut reaches past its window by the track's context, which is what lets\n// walk rows see where a walk ends. A tube map is a picture of the window, so\n// it draws each walk only from its first reference node overlapping the\n// window to its last, and the nodes and links those stretches use.\n//\n// A graph with no reference node in the window, or none at all, is returned\n// as it is: there is nothing to trim it to.\nexport function trimToWindow(\n graph: Graph,\n window: { start: number; end: number },\n): Graph {\n const inWindow = new Set(\n graph.nodes\n .filter(\n n =>\n isBackbone(n) &&\n n.stable.start < window.end &&\n n.stable.start + n.length > window.start,\n )\n .map(n => n.id),\n )\n const paths = graph.paths ?? []\n if (inWindow.size === 0 || paths.length === 0) {\n return graph\n }\n const lengthOf = new Map(graph.nodes.map(n => [n.id, n.length]))\n\n // each walk's kept stretch, as indexes into its steps; none when it never\n // meets the window's reference\n const spans = paths.map(path => {\n let first = -1\n let last = -1\n path.nodeIds.forEach((id, i) => {\n if (inWindow.has(id)) {\n if (first < 0) {\n first = i\n }\n last = i\n }\n })\n return first < 0 ? undefined : { first, last }\n })\n if (\n spans.every(\n (span, i) =>\n span?.first === 0 && span.last === paths[i]!.nodeIds.length - 1,\n )\n ) {\n return graph\n }\n\n const kept = new Set<string>()\n const trimmed: GraphPath[] = []\n const droppedBp = new Map<string, number>()\n paths.forEach((path, i) => {\n const span = spans[i]\n if (span) {\n const nodeIds = path.nodeIds.slice(span.first, span.last + 1)\n for (const id of nodeIds) {\n kept.add(id)\n }\n trimmed.push({ ...path, nodeIds })\n droppedBp.set(\n path.name,\n path.nodeIds\n .slice(0, span.first)\n .reduce((sum, id) => sum + (lengthOf.get(id) ?? 0), 0),\n )\n }\n })\n\n return {\n ...graph,\n nodes: graph.nodes.filter(n => kept.has(n.id)),\n edges: graph.edges.filter(e => kept.has(e.from) && kept.has(e.to)),\n paths: trimmed,\n pathVisits:\n graph.pathVisits &&\n trimVisits(\n graph.pathVisits,\n paths,\n spans,\n new Map(graph.nodes.map(n => [n.id, n.name])),\n ),\n anchorPaths:\n graph.anchorPaths && shiftOrigins(graph.anchorPaths, paths, droppedBp),\n }\n}\n\n// Visits are keyed by segment name and listed in walk order, and a walk in\n// several pieces lists its pieces in the order the paths are, so replaying\n// every path's steps consumes the visits one for one; a visit is kept when its\n// step lies in the kept stretch.\nfunction trimVisits(\n visits: Map<string, PathVisit[]>,\n paths: GraphPath[],\n spans: ({ first: number; last: number } | undefined)[],\n nameOf: Map<string, string>,\n) {\n const cursor = new Map<string, number>()\n const out = new Map<string, PathVisit[]>()\n paths.forEach((path, p) => {\n const origin = pathOrigin(path.name).name\n const span = spans[p]\n path.nodeIds.forEach((id, i) => {\n const segment = nameOf.get(id) ?? id\n const list = visits.get(segment)\n if (!list) {\n return\n }\n const key = `${origin}\\t${segment}`\n let at = cursor.get(key) ?? 0\n while (at < list.length && list[at]!.path !== origin) {\n at++\n }\n const visit = list[at]\n cursor.set(key, at + 1)\n if (visit && span && i >= span.first && i <= span.last) {\n const keptVisits = out.get(segment)\n if (keptVisits) {\n keptVisits.push(visit)\n } else {\n out.set(segment, [visit])\n }\n }\n })\n })\n return out\n}\n\n// A walk whose front was trimmed starts that much further along its sequence\nfunction shiftOrigins(\n origins: PathOrigin[],\n paths: GraphPath[],\n droppedBp: Map<string, number>,\n) {\n const pieces = new Map<string, number>()\n for (const path of paths) {\n const origin = pathOrigin(path.name).name\n pieces.set(origin, (pieces.get(origin) ?? 0) + 1)\n }\n return origins.map(o => {\n const path = paths.find(p => pathOrigin(p.name).name === o.name)\n const dropped = path ? droppedBp.get(path.name) : undefined\n return dropped && pieces.get(o.name) === 1\n ? { ...o, start: o.start + dropped }\n : o\n })\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { visitStrands } from '../layout/tubeMapLayout'\nimport { pathOrigin } from '../pathAnchoring'\nimport { canonicalStrand } from './reads'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type {\n Graph,\n GraphEdge,\n GraphNode,\n GraphPath,\n PathVisit,\n} from '../types'\n\n// A tube map of a human window is mostly SNP bubbles: MICB's 22 kb cut draws\n// 475 columns for 238 bubbles, one of them 50 bp or more. This folds every\n// variant under `sigma` into the reference, as svSTM does (van den Brandt et\n// al., EuroVis 2025), so the reference between two structural variants is one\n// node and each walk keeps what it carries there as deviations at their bp.\n//\n// Cut from each walk's own runs over the reference, not from the bubbles:\n// derived bubbles are chain level, so a kept superbubble keeps every SNP inside\n// it raw, and a one-node inversion never forms one.\n\ntype Strand = '+' | '-'\n\n// A variant a walk carries inside a merged node: it leaves the reference at\n// `start`, rejoins it at `end`, and carries `bp` of its own in between\nexport interface Deviation {\n start: number\n end: number\n bp: number\n inverted?: true\n}\n\nexport type CutCause =\n | 'variant'\n | 'backward'\n | 'inversion'\n | 'walk-end'\n | 'overlap'\n | 'reference-gap'\n\nexport interface Coarsened {\n graph: Graph\n // coarse node id to the original node ids it stands for\n members: Map<string, string[]>\n // original node id to the coarse node that draws it\n coarseOf: Map<string, string>\n // by path name, the variants folded into the merged nodes it visits\n deviations: Map<string, Deviation[]>\n cuts: Map<number, CutCause>\n}\n\ninterface Step {\n node: GraphNode\n strand: Strand\n}\n\n// A stretch of reference a walk reads contiguously, in reference order\n// whichever way it reads it\ninterface Run {\n start: number\n end: number\n inverted: boolean\n}\n\ninterface Gap {\n a: number\n b: number\n detour: Step[]\n bp: number\n}\n\n// A walk as runs over the reference and the detours between them. A detour\n// before the first run or after the last has no reference on that side.\ninterface Walk {\n path: GraphPath\n runs: Run[]\n // gaps[i] lies between runs[i - 1] and runs[i]; gaps[runs.length] trails\n gaps: Gap[]\n}\n\nfunction readWalk(\n path: GraphPath,\n nodeById: Map<string, GraphNode>,\n strands: Map<string, Strand[]>,\n): Walk {\n const visitName = pathOrigin(path.name).name\n const seen = new Map<string, number>()\n const runs: Run[] = []\n const open = (): Gap => ({ a: -Infinity, b: Infinity, detour: [], bp: 0 })\n const gaps: Gap[] = [open()]\n for (const id of path.nodeIds) {\n const node = nodeById.get(id)\n if (!node) {\n continue\n }\n const k = seen.get(node.name) ?? 0\n seen.set(node.name, k + 1)\n const strand =\n strands.get(`${visitName}\\t${node.name}`)?.[k] ?? canonicalStrand(node)\n const gap = gaps.at(-1)!\n const last = runs.at(-1)\n if (isBackbone(node)) {\n const start = node.stable.start\n const end = start + node.length\n const inverted = strand !== (node.stable.strand ?? '+')\n const joins = last !== undefined && gap.detour.length === 0\n if (joins && !last.inverted && !inverted && last.end === start) {\n last.end = end\n } else if (joins && last.inverted && inverted && last.start === end) {\n last.start = start\n } else {\n runs.push({ start, end, inverted })\n gaps.push(open())\n }\n } else {\n gap.detour.push({ node, strand })\n gap.bp += node.length\n }\n }\n runs.forEach((run, i) => {\n gaps[i]!.b = run.start\n gaps[i + 1]!.a = run.end\n })\n return { path, runs, gaps }\n}\n\nfunction isSmall(gap: Gap, sigma: number) {\n return (\n Number.isFinite(gap.a) &&\n Number.isFinite(gap.b) &&\n gap.b >= gap.a &&\n Math.max(gap.b - gap.a, gap.bp) < sigma\n )\n}\n\nfunction strictlyInside(cuts: readonly number[], a: number, b: number) {\n let lo = 0\n let hi = cuts.length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (cuts[mid]! <= a) {\n lo = mid + 1\n } else {\n hi = mid\n }\n }\n return lo < cuts.length && cuts[lo]! < b\n}\n\n// Both sides of a hole in the reference, which a walk that seems to step over\n// it has not stepped over any reference\nfunction referenceGaps(backbone: readonly AnchoredNode[]) {\n const cuts = new Map<number, CutCause>()\n for (let i = 1; i < backbone.length; i++) {\n const prev = backbone[i - 1]!\n const node = backbone[i]!\n const end = prev.stable.start + prev.length\n if (end !== node.stable.start) {\n cuts.set(end, 'reference-gap')\n cuts.set(node.stable.start, 'reference-gap')\n }\n }\n return cuts\n}\n\n// Where the reference is cut. A small variant that straddles another walk's cut\n// would skip a whole merged node, so it is cut too, until nothing changes.\nfunction findCuts(\n walks: readonly Walk[],\n sigma: number,\n cuts: Map<number, CutCause>,\n) {\n const cut = (bp: number, cause: CutCause) => {\n if (Number.isFinite(bp) && !cuts.has(bp)) {\n cuts.set(bp, cause)\n }\n }\n for (const { runs, gaps } of walks) {\n const first = runs[0]\n const last = runs.at(-1)\n if (first && last) {\n cut(first.start, 'walk-end')\n cut(last.end, 'walk-end')\n }\n for (const run of runs) {\n if (run.inverted && run.end - run.start >= sigma) {\n cut(run.start, 'inversion')\n cut(run.end, 'inversion')\n }\n }\n for (const gap of gaps) {\n if (gap.b < gap.a) {\n cut(gap.a, 'backward')\n cut(gap.b, 'backward')\n } else if (!isSmall(gap, sigma)) {\n cut(gap.a, 'variant')\n cut(gap.b, 'variant')\n }\n }\n }\n for (let changed = true; changed;) {\n changed = false\n const sorted = [...cuts.keys()].sort((x, y) => x - y)\n const spans = walks.flatMap(({ runs, gaps }) => [\n ...gaps.filter(g => isSmall(g, sigma)).map(g => [g.a, g.b] as const),\n ...runs\n .filter(r => r.inverted && r.end - r.start < sigma)\n .map(r => [r.start, r.end] as const),\n ])\n for (const [a, b] of spans) {\n if (strictlyInside(sorted, a, b)) {\n changed ||= !cuts.has(a) || !cuts.has(b)\n cut(a, 'overlap')\n cut(b, 'overlap')\n }\n }\n }\n return cuts\n}\n\ninterface Merged {\n node: GraphNode\n members: AnchoredNode[]\n}\n\nfunction mergeReference(\n backbone: readonly AnchoredNode[],\n cuts: Map<number, CutCause>,\n) {\n const merged: Merged[] = []\n let group: AnchoredNode[] = []\n const close = () => {\n const first = group[0]\n const last = group.at(-1)\n if (first && last) {\n const bp0 = first.stable.start\n const bp1 = last.stable.start + last.length\n const name = `${bp0}-${bp1}`\n merged.push({\n node: {\n id: `${name}+`,\n name,\n length: bp1 - bp0,\n depth: 0,\n stable: {\n refName: first.stable.refName,\n start: bp0,\n rank: 0,\n strand: '+',\n },\n },\n members: group,\n })\n }\n group = []\n }\n for (const node of backbone) {\n if (cuts.has(node.stable.start)) {\n close()\n }\n group.push(node)\n }\n close()\n return merged\n}\n\nexport function coarsenTubeMap(\n graph: Graph,\n sigma: number,\n): Coarsened | undefined {\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort((a, b) => a.stable.start - b.stable.start)\n const paths = graph.paths ?? []\n if (backbone.length === 0 || paths.length === 0) {\n return undefined\n }\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const strands = visitStrands(graph)\n const walks = paths.map(p => readWalk(p, nodeById, strands))\n const cuts = findCuts(walks, sigma, referenceGaps(backbone))\n const sortedCuts = [...cuts.keys()].sort((x, y) => x - y)\n const merged = mergeReference(backbone, cuts)\n const starts = merged.map(m => m.node.stable!.start)\n const mergedAt = (bp: number) => {\n let lo = 0\n let hi = starts.length - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (starts[mid]! <= bp) {\n lo = mid\n } else {\n hi = mid - 1\n }\n }\n return lo\n }\n\n const members = new Map<string, string[]>()\n const coarseOf = new Map<string, string>()\n for (const m of merged) {\n members.set(\n m.node.id,\n m.members.map(n => n.id),\n )\n for (const n of m.members) {\n coarseOf.set(n.id, m.node.id)\n }\n }\n\n // One node per distinct detour a structural gap takes, shared by the walks\n // that take the same one\n const routes = new Map<string, GraphNode>()\n const routeFor = (gap: Gap) => {\n const key = `${gap.a}\\t${gap.b}\\t${gap.detour.map(s => s.node.id + s.strand).join(',')}`\n let node = routes.get(key)\n if (!node) {\n const name = `r${routes.size}`\n node = {\n id: `${name}+`,\n name,\n length: gap.bp,\n depth: 0,\n stable: {\n refName: backbone[0]!.stable.refName,\n start: Number.isFinite(gap.a) ? gap.a : gap.b,\n rank: 1,\n strand: '+',\n },\n }\n routes.set(key, node)\n members.set(\n node.id,\n gap.detour.map(s => s.node.id),\n )\n for (const s of gap.detour) {\n if (!coarseOf.has(s.node.id)) {\n coarseOf.set(s.node.id, node.id)\n }\n }\n }\n return node\n }\n\n const deviations = new Map<string, Deviation[]>()\n const coarsePaths: GraphPath[] = []\n const pathVisits = new Map<string, PathVisit[]>()\n const edges = new Map<string, GraphEdge>()\n const depth = new Map<string, number>()\n\n for (const { path, runs, gaps } of walks) {\n const steps: { node: GraphNode; strand: Strand }[] = []\n const devs: Deviation[] = []\n // a structural gap ends the stretch a merged node may be continued over\n let continuing = false\n const append = (node: GraphNode, strand: Strand) => {\n const last = steps.at(-1)\n if (!(continuing && last?.node === node && last.strand === strand)) {\n steps.push({ node, strand })\n }\n continuing = true\n }\n const structural = (gap: Gap) => {\n continuing = false\n if (gap.detour.length > 0) {\n append(routeFor(gap), '+')\n continuing = false\n }\n }\n const fold = (gap: Gap) => {\n if (gap.bp > 0 || gap.b > gap.a) {\n devs.push({ start: gap.a, end: gap.b, bp: gap.bp })\n }\n const home = merged[mergedAt(gap.b > gap.a ? gap.a : gap.a - 1)]!\n for (const s of gap.detour) {\n if (!coarseOf.has(s.node.id)) {\n coarseOf.set(s.node.id, home.node.id)\n members.get(home.node.id)!.push(s.node.id)\n }\n }\n }\n runs.forEach((run, i) => {\n const gap = gaps[i]!\n if (isSmall(gap, sigma) && !strictlyInside(sortedCuts, gap.a, gap.b)) {\n fold(gap)\n } else {\n structural(gap)\n }\n const i0 = mergedAt(run.start)\n const i1 = mergedAt(run.end - 1)\n const small = run.end - run.start < sigma\n if (run.inverted && !small) {\n continuing = false\n for (let j = i1; j >= i0; j--) {\n append(merged[j]!.node, '-')\n }\n continuing = false\n } else {\n if (run.inverted) {\n devs.push({\n start: run.start,\n end: run.end,\n bp: run.end - run.start,\n inverted: true,\n })\n }\n for (let j = i0; j <= i1; j++) {\n append(merged[j]!.node, '+')\n }\n }\n })\n structural(gaps.at(-1)!)\n\n const visitName = pathOrigin(path.name).name\n let pos = 0\n steps.forEach(({ node, strand }, i) => {\n depth.set(node.id, (depth.get(node.id) ?? 0) + 1)\n const visits = pathVisits.get(node.name) ?? []\n visits.push({\n path: visitName,\n sample: path.sample ?? visitName,\n start: pos,\n strand,\n })\n pathVisits.set(node.name, visits)\n pos += node.length\n const next = steps[i + 1]\n if (next) {\n const key = `${node.id}${strand}\\t${next.node.id}${next.strand}`\n const edge = edges.get(key)\n if (edge) {\n edge.pathIds!.push(path.name)\n } else {\n edges.set(key, {\n from: node.id,\n to: next.node.id,\n fromStrand: strand,\n toStrand: next.strand,\n pathIds: [path.name],\n })\n }\n }\n })\n coarsePaths.push({ ...path, nodeIds: steps.map(s => s.node.id) })\n deviations.set(path.name, devs)\n }\n\n const nodes = [...merged.map(m => m.node), ...routes.values()]\n .filter(n => depth.has(n.id))\n .map(n => ({ ...n, depth: depth.get(n.id)! }))\n return {\n graph: {\n ...graph,\n nodes,\n edges: [...edges.values()],\n paths: coarsePaths,\n pathVisits,\n reads: undefined,\n },\n members,\n coarseOf,\n deviations,\n cuts,\n }\n}\n", "import type { TubeMapColumn } from '../layout/tubeMapLayout'\n\n// Tube x to screen x under the reference axis. A column covers the screen span\n// of the reference bp its backbone node covers, which puts the tubes under the\n// linear view's other tracks. But the tube map changes lanes BETWEEN columns,\n// and adjacent reference nodes abut in bp, so the curves would have no width\n// at all. At each boundary the gap the tube map drew (its curves, and any\n// columns of inserted sequence, which cover no reference) gets its tube px\n// back, up to MAX_GAP_PX, where there is room: first from reference no\n// column covers, then from up to half of each neighbouring column. Zoomed in that is a\n// few px off a wide node; zoomed out the columns are thin, the gap gets what\n// they can spare and the curves steepen.\n//\n// Piecewise linear, so a curve's bezier, which lies inside one gap, maps\n// exactly by mapping its control points.\n\nexport interface Knot {\n tx: number\n sx: number\n}\n\n// The most screen a boundary claims. The tube map spaces its columns to keep\n// the lane changes shallow, which with many haplotypes is hundreds of px a\n// gap; on the reference axis the curves steepen instead.\nconst MAX_GAP_PX = 24\n\nconst isReal = (c: TubeMapColumn) => c.bp1 > c.bp0\n\nexport function referenceKnots(\n columns: readonly TubeMapColumn[],\n bpToScreen: (bp: number) => number,\n): Knot[] {\n const real = columns.filter(isReal)\n if (real.length === 0) {\n const first = columns[0]\n return first ? [{ tx: first.x0, sx: bpToScreen(first.bp0) }] : []\n }\n const screen = real.map(c => ({\n c,\n s0: bpToScreen(c.bp0),\n s1: bpToScreen(c.bp1),\n trimLeft: 0,\n trimRight: 0,\n }))\n for (let i = 0; i + 1 < screen.length; i++) {\n const left = screen[i]!\n const right = screen[i + 1]!\n const demand = Math.min(right.c.x0 - left.c.x1, MAX_GAP_PX)\n const short = demand - (right.s0 - left.s1)\n if (short > 0) {\n const capLeft = (left.s1 - left.s0) / 2\n const capRight = (right.s1 - right.s0) / 2\n let fromLeft = Math.min(short / 2, capLeft)\n const fromRight = Math.min(short - fromLeft, capRight)\n fromLeft = Math.min(short - fromRight, capLeft)\n left.trimRight = fromLeft\n right.trimLeft = fromRight\n }\n }\n const knots: Knot[] = []\n for (const { c, s0, s1, trimLeft, trimRight } of screen) {\n knots.push(\n { tx: c.x0, sx: s0 + trimLeft },\n { tx: c.x1, sx: s1 - trimRight },\n )\n }\n return knots\n}\n\n// Beyond the first and last knot the drawing continues at one screen px per\n// tube px, which is where the track stubs past the end nodes go.\nexport function warpX(knots: readonly Knot[], tx: number) {\n const n = knots.length\n if (n === 0) {\n return tx\n }\n const first = knots[0]!\n const last = knots[n - 1]!\n if (tx <= first.tx) {\n return first.sx + (tx - first.tx)\n }\n if (tx >= last.tx) {\n return last.sx + (tx - last.tx)\n }\n let lo = 0\n let hi = n - 1\n while (hi - lo > 1) {\n const mid = (lo + hi) >> 1\n if (knots[mid]!.tx <= tx) {\n lo = mid\n } else {\n hi = mid\n }\n }\n const a = knots[lo]!\n const b = knots[hi]!\n const span = b.tx - a.tx\n return span > 0 ? a.sx + ((tx - a.tx) / span) * (b.sx - a.sx) : a.sx\n}\n", "import { referenceKnots, warpX } from './warp'\n\nimport type { TubeMapDrawing } from '../layout/tubeMapLayout'\n\nexport interface TubeMapView {\n scaleX: number\n translateX: number\n scaleY: number\n translateY: number\n // the pane's height less its fit padding\n usableHeight: number\n}\n\n// Tube coordinates to screen px\nexport interface TubeMapTransform {\n x: (tx: number) => number\n y: (ty: number) => number\n // screen px per tube px in y, which thins the node outlines of a squeezed\n // stack along with its tubes\n yScale: number\n}\n\nconst MIN_TUBE_Y_SCALE = 0.05\n\n// How far a node's box reaches past its tubes (tubemap-core's nodeOutlinePath)\nconst NODE_BOX_PAD = 9\n\n// On the reference axis y is px and a linear view has no vertical pan, so a\n// stack of tubes taller than the track squeezes to fit it; dragging the track\n// taller gives the tubes back their width.\nfunction squeeze(drawing: TubeMapDrawing, usableHeight: number) {\n const { minY, maxY } = drawing.layout.bounds\n const h = maxY - minY\n return drawing.columns && h > 0\n ? Math.max(MIN_TUBE_Y_SCALE, Math.min(1, usableHeight / h))\n : 1\n}\n\n// The tube map's own axis is the layout's x; the reference axis warps each\n// column onto the bp it covers (warp.ts).\nexport function tubeMapFrame(\n drawing: TubeMapDrawing,\n view: TubeMapView,\n): TubeMapTransform {\n const { scaleX, translateX, translateY } = view\n const yScale = view.scaleY * squeeze(drawing, view.usableHeight)\n const y = (ty: number) => (ty - drawing.yOffset) * yScale + translateY\n const { columns } = drawing\n if (columns) {\n const knots = referenceKnots(columns, bp => bp * scaleX + translateX)\n return { x: tx => warpX(knots, tx), y, yScale }\n }\n return { x: tx => tx * scaleX + translateX, y, yScale }\n}\n\n// The node whose box is under a screen point. The canvas's hit test measures\n// from a centreline and a stroke width, and a tube map box is as tall as the\n// tubes through it.\nexport function tubeMapNodeAt(\n drawing: TubeMapDrawing,\n { x, y }: TubeMapTransform,\n sx: number,\n sy: number,\n): string | null {\n let hit: string | null = null\n drawing.layout.nodes.forEach(node => {\n if (\n node.order >= 0 &&\n x(node.x - NODE_BOX_PAD) <= sx &&\n sx <= x(node.x + node.pixelWidth + NODE_BOX_PAD) &&\n y(node.y - NODE_BOX_PAD) <= sy &&\n sy <= y(node.y + node.contentHeight + NODE_BOX_PAD)\n ) {\n hit = node.name\n }\n })\n return hit\n}\n", "import { panSNContig } from './pansn'\nimport { pathOrigin } from './pathAnchoring'\nimport { encodedColor, resolveEncoding } from './walkEncoding'\n\nimport type { ReferenceRamp } from './renderer/GeometryBuilder'\nimport type { Graph } from './types'\nimport type { WalkEncoding, WalkLayer } from './walkEncoding'\n\n// One walk lifted out of the drawing: the nodes it visits and the links it\n// takes, how far along the walk each node is, and what it carries through the\n// window against the reference walk.\nexport interface WalkHighlight {\n name: string\n // whether this is the graph's reference walk, which keeps the node colours\n reference: boolean\n nodeIds: Set<string>\n edgeIndexes: Set<number>\n // each node's first visit, as the fraction of the walk's bp before its\n // midpoint: 0 at the walk's start, 1 at its end\n progress: Map<string, number>\n steps: number\n bp: number\n referenceBp?: number\n // where the walk's stretch of the cut sits on its own contig, from its W or\n // P record: its start where it begins and its end where it ends\n range?: { contig: string; start: number; end: number }\n}\n\nexport interface LiftedWalk extends WalkHighlight {\n encoding: WalkEncoding\n // the lane colour at each node the walk visits\n colors: Map<string, number>\n // the nodes it crosses the other way from the reference walk, and their\n // bp; empty for the reference\n reversed: Set<string>\n reversedBp: number\n}\n\n// The walks lifted together, reference first and the rest in the order they\n// were picked, each in its lane of the nodes it visits. A node or link on any\n// of them keeps its ink.\nexport interface WalkLift {\n walks: LiftedWalk[]\n // the reference interval a lane coloured by reference position spans\n referenceDomain?: { start: number; end: number }\n nodeIds: Set<string>\n edgeIndexes: Set<number>\n names: Set<string>\n}\n\nfunction referenceOf(graph: Graph) {\n // `referencePath` is the anchor name, which pathOrigin has stripped of the\n // range suffix odgi leaves on a P record's name\n return graph.referencePath\n ? graph.paths?.find(p => pathOrigin(p.name).name === graph.referencePath)\n : undefined\n}\n\nexport function walkHighlight(\n graph: Graph,\n name: string,\n): WalkHighlight | undefined {\n const path = graph.paths?.find(p => p.name === name)\n if (!path) {\n return undefined\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const edgeAt = new Map<string, number>()\n graph.edges.forEach((e, i) => {\n edgeAt.set(`${e.from}>${e.to}`, i)\n })\n const edgeIndexes = new Set<number>()\n for (let i = 1; i < path.nodeIds.length; i++) {\n const a = path.nodeIds[i - 1]!\n const b = path.nodeIds[i]!\n const index = edgeAt.get(`${a}>${b}`) ?? edgeAt.get(`${b}>${a}`)\n if (index !== undefined) {\n edgeIndexes.add(index)\n }\n }\n const lengthOf = (id: string) => byId.get(id)?.length ?? 0\n const bp = path.nodeIds.reduce((sum, id) => sum + lengthOf(id), 0)\n const progress = new Map<string, number>()\n let before = 0\n for (const id of path.nodeIds) {\n const length = lengthOf(id)\n if (!progress.has(id)) {\n progress.set(id, bp > 0 ? (before + length / 2) / bp : 0)\n }\n before += length\n }\n const reference = referenceOf(graph)\n const originName = pathOrigin(name).name\n const origin =\n graph.anchorPaths?.find(a => a.name === originName && a.length === bp) ??\n graph.anchorPaths?.find(a => a.name === originName)\n return {\n name,\n reference: reference === path,\n range: origin\n ? {\n contig: path.contig ?? panSNContig(origin.name),\n start: origin.start,\n end: origin.start + origin.length,\n }\n : undefined,\n nodeIds: new Set(path.nodeIds),\n edgeIndexes,\n progress,\n steps: path.nodeIds.length,\n bp,\n referenceBp:\n reference && reference !== path\n ? reference.nodeIds.reduce((sum, id) => sum + lengthOf(id), 0)\n : undefined,\n }\n}\n\n// Each node's value for the walk's field, through its scheme\nfunction laneColors(\n graph: Graph,\n walk: WalkHighlight,\n encoding: WalkEncoding,\n ramp: ReferenceRamp | undefined,\n) {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const colors = new Map<string, number>()\n for (const [id, progress] of walk.progress) {\n const node = byId.get(id)\n const mid = ramp?.midpoints.get(id)\n const t =\n encoding.field === 'progress'\n ? progress\n : ramp && mid !== undefined && !(node?.stable && node.stable.rank > 0)\n ? (mid - ramp.start) / ramp.span\n : undefined\n colors.set(id, encodedColor(encoding, t))\n }\n return colors\n}\n\n// The nodes a walk reads on the other strand from the reference walk\nfunction reversedNodes(graph: Graph, walk: WalkHighlight) {\n const reference = referenceOf(graph)\n const reversed = new Set<string>()\n let bp = 0\n if (!reference || walk.reference) {\n return { reversed, bp }\n }\n const walkPath = pathOrigin(walk.name).name\n const referencePath = pathOrigin(reference.name).name\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n for (const id of walk.progress.keys()) {\n const node = byId.get(id)\n const visits = node ? graph.pathVisits?.get(node.name) : undefined\n const own = visits?.find(v => v.path === walkPath)?.strand\n const theirs = visits?.find(v => v.path === referencePath)?.strand\n if (own && theirs && own !== theirs) {\n reversed.add(id)\n bp += node!.length\n }\n }\n return { reversed, bp }\n}\n\nexport function walkLift(\n graph: Graph,\n layers: readonly WalkLayer[],\n ramp?: ReferenceRamp,\n): WalkLift | undefined {\n const seen = new Set<string>()\n const lifted = layers\n .filter(layer => !seen.has(layer.walk) && seen.add(layer.walk))\n .flatMap(layer => {\n const walk = walkHighlight(graph, layer.walk)\n return walk ? [{ walk, layer }] : []\n })\n .sort((a, b) => Number(b.walk.reference) - Number(a.walk.reference))\n if (lifted.length === 0) {\n return undefined\n }\n let picked = 0\n const walks = lifted.map(({ walk, layer }) => {\n const encoding = resolveEncoding(\n layer,\n walk.reference,\n walk.reference ? 0 : picked++,\n lifted.length === 1,\n )\n const { reversed, bp } = reversedNodes(graph, walk)\n return {\n ...walk,\n encoding,\n colors: laneColors(graph, walk, encoding, ramp),\n reversed,\n reversedBp: bp,\n }\n })\n return {\n walks,\n referenceDomain: ramp\n ? { start: ramp.start, end: ramp.start + ramp.span }\n : undefined,\n nodeIds: new Set(walks.flatMap(w => [...w.nodeIds])),\n edgeIndexes: new Set(walks.flatMap(w => [...w.edgeIndexes])),\n names: new Set(walks.map(w => w.name)),\n }\n}\n", "import {\n backboneNodes,\n backboneSpan,\n} from '@jbrowse/bandage-core/anchoredNodes'\nimport { BUBBLE_SPREAD_VALUES } from '@jbrowse/bandage-core/bubbleSpreads'\nimport { bubbleHalos } from '@jbrowse/bandage-core/bubbles/bubbleHalos'\nimport { sameBubble } from '@jbrowse/bandage-core/bubbles/bubbleLine'\nimport { bubblesFromGraph } from '@jbrowse/bandage-core/bubbles/bubblesFromGraph'\nimport {\n BUBBLE_KIND_NAMES,\n bubbleSegmentIds,\n classifyBubble,\n} from '@jbrowse/bandage-core/bubbles/classifyBubble'\nimport { bubbleSubgraph } from '@jbrowse/bandage-core/bubbles/popBubble'\nimport {\n COLOR_SCHEMES,\n COLOR_SCHEME_VALUES,\n resolveColorScheme,\n} from '@jbrowse/bandage-core/colorSchemes'\nimport { deletionEdges } from '@jbrowse/bandage-core/deletionEdges'\nimport { genePins } from '@jbrowse/bandage-core/genes/genePins'\nimport { rowLabelBox } from '@jbrowse/bandage-core/graphLabels'\nimport {\n LEGEND_INSET_PX,\n layoutLabels,\n} from '@jbrowse/bandage-core/labelLayout'\nimport { ROW_HEIGHT_PX } from '@jbrowse/bandage-core/layout/rowSpacing'\nimport { trimToWindow } from '@jbrowse/bandage-core/layout/trimToWindow'\nimport { walkRowsExtent } from '@jbrowse/bandage-core/layout/walkRowLayout'\nimport { walkRows } from '@jbrowse/bandage-core/layout/walkRows'\nimport {\n LAYOUT_MODES,\n LAYOUT_MODE_VALUES,\n layoutModeByValue,\n modeUsesLayoutEngine,\n} from '@jbrowse/bandage-core/layoutModes'\nimport { NODE_WIDTH_VALUES, nodeInk } from '@jbrowse/bandage-core/nodeWidths'\nimport {\n anchorFromPaths,\n chooseReferencePath,\n} from '@jbrowse/bandage-core/pathAnchoring'\nimport { pathColorsLegible, pathLegend } from '@jbrowse/bandage-core/pathColors'\nimport {\n FIT_PADDING,\n clampZoom,\n drawingBounds,\n engineKey,\n fitTransform,\n fittedTranslateY,\n forceLayout,\n loadGraph,\n} from '@jbrowse/bandage-core/pipeline'\nimport {\n assemblyWalk,\n backboneAssembly,\n featuresOnBackbone,\n graphBackbone,\n} from '@jbrowse/bandage-core/reference'\nimport {\n buildNeighbors,\n nodeReferenceSpan,\n} from '@jbrowse/bandage-core/referenceSpan'\nimport {\n REFERENCE_STRIP_ZONE_PX,\n nodeAnchor,\n referenceStripBlocks,\n stripBlockAt,\n stripOverhang,\n} from '@jbrowse/bandage-core/referenceStrip'\nimport {\n buildGeometry,\n computeReferenceRamp,\n} from '@jbrowse/bandage-core/renderer/GeometryBuilder'\nimport { referenceBoxes, rulerBoxes } from '@jbrowse/bandage-core/tubeMap/axis'\nimport { coarsenTubeMap } from '@jbrowse/bandage-core/tubeMap/coarsen'\nimport {\n connectorAt,\n referenceNodes,\n tubeMapConnectors,\n} from '@jbrowse/bandage-core/tubeMap/connectors'\nimport { deviationMarks } from '@jbrowse/bandage-core/tubeMap/deviations'\nimport {\n mismatchOnScreen,\n mismatchesLegible,\n tubeMapPicture,\n} from '@jbrowse/bandage-core/tubeMap/draw'\nimport {\n tubeMapFrame,\n tubeMapNodeAt,\n} from '@jbrowse/bandage-core/tubeMap/frame'\nimport {\n GENE_ROW_PX,\n tubeMapGeneRows,\n tubeMapGenes,\n} from '@jbrowse/bandage-core/tubeMap/genes'\nimport {\n axisScaleOf,\n contains,\n padded,\n viewportOf,\n zoomAbout,\n} from '@jbrowse/bandage-core/viewport'\nimport { WALK_FIELDS, WALK_SCHEMES } from '@jbrowse/bandage-core/walkEncoding'\nimport { walkLift } from '@jbrowse/bandage-core/walkHighlight'\nimport { readConfObject } from '@jbrowse/core/configuration'\nimport { pushLaunchViewMenuItem } from '@jbrowse/core/ui'\nimport {\n getContainingTrack,\n getContainingView,\n getRpcSessionId,\n getSession,\n isSessionModelWithWidgets,\n statusMessageText,\n} from '@jbrowse/core/util'\nimport { openLocation } from '@jbrowse/core/util/io'\nimport { addDisposer, flow, isAlive, types } from '@jbrowse/mobx-state-tree'\nimport { RenderLifecycleMixin } from '@jbrowse/render-core/RenderLifecycleMixin'\nimport { getDpr } from '@jbrowse/render-core/canvas2dUtils'\nimport { autorun, reaction, untracked } from 'mobx'\n\nimport {\n GENE_ADAPTER_TYPES,\n geneModelsFrom,\n pickGeneTrack,\n} from './genes/geneFeatures'\nimport { hostFrame, isLinearHost } from './host'\nimport { lenientMaybeEnum, lenientOptionalEnum } from './lenientEnum'\nimport { namesReads } from '../GetGraphReads'\nimport {\n REPEAT_ADAPTER_TYPES,\n pickRepeatTrack,\n repeatArraysFrom,\n} from './repeats/repeatFeatures'\nimport { withCalls } from './repeats/walkCalls'\nimport {\n hoverInRegion,\n nodeForLgvHover,\n readLgvHover,\n} from '../hoverSync/lgvHover'\nimport {\n contributingAssemblies,\n locLabel,\n nodeOwnLocation,\n resolveContributors,\n resolveLocationAssembly,\n} from '../launchFromGraph/contributors'\nimport { graphLaunchMenuItems } from '../launchFromGraph/graphMenuItems'\nimport {\n highlightInLinearView,\n launchSyntenyView,\n paddedLocation,\n showInLinearView,\n withReferenceRegion,\n} from '../launchFromGraph/launchFromGraph'\nimport { launchTracks } from '../launchFromGraph/launchTracks'\nimport { linearViewTarget, withRows } from '../launchFromGraph/linearViewTarget'\nimport { launchableSyntenyTracks } from '../launchFromGraph/syntenyTracks'\n\nimport type { LinearHost } from './host'\nimport type { SubgraphCutOptions, SubgraphRegion } from '../GetSubgraph'\nimport type { RepeatArray } from './repeats/repeatFeatures'\nimport type { GafReads } from '../gaf/gafFile'\nimport type { GraphLocation } from '../launchFromGraph/contributors'\nimport type { BubbleSpread } from '@jbrowse/bandage-core/bubbleSpreads'\nimport type { MinigraphBubble } from '@jbrowse/bandage-core/bubbles/bubbleLine'\nimport type {\n ColorScheme,\n ResolvedColorScheme,\n} from '@jbrowse/bandage-core/colorSchemes'\nimport type { GeneModel } from '@jbrowse/bandage-core/genes/genePins'\nimport type { LayoutModeValue } from '@jbrowse/bandage-core/layoutModes'\nimport type { NodeWidth } from '@jbrowse/bandage-core/nodeWidths'\nimport type { Bounds, EngineRequest } from '@jbrowse/bandage-core/pipeline'\nimport type { AssemblyNames } from '@jbrowse/bandage-core/reference'\nimport type {\n RenderBatch,\n Renderer,\n} from '@jbrowse/bandage-core/renderer/types'\nimport type {\n Graph,\n GraphNode,\n LayoutResult,\n} from '@jbrowse/bandage-core/types'\nimport type { AxisScale } from '@jbrowse/bandage-core/util/geometry'\nimport type { NodeInk } from '@jbrowse/bandage-core/util/hitDetection'\nimport type {\n WalkEncoding,\n WalkLayer,\n} from '@jbrowse/bandage-core/walkEncoding'\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Feature } from '@jbrowse/core/util'\nimport type { FileLocation } from '@jbrowse/core/util/types'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n// Ceiling on the pane, and what it falls back to before there is a layout to\n// size against. A roughly square drawing (FMMM) hits this and keeps the\n// scrollable pane it has always had.\nconst MAX_CANVAS_HEIGHT = 600\n// Floor, so a window holding only backbone \u2014 one row, no height at all \u2014 still\n// leaves room to hover a node and read its tooltip.\nconst MIN_CANVAS_HEIGHT = 160\n// The facet grid: panels this far apart, each with a title row over a drawing\n// padded this much, at most this many across. A drawing this much wider than\n// it is tall stacks its panels in one column.\nexport const FACET_GAP_PX = 8\nexport const FACET_TITLE_PX = 34\nexport const FACET_PAD_PX = 12\nconst FACET_MAX_COLUMNS = 3\nconst FACET_WIDE = 2.5\nconst MIN_FACET_PANEL_PX = 60\n// The thinnest a fit draws a tube map's tubes\nconst MIN_FIT_TUBE_PX = 5\n\nconst TUBE_MAP_MODES = new Set<string>(['tubemap', 'tubemapref'])\n\n// The sizes a tube map folds variants under. MICB's 22 kb cut draws 475\n// columns whole, 34 under 3 bp and one under 50, where only its structural\n// variants would stand: human windows are mostly SNPs.\nconst TUBE_MAP_FOLDS = [\n { bp: 0, label: 'None' },\n { bp: 3, label: 'Under 3 bp' },\n { bp: 10, label: 'Under 10 bp' },\n { bp: 50, label: 'Under 50 bp, leaving structural variants' },\n { bp: 1000, label: 'Under 1 kb' },\n]\n\nconst SEGMENTS_SUFFIX = '.segs.bed.gz'\n\n// The rGFA index prefix an adapter config names, from either spelling the\n// config schema accepts: the `uri` shorthand or an explicit segments location.\nfunction bubblePrefix(adapterConfig: Record<string, unknown>) {\n if (typeof adapterConfig.uri === 'string') {\n return adapterConfig.uri\n }\n const segments = adapterConfig.segmentsLocation as\n { uri?: string } | undefined\n const uri = segments?.uri\n return typeof uri === 'string' && uri.endsWith(SEGMENTS_SUFFIX)\n ? uri.slice(0, -SEGMENTS_SUFFIX.length)\n : undefined\n}\nconst HOVER_BRIGHTEN = 1.4\nconst SELECT_BRIGHTEN = 1.6\nconst VIEWPORT_DEBOUNCE_MS = 150\nconst VIEWPORT_PANES_BUILT = 1\n\n// What the canvas draws under the tube map, whose ink is all TubeMapOverlay's\nconst EMPTY_BATCH: RenderBatch = {\n nodeStrokes: [],\n nodeStrokeRuns: new Map(),\n arrows: [],\n arrowRuns: new Map(),\n edgeCurves: [],\n edgeCurveRuns: new Map(),\n}\n\n// MobX tracks every observable read while a computed or autorun runs, so\n// passing values here registers them as dependencies without otherwise using\n// them.\nfunction dependOn(..._values: unknown[]) {}\n\n// Hard size cap for the single-mode graph view. Past it the view declines with a\n// \"zoom in\" message rather than switching to a degraded rendering mode \u2014 one mode\n// only, since the large-region case is a linear synteny view, not a graph.\n// (The `adr-027` citation this used to carry was stale: that number was reused\n// for wheel-input semantics after the large-mode ADR was removed in 9d8102f0b5.)\n//\n// This bounds the *fetch*, and it is the only cap that can be applied before one\n// happens. It is a poor proxy for cost, because cost tracks node count, so the\n// number is chosen against the density of the graphs it can actually guard: a\n// region is fetched through `getSubgraph`, and the rGFA cut is what this number\n// was chosen against (the GBZ cut is base-level and has its own `nodeLimit`,\n// which trips long before 5 Mb). Both measured rGFAs are sparse, and at 5 Mb\n// both land at or under the ~2k nodes that redraw in under 10 ms (see agent-docs/GRAPH_SCALE_AND_LOD.md):\n//\n// HPRC MC GRCh38 ~7,000 bp/segment whole 4.9 Mb MHC 1,173 nodes\n// ecoli minigraph 3,078 bp/segment whole 4.6 Mb genome 2,415 nodes\n//\n// Fetch cost does not scale with the window either: against the hosted HPRC\n// index a 4.9 Mb links query and a 100 kb one both cost ~1.3 s, being dominated\n// by HTTP setup. FMMM is the slowest consumer at this size and stays tolerable,\n// ~0.1-3 s for ~1k nodes depending on layout quality (bandage-layout-js), and it\n// runs off the main thread.\n//\n// A dense graph is what this cannot protect against, because bp per node varies\n// by orders of magnitude between graph types \u2014 a base-level pggb graph runs ~17\n// bp/node, where 5 Mb would be hundreds of thousands. Such a graph has no region\n// query to reach this check with today, and the node budget below is the cap that\n// catches it if one ever does.\nexport const MAX_GRAPH_REGION_BP = 5_000_000\n\n// The cap in a message, in the unit that reads: `5 Mb`, not `5000 kb`.\nexport function formatSpanBp(bp: number) {\n return bp >= 1_000_000\n ? `${+(bp / 1_000_000).toFixed(1)} Mb`\n : `${Math.round(bp / 1000)} kb`\n}\n\n// What the view will actually draw, checked once the graph is parsed and so\n// applying to a whole-file import as much as to a subgraph fetch. Measured on\n// bubble-chain graphs: ~1-2k nodes redraws in under 10 ms, 10k takes ~43 ms of\n// geometry and ~125k canvas draw calls per frame (single-digit fps while\n// panning), and 100k needs 632 ms and 75 MB of vertex buffers. So this is set\n// where the tab stops being usable rather than where it stops being smooth, and\n// it is a view prop rather than a constant so a session can raise it \u2014 the same\n// escape hatch strangepg gives with `-T N`.\nexport const DEFAULT_MAX_GRAPH_NODES = 20_000\n\ntype ViewportOwner = 'fit' | 'user' | 'host'\n\ninterface BuiltViewport {\n scale: number\n bounds: Bounds\n viewportDirty: number\n}\n\nfunction geometryPainted(model: {\n viewportRebuildPending: boolean\n viewportDirty: number\n builtViewport?: BuiltViewport\n paintedViewport?: BuiltViewport\n}) {\n return (\n model.builtViewport !== undefined &&\n model.paintedViewport === model.builtViewport &&\n model.builtViewport.viewportDirty === model.viewportDirty &&\n !model.viewportRebuildPending\n )\n}\n\n// What the pane shows, in layout units.\nfunction paneViewportOf(model: {\n translateX: number\n translateY: number\n paneWidth: number\n axisScale: AxisScale\n canvasHeight: number\n}): Bounds {\n return viewportOf(model, model.axisScale, model.paneWidth, model.canvasHeight)\n}\n\n// Force layouts already computed for a graph, keyed by the view props the\n// engine reads. Every layout mode is local and costs a millisecond except this\n// one, which is seconds: measured on the committed engine, a 1,201-node bubble\n// chain is 0.6 s proportional, 4.3 s at 'Wide bubbles' and 3.4 s at the highest\n// quality.\n//\n// Worth keeping because a session revisits the same layout. The two drawings\n// answer different questions and the README's own figure is one subgraph in\n// both, so Anchored <-> Force is a round trip a reader makes repeatedly \u2014 and\n// every control in the settings dialog calls `recomputeLayout` on change\n// whether or not the current mode reads what it changed, which made picking a\n// reference path in force mode a multi-second re-run of an identical layout.\n// Both are hits.\n//\n// Entries are the live position objects rather than copies, which a node drag\n// mutates in place (see moveNode), so a layout comes back arranged the way it\n// was left rather than snapping back.\n//\n// Weak on the graph so nothing has to invalidate it: a graph is rebuilt by\n// every load and dropped by `clearGraph`, and its layouts go with it.\nconst forceLayouts = new WeakMap<Graph, Map<string, LayoutResult>>()\n\n// Enough to hold the settings a comparison moves between; past that the oldest\n// goes, since a big graph's positions are megabytes.\nconst FORCE_LAYOUT_CACHE_SIZE = 4\n\nfunction forceLayoutsOf(graph: Graph) {\n let cache = forceLayouts.get(graph)\n if (!cache) {\n cache = new Map()\n forceLayouts.set(graph, cache)\n }\n return cache\n}\n\n// Re-anchoring builds a new Graph, and the force layout of it is the same\n// drawing: `anchorFromPaths` rewrites each node's `stable` and `samples` and\n// touches neither the ids, the lengths, the depths nor the edges, which are all\n// the engine reads. So the layouts follow the graph they were computed for\n// rather than being thrown away with it.\nfunction inheritForceLayouts(from: Graph, to: Graph) {\n const cache = forceLayouts.get(from)\n if (cache && from !== to) {\n forceLayouts.set(to, cache)\n }\n}\n\nfunction remember(\n cache: Map<string, LayoutResult>,\n key: string,\n result: LayoutResult,\n) {\n if (cache.size >= FORCE_LAYOUT_CACHE_SIZE) {\n cache.delete(cache.keys().next().value!)\n }\n cache.set(key, result)\n}\n\nexport function GraphPaneMixin() {\n return types\n .compose(\n 'GraphPane',\n RenderLifecycleMixin(),\n types.model({\n // FMMM's iteration budget, 0-4, the same scale Bandage's own settings\n // dialog exposes: 3+1 iterations at 0, 15+10 at 1, 30+20 at 2, 60+40 at\n // 3, 120+60 at 4 (BandageNG layout/graphlayoutworker.cpp).\n //\n // 2 because that is what the engine we vendor ships as its default\n // (`graphLayoutQuality = IntSetting(2, 0, 4)`, program/settings.cpp).\n // This was 1 for no recorded reason, i.e. one step BELOW upstream, and a\n // first sight of any graph is the default: review kept asking whether\n // the drawing could be iterated more (\"are you sure you can't iterate it\n // more times for better layout?\") of figures that were simply on a lower\n // setting than the tool they come from. Cost at the sizes this view\n // draws is milliseconds, and the header states the layout time so the\n // difference is visible rather than asserted.\n layoutQuality: types.optional(types.number, 2),\n linearLayout: types.optional(types.boolean, false),\n // unset takes the host's default: force in a view of its own, the\n // display config's in a track\n layoutMode: lenientMaybeEnum(LAYOUT_MODE_VALUES),\n colorScheme: lenientMaybeEnum(COLOR_SCHEME_VALUES),\n // How far the force layout opens a bubble, which on a variation graph is\n // the difference between a legible drawing and a rope. See\n // BUBBLE_SPREADS; no effect on the reference-anchored layouts, which\n // place a node from its coordinates rather than from a force sim.\n bubbleSpread: lenientOptionalEnum(BUBBLE_SPREAD_VALUES, 'auto'),\n // Node thickness by depth, Bandage's own device; see NODE_WIDTHS.\n nodeWidth: lenientOptionalEnum(NODE_WIDTH_VALUES, 'depth'),\n // Whether the node layouts draw each bubble as a halo along its nodes\n // with a label that opens it. Off by default: on a base-level cut\n // every SNP's halo is a blob.\n showBubbles: types.optional(types.boolean, false),\n showDeletionEdges: types.optional(types.boolean, false),\n // The session's genes drawn onto the backbone: exons along the nodes\n // that carry them, names pinned at their midpoints. See genes/.\n showGenes: types.optional(types.boolean, true),\n // Whether a drawing in its own coordinates inside a linear view gets\n // the strip of reference segments at their bp. See referenceStrip.ts.\n showReferenceStrip: types.optional(types.boolean, true),\n // Variants under this many bp fold into the reference before a tube map\n // is laid out, each kept as a tick on its walk's tube (coarsen.ts); 0\n // draws every one\n tubeMapFold: types.optional(types.number, 0),\n // Which track the genes come from; empty picks the assembly's\n // annotation track (pickGeneTrack).\n geneTrackId: types.optional(types.string, ''),\n // Which track the walk rows read tandem repeat arrays from; empty\n // picks a track whose name says repeats (pickRepeatTrack).\n repeatTrackId: types.optional(types.string, ''),\n // The array the walk rows measure between and tile by, as\n // RepeatArray.key; empty measures the whole window untiled.\n repeatKey: types.optional(types.string, ''),\n // Samples whose walks the walk rows show, by the name before the\n // haplotype number; undefined shows every walk the cut holds.\n walkRowSamples: types.maybe(types.frozen<string[]>()),\n // Walks lifted out of the drawing, each a layer with a lane of its own\n // and the rest fading, coloured by the encoding it states or by the\n // default one. See walkEncoding.ts. Empty lifts none.\n walkLayers: types.optional(types.frozen<WalkLayer[]>(), []),\n // Facets, as a grammar of graphics splits a plot: 'walk' draws the\n // pane once per lifted walk, side by side on the same layout, each\n // panel with that walk alone. See facetPanels.\n facet: lenientOptionalEnum<'none' | 'walk'>(['none', 'walk'], 'none'),\n // Which of a general GFA's paths the anchored layouts put on x. A path\n // GFA's names are arbitrary and none of them is marked as the\n // reference, so this is a choice; empty means \"infer\", which is the\n // assembly a subgraph was cut against, and the first path in the file\n // for a whole-file import. No effect on an rGFA, whose segments carry\n // their own coordinates. See pathAnchoring.ts.\n referencePath: types.optional(types.string, ''),\n // Whether the toolbar spells out `fetch 12371ms \u00B7 layout 4ms \u00B7 geom 9ms`.\n // Off by default because it was in the toolbar of every published graph\n // figure, where a machine's fetch time is noise a reader has to skip\n // past. The numbers themselves are always on the element as `data-*`\n // attributes, which is what browser tests assert against, so hiding the\n // text costs no coverage.\n showPerf: types.optional(types.boolean, false),\n contigThickness: types.optional(types.number, 6),\n connectorThickness: types.optional(types.number, 2),\n darkMode: types.optional(types.boolean, false),\n scale: types.optional(types.number, 1),\n translateX: types.optional(types.number, 0),\n translateY: types.optional(types.number, 0),\n drawPaths: types.optional(types.boolean, false),\n // Ceiling on the drawing pane, in css px, for a session or a figure that\n // wants a shorter one than the drawing asks for. Unset means the built-in\n // MAX_CANVAS_HEIGHT, which is what every existing session gets.\n //\n // Why this is a knob rather than a smaller default: the pane is as tall\n // as the drawing's own aspect ratio (see canvasHeight), which is right \u2014\n // a graph that is twice as tall as it is wide should not be squeezed\n // into a strip. But one very long node in a cut of short ones makes that\n // aspect ratio all arc: the HPRC CHM13 figure draws a 142 kb allele\n // among sub-kb backbone segments, pins the 600 px ceiling and spends\n // most of it on the loop, with the chain squashed along the bottom edge.\n // Lowering the ceiling there scales the whole drawing down, which is\n // exactly the trade that figure wants and the wrong default for a graph\n // whose height is carrying information.\n paneHeight: types.maybe(types.number),\n // Raise to draw a bigger graph than the default budget allows; see\n // DEFAULT_MAX_GRAPH_NODES for what the numbers cost.\n maxGraphNodes: types.optional(types.number, DEFAULT_MAX_GRAPH_NODES),\n // The reference span the reference-position ramp runs over, for a graph\n // with no region of its own to span it\n colorDomain: types.maybe(\n types.frozen<{ start: number; end: number }>(),\n ),\n // the linear view a graph of its own is paired with for the hover sync\n connectedViewId: types.maybe(types.string),\n }),\n )\n .volatile(() => ({\n graph: undefined as Graph | undefined,\n // the reference window the graph on screen was cut for, set with it\n graphRegion: undefined as SubgraphRegion | undefined,\n // The walk the track's graph lies on for the region's assembly, which\n // the track's config declares; x starts along it unless the user chose\n // another. Undefined for an rGFA, whose backbone is fixed, and for a\n // graph with no region.\n loadedReferencePath: undefined as string | undefined,\n layoutResult: undefined as LayoutResult | undefined,\n // what the legends in the pane's top-right corner measure, so no label\n // is placed under them\n legendSize: { width: 0, height: 0 },\n // The bubble index rows over the cut window, when the source track has\n // one beside its segments. Undefined for a graph with no index, whose\n // bubbles are `derivedBubbles` instead.\n indexBubbles: undefined as MinigraphBubble[] | undefined,\n // the genes over the cut window, read once per cut from the gene track\n geneFeatures: undefined as GeneModel[] | undefined,\n // the tandem repeat arrays over the cut window, from the repeat track\n repeatArrays: undefined as RepeatArray[] | undefined,\n // The graphs the open bubble was popped out of, outermost first, each\n // with what closing back to it restores without a refetch. A stack so a\n // popped superbubble can be mapped and popped again.\n popStack: [] as {\n graph: Graph\n layoutMode: LayoutModeValue\n label: string\n indexBubbles: MinigraphBubble[] | undefined\n }[],\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n error: undefined as unknown,\n isLoading: false,\n loadCanceled: false,\n statusMessage: '',\n hoveredNode: null as string | null,\n // the bubble whose label the pointer is on\n hoveredBubble: null as MinigraphBubble | null,\n // whether the pointer is over this pane, whose own hit test then says\n // what it is on\n pointerInPane: false,\n hoveredEdge: null as number | null,\n selectedNode: null as string | null,\n viewportDirty: 0,\n // Bumped when the layout's positions are mutated IN PLACE, which only a\n // node drag does. Separate from `viewportDirty` because that one also\n // fires on pan and zoom, and the things keyed off this \u2014 the two hit\n // indexes and the label placement's per-layout caches \u2014 do not depend on\n // the transform at all. Rebuilding a 12k-edge hit index because the user\n // panned cost ~14 ms of the first mousemove after every gesture.\n positionsVersion: 0,\n // Bumped per upload, so the hover autorun re-states its highlights\n // against the batch the renderer now holds.\n geometryVersion: 0,\n // The zoom and the window the current batch was built for; a pan that\n // stays inside it needs no rebuild.\n builtViewport: undefined as BuiltViewport | undefined,\n // The builtViewport whose batch is on the canvas\n paintedViewport: undefined as BuiltViewport | undefined,\n draggingNode: null as string | null,\n // Dragging the background rather than a node. Lives here beside\n // draggingNode instead of in a component ref+state pair, so the two\n // mutually exclusive drag modes are one piece of state read from one place.\n isPanning: false,\n // Who places the view. 'fit' refits it to every new layout, 'user' leaves\n // it where a gesture or a restored session put it, and 'host' takes x\n // from the linear view the pane sits in. Not derived from\n // `isDefaultViewport`, which zoomToFit itself invalidates on its first\n // run \u2014 that made the fit fire once against not-yet-measured dimensions\n // and then never re-fit.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n viewportOwner: 'fit' as ViewportOwner,\n viewportDirtyTimer: undefined as\n ReturnType<typeof setTimeout> | undefined,\n // 0 is never a live rAF handle, so it doubles as \"nothing pending\"\n positionsDirtyFrame: 0,\n // Performance instrumentation, surfaced in GraphStats for browser tests\n // to assert against budgets. `fetchMs` is the GetSubgraph RPC round-trip,\n // `layoutMs` is the Bandage FMMM compute time reported by the\n // GraphComputeLayout RPC, `geometryMs` is the main-thread buildGeometry\n // pass and `geometryStrokeCount` the node strokes it produced.\n lastFetchMs: undefined as number | undefined,\n // how many of the cut's reads the tube map was handed, of how many\n readsShown: undefined as { shown: number; total: number } | undefined,\n lastLayoutMs: undefined as number | undefined,\n lastGeometryMs: undefined as number | undefined,\n lastGeometryStrokeCount: undefined as number | undefined,\n viewportRebuildPending: false,\n }))\n .views(self => ({\n get defaultLayoutMode(): LayoutModeValue {\n return 'force'\n },\n get defaultColorScheme(): ColorScheme {\n return 'auto'\n },\n // a source declared but not yet loaded\n get hasPendingSource() {\n return false\n },\n get canRetryLoad() {\n return false\n },\n // the track the graph is cut from: the one the pane is a display of\n get sourceTrack(): AnyConfigurationModel | undefined {\n try {\n return getContainingTrack(self).configuration\n } catch {\n return undefined\n }\n },\n }))\n .views(self => ({\n get chosenLayoutMode() {\n return self.layoutMode ?? self.defaultLayoutMode\n },\n get chosenColorScheme() {\n return self.colorScheme ?? self.defaultColorScheme\n },\n }))\n .views(self => ({\n get paneWidth() {\n return getContainingView(self).width\n },\n // The graph the drawing's node ids address: a folded tube map's coarse\n // graph, the cut otherwise. Hover, details and labels read it, so a\n // merged node reports its own span and length.\n get drawnGraph() {\n return self.layoutResult?.tubeMap?.coarse?.graph ?? self.graph\n },\n }))\n .views(self => ({\n get nodeById() {\n if (self.drawnGraph) {\n const m = new Map<string, GraphNode>()\n for (const n of self.drawnGraph.nodes) {\n m.set(n.id, n)\n }\n return m\n }\n return undefined\n },\n // bp per node id, which is all the label overlay needs of a GraphNode.\n // Held here rather than rebuilt in the component: that render runs on\n // every pan, and the map is a fact about the graph (measured 6.9 ms an\n // allocation on a 30k-node cut).\n get nodeLengths() {\n const m = new Map<string, number>()\n for (const n of self.drawnGraph?.nodes ?? []) {\n m.set(n.id, n.length)\n }\n return m\n },\n get nodeCount() {\n return self.graph?.nodes.length ?? 0\n },\n get edgeCount() {\n return self.graph?.edges.length ?? 0\n },\n get pathCount() {\n return self.graph?.paths?.length ?? 0\n },\n get hasGraph() {\n return self.graph !== undefined\n },\n get geometryPainted() {\n return geometryPainted(self)\n },\n // The app-wide readiness contract (AppReadyMarker, @jbrowse/capture)\n // reads this: a declared source still fetching, or a graph whose\n // settled geometry is not on the canvas yet.\n get showLoading() {\n return (\n self.error === undefined &&\n (self.isLoading ||\n self.hasPendingSource ||\n (self.graph !== undefined && !geometryPainted(self)))\n )\n },\n // Only before anything is drawn: past that point a reload is superseded\n // by the next setting change, and stopping one midway would leave a new\n // graph under the previous graph's layout.\n get canCancelLoad() {\n return self.isLoading && !self.layoutResult\n },\n // Whether the drawing on screen is the FMMM engine's. False before there\n // is a graph, since nothing is drawn by anything yet.\n //\n // Read by the settings that only the engine looks at: the layout quality\n // and the bubble spread do nothing at all to an anchored drawing, which\n // places a node from its coordinates, and the guide says so in prose\n // precisely because the dialog did not. A control that silently does\n // nothing is worse than one that says it cannot \u2014 the same rule the\n // `drawPaths` switch already follows.\n get usesLayoutEngine() {\n return self.graph\n ? modeUsesLayoutEngine(self.chosenLayoutMode, self.graph)\n : false\n },\n // A rank-0 backbone to draw x against, whether the segments declared it\n // (rGFA) or a path walk derived it. Only a GFA with neither tags nor\n // paths has no backbone at all, and there force is the one honest layout.\n get canAnchorLayout() {\n return self.graph?.nodes.some(n => n.stable?.rank === 0) ?? false\n },\n // The paths this graph could be anchored on, for the picker. Empty for an\n // rGFA, which has no P/W records and needs none.\n get anchorPaths() {\n return self.graph?.anchorPaths ?? []\n },\n // Which haplotype each ribbon colour belongs to, in the order the file\n // states the paths \u2014 the same list and the same order the geometry keys\n // its colours off, so the key cannot name a colour that is not drawn.\n // Empty unless the ribbons are actually on: a colour key beside a drawing\n // with no colours in it is a legend for nothing.\n // The tube map colours its tubes whether or not paths are drawn on the\n // nodes, so under it the key follows the tubes, and names the colours\n // the tubes were drawn in.\n get pathLegend() {\n const paths = self.graph?.paths\n const tubeMap = self.layoutResult?.tubeMap\n const colouring = self.drawPaths || tubeMap\n return colouring && paths && pathColorsLegible(paths.length)\n ? pathLegend(paths, tubeMap?.pathColors)\n : []\n },\n // Every walk the graph carries, named for a picker, whatever the count.\n get walkChoices() {\n const paths = self.graph?.paths\n return paths?.length ? pathLegend(paths) : []\n },\n // Whether the drawing is actually painted per path, which is not the same\n // question as whether the user asked for it: past MAX_PATH_COLORS the\n // colours say nothing and cost a stroke per path per edge. A bare getter\n // returning a resolved value, the same shape and for the same reason as\n // `effectiveColorScheme` \u2014 every consumer of a path colour reads this\n // one, so the geometry, the hit index and the key cannot disagree about\n // whether there are any. The switch in the settings dialog reads the raw\n // `drawPaths`, so the user's choice stays visible as the choice it is.\n get effectiveDrawPaths() {\n return (\n self.drawPaths && pathColorsLegible(self.graph?.paths?.length ?? 0)\n )\n },\n // The path x is currently drawn on, which is not necessarily the one\n // `referencePath` asked for: an unmatched name falls back rather than\n // leaving the graph unanchored, and the picker has to show what happened.\n get activeReferencePath() {\n return self.graph?.referencePath\n },\n // Undefined for a graph with neither, where the ramp spans the drawn\n // extent (computeReferenceRamp)\n get rampDomain() {\n return self.colorDomain ?? self.graphRegion\n },\n // The lifted walks the graph on screen carries, or undefined when none\n // is named or it carries none of those that were. A walk coloured by\n // reference position reads the ramp whatever the node colour scheme.\n get walkLift() {\n const { graph } = self\n return graph && self.walkLayers.length > 0\n ? walkLift(\n graph,\n self.walkLayers,\n computeReferenceRamp(graph, this.rampDomain),\n )\n : undefined\n },\n // One panel per lifted walk while the pane is faceted by walk, each a\n // lift of that walk alone. The panels share one scale, light to dark\n // along each walk, as a faceted plot shares its axes, so they compare\n // at a glance; a colour a layer states still wins.\n get facetPanels() {\n const { graph } = self\n const lift = this.walkLift\n if (self.facet !== 'walk' || !graph || !lift || lift.walks.length < 2) {\n return undefined\n }\n const ramp = computeReferenceRamp(graph, this.rampDomain)\n return lift.walks.flatMap(w => {\n const layer = self.walkLayers.find(l => l.walk === w.name)\n const panel = walkLift(\n graph,\n [\n {\n walk: w.name,\n color: { field: 'progress', scheme: 'red', ...layer?.color },\n },\n ],\n ramp,\n )\n return panel ? [panel] : []\n })\n },\n // The scheme the renderer actually paints with, which is the raw prop\n // unless it is 'auto'. A bare getter returns a resolved value (root\n // CLAUDE.md): every consumer of a colour reads this one, and the two\n // dropdowns read `colorScheme` so \"Auto\" stays visible as the choice it\n // is.\n //\n // 'auto' asks the GRAPH, not the layout. A hue along the reference is\n // meaningful whenever the segments have reference coordinates \u2014\n // `anchoredBy` is exactly that question \u2014 and a force-directed drawing of\n // an rGFA is the case that proves it: the layout has no reference axis and\n // the ramp still says where on the reference each node came from, which is\n // the only quantity a linear track beside it can be painted with too.\n get effectiveColorScheme(): ResolvedColorScheme {\n return resolveColorScheme(self.chosenColorScheme, self.graph)\n },\n get nodePositions() {\n return self.layoutResult?.nodePositions\n },\n // a length label would sit on the lanes of lifted walks\n get labelsNodeSizes() {\n return !self.layoutResult?.tubeMap && !this.walkLift\n },\n // Empty rather than undefined: every consumer maps over it, and a layout\n // with no row structure (FMMM) is a normal state, not a missing one.\n get rowLabels() {\n return self.layoutResult?.rowLabels ?? []\n },\n // Deletions the layout found inside an allele, empty for the same reason\n // rowLabels is: a layout drawn at sequence scale states none, because\n // there a node's drawn extent already IS its length. `deletions` beside\n // this one is the bare-edge kind, which every layout has.\n get alleleDeletions() {\n return self.layoutResult?.alleleDeletions ?? []\n },\n // Whether the current layout states y in screen px rather than in the same\n // units as x (LayoutResult.pixelRows). Everything that has to put the two\n // axes in one expression reads this through scaleX/scaleY.\n get pixelRows() {\n return self.layoutResult?.pixelRows ?? false\n },\n get zoomPercent() {\n return `${(self.scale * 100).toFixed(1)}%`\n },\n // True while the persisted transform is still the schema default, i.e.\n // neither a restored session nor the user has positioned this view yet,\n // so an incoming layout is free to zoom-to-fit.\n get isDefaultViewport() {\n return (\n self.scale === 1 && self.translateX === 0 && self.translateY === 0\n )\n },\n }))\n .views(self => ({\n get poppedFrom() {\n return self.popStack.at(-1)\n },\n // The bubbles the graph itself states, for a graph with no index: a GBZ\n // cut, a pggb file, the inside of a popped bubble.\n get derivedBubbles() {\n return self.graph ? bubblesFromGraph(self.graph) : []\n },\n }))\n .views(self => ({\n // The bubbles halos draw and pops open. Index rows win where both exist:\n // gfatools measured every allele, the layered order only bounds them.\n get bubbles() {\n return self.indexBubbles?.length\n ? self.indexBubbles\n : self.derivedBubbles\n },\n }))\n .views(self => ({\n get sourceAdapter() {\n const track = self.sourceTrack\n return track\n ? (readConfObject(track, 'adapter') as Record<string, unknown>)\n : undefined\n },\n get sourceTrackId() {\n const track = self.sourceTrack\n return track ? (readConfObject(track, 'trackId') as string) : undefined\n },\n get assemblyTrackChoices() {\n const region = self.graphRegion\n if (!region) {\n return []\n }\n return getSession(self)\n .tracks.filter(t =>\n (readConfObject(t, 'assemblyNames') as string[]).includes(\n region.assemblyName,\n ),\n )\n .map(t => ({\n trackId: t.trackId as string,\n name: readConfObject(t, 'name') as string,\n adapterType: (readConfObject(t, 'adapter') as { type: string })\n .type,\n }))\n },\n }))\n .views(self => ({\n get geneTrackChoices() {\n return self.assemblyTrackChoices.filter(t =>\n GENE_ADAPTER_TYPES.has(t.adapterType),\n )\n },\n // The session's feature tracks a repeat annotation could be, for the\n // picker; which one is read is pickRepeatTrack's choice.\n get repeatTrackChoices() {\n return self.assemblyTrackChoices.filter(t =>\n REPEAT_ADAPTER_TYPES.has(t.adapterType),\n )\n },\n // The arrays over the window that state a unit, for the Repeat picker.\n get repeatChoices() {\n return self.repeatArrays ?? []\n },\n }))\n .views(self => ({\n get repeatTrack() {\n return pickRepeatTrack(self.repeatTrackChoices, self.repeatTrackId)\n },\n get selectedRepeat() {\n return self.repeatChoices.find(r => r.key === self.repeatKey)\n },\n get backbone() {\n return self.graph ? graphBackbone(self.graph) : undefined\n },\n // An assembly by each name a backbone may spell it with: the session's\n // name and aliases, and the PanSN prefix the source track maps each of\n // those to.\n assemblySpellings(assemblyName: string): AssemblyNames {\n const { assemblyManager } = getSession(self)\n const assembly = assemblyManager.has(assemblyName)\n ? assemblyManager.get(assemblyName)\n : undefined\n const panSN = (self.sourceAdapter?.assemblyNameToPanSN ?? {}) as Record<\n string,\n string\n >\n const names = [\n assemblyName,\n ...(assembly ? [assembly.name, ...assembly.aliases] : []),\n ]\n return {\n name: assemblyName,\n aliases: [...names, ...names.flatMap(n => panSN[n] ?? [])],\n }\n },\n // Whether x lies along the reference the track's graph came with, not a\n // walk \"Draw x along\" picked. An rGFA's backbone is fixed.\n get drawsLoadedReference() {\n const graph = self.graph\n return (\n graph?.anchoredBy === 'tags' ||\n (graph?.referencePath !== undefined &&\n graph.referencePath === self.loadedReferencePath)\n )\n },\n }))\n .views(self => ({\n // the assembly the genes were read for\n get geneAssembly() {\n const region = self.graphRegion\n return region ? self.assemblySpellings(region.assemblyName) : undefined\n },\n }))\n .views(self => ({\n // The genes on the drawn backbone under its own refNames. The track's\n // config puts its reference on the cut's assembly, whatever the walk's\n // name. After \"Draw x along\" another walk, the backbone takes the genes\n // only where its PanSN prefix names their assembly.\n get backboneGenes() {\n const { backbone, geneAssembly, geneFeatures } = self\n const binds =\n self.drawsLoadedReference ||\n (geneAssembly !== undefined &&\n backboneAssembly(backbone, [geneAssembly]) !== undefined)\n return backbone && geneFeatures && binds\n ? featuresOnBackbone(geneFeatures, backbone)\n : undefined\n },\n }))\n .views(self => ({\n get nodeNeighbors() {\n return self.drawnGraph ? buildNeighbors(self.drawnGraph) : undefined\n },\n get nodeInk(): NodeInk {\n return nodeInk(\n self.drawnGraph,\n self.nodeById,\n self.contigThickness,\n self.nodeWidth,\n )\n },\n // Links that skip reference sequence, i.e. the deletions this graph\n // holds. Computed once per graph rather than per geometry rebuild: it is\n // a pass over the edges and the drawing rebuilds on every pan.\n // Walk rows state what each walk skips as its own bar length, so the arcs\n // over the backbone would only say it again, across the bars.\n get deletions() {\n return self.graph &&\n self.chosenLayoutMode !== 'walkrows' &&\n !self.layoutResult?.tubeMap\n ? deletionEdges(self.graph)\n : []\n },\n // One bar per haplotype walk on its own bp axis, for the walk-rows\n // overlay. Empty under every other layout.\n get walkRowBars() {\n if (self.chosenLayoutMode !== 'walkrows' || !self.graph) {\n return undefined\n }\n const repeat = self.selectedRepeat\n const bars = repeat\n ? walkRows(self.graph, repeat, repeat.unit)\n : walkRows(self.graph, self.graphRegion)\n if (!bars) {\n return undefined\n }\n const samples = self.walkRowSamples\n const rows = samples\n ? bars.rows\n .filter(r => samples.includes(r.sample))\n .sort(\n (a, b) =>\n samples.indexOf(a.sample) - samples.indexOf(b.sample) ||\n a.label.localeCompare(b.label),\n )\n : bars.rows\n const [reference, ...paired] = withCalls(\n [bars.reference, ...rows],\n repeat?.calls,\n )\n return { ...bars, reference: reference!, rows: paired }\n },\n // The labels drawn beside the rows. Walk rows label from the bars\n // themselves, which follow the selected repeat and sample filter that\n // the layout, run once per cut, cannot.\n get drawnRowLabels() {\n const bars = this.walkRowBars\n return bars\n ? [bars.reference, ...bars.rows].map((row, i) => ({\n label: row.label,\n y: i * ROW_HEIGHT_PX,\n }))\n : self.rowLabels\n },\n // The row labels are pinned to the pane's left edge, so the fit starts\n // the drawing past the widest one.\n get fitPadLeft() {\n return Math.max(\n FIT_PADDING,\n ...this.drawnRowLabels.map(r => rowLabelBox(r.label, 0).x1 + 6),\n )\n },\n // Exons and names on the backbone, in layout units. Reads\n // positionsVersion so a dragged node takes its exons with it.\n get genePins() {\n dependOn(self.positionsVersion)\n const positions = self.layoutResult?.nodePositions\n return self.showGenes &&\n self.chosenLayoutMode !== 'walkrows' &&\n !self.layoutResult?.tubeMap &&\n self.graph &&\n self.backboneGenes &&\n positions\n ? genePins(self.graph, self.backboneGenes, positions)\n : []\n },\n // The bubbles as halos along their nodes.\n // Reads positionsVersion so a dragged node takes its halo with it.\n get bubbleHalos() {\n dependOn(self.positionsVersion)\n const positions = self.layoutResult?.nodePositions\n if (\n !self.showBubbles ||\n self.chosenLayoutMode === 'walkrows' ||\n self.layoutResult?.tubeMap ||\n !self.graph ||\n !positions\n ) {\n return []\n }\n const labels = new Map(self.walkChoices.map(c => [c.name, c.label]))\n return bubbleHalos(\n self.graph,\n self.bubbles,\n positions,\n name => labels.get(name) ?? name,\n self.repeatArrays,\n )\n },\n // Every node's midpoint on the reference plus the interval the hue ramps\n // over \u2014 what `reference-position` paints from, and undefined under every\n // other scheme so the walk is never run for a colouring that ignores it.\n //\n // Here for the same reason `deletions` is, only more so: deriving it is a\n // bounded neighbour walk PER NODE (referenceMidpoints), it depends on\n // nothing the transform touches, and it was being redone inside every\n // geometry rebuild \u2014 1.6 ms at 1.5k nodes, 45 ms at 30k, on a pass that\n // reruns on each debounced pan and each drag frame. `reference-position`\n // is also what `auto` resolves to on any anchored graph, so this was the\n // default path rather than an opt-in one.\n get referenceRamp() {\n return self.effectiveColorScheme === 'reference-position' &&\n self.graph &&\n !self.layoutResult?.tubeMap\n ? computeReferenceRamp(self.graph, self.rampDomain)\n : undefined\n },\n // The assemblies this view is showing, which is the interface every\n // assembly-aware piece of the app looks for \u2014 `viewTitle` first among\n // them, which falls back through it and otherwise names the view\n // \"Untitled view\". Every published HPRC figure showed that.\n // `launchSubgraphView` only avoids it by writing `displayName`, so a\n // declaratively-instantiated view (a session snapshot, a figure spec) got\n // the fallback.\n //\n // A whole-file import has none: its stable names are a GFA's business and\n // need not name anything the session has loaded. Empty rather than\n // undefined, matching what an LGV with no displayed regions reports.\n get assemblyNames() {\n const region = self.graphRegion\n return region ? [region.assemblyName] : []\n },\n // Screen px per layout unit, per axis. `scale` is the x zoom and the whole\n // of what a user's wheel moves; y is a second question the layout answers\n // for itself.\n //\n // On a row layout the pitch is already in screen px (rowSpacing.ts), so\n // scaleY is 1 and stays 1: rows are a track's row height, unchanged by\n // zoom, and the backbone under them zooms the way the ruler above it does.\n // On an isotropic layout (FMMM) x and y are one simulation space and one\n // number scales both, exactly as before.\n //\n // Everything that mixes the two axes takes their ratio as `yToX`\n // (scaleY / scaleX) \u2014 see computeEdgeCurves.\n get scaleX() {\n return axisScaleOf(self.scale, self.pixelRows).scaleX\n },\n get scaleY() {\n return axisScaleOf(self.scale, self.pixelRows).scaleY\n },\n // The pair, for everything that draws or hit-tests. Handed over as one\n // value on purpose: every consumer needs both, and passing them separately\n // let a caller supply the x scale and default y, which compiles and draws\n // a wrong picture. See AxisScale.\n get axisScale() {\n return axisScaleOf(self.scale, self.pixelRows)\n },\n // Extent of the drawing in layout units, shared by the pane height and\n // zoomToFit. On a reference-bp layout x is the cut window rather than\n // how far the drawing reaches: an allele anchored far outside it is a\n // fact about the graph, not a reason to draw the window at 6% of the\n // frame. A popped bubble fits to what it drew. Walk rows reach as far as\n // the bars on screen, which a repeat pick or a sample filter narrows\n // after the layout ran.\n get layoutBounds() {\n const layout = self.layoutResult\n const bars = this.walkRowBars\n return layout\n ? drawingBounds(layout, {\n region: self.popStack.length === 0 ? self.graphRegion : undefined,\n extent: bars && layout.extent ? walkRowsExtent(bars) : undefined,\n })\n : undefined\n },\n }))\n .views(self => ({\n get geneTrack() {\n return pickGeneTrack(self.geneTrackChoices, self.geneTrackId)\n },\n // Which of `graph.edges` are deletions, and what each one bypasses, keyed\n // the way the geometry and the hit index address an edge. One map per\n // graph rather than per rebuild, since both of those take it on every\n // pan.\n get deletionEdgeIndexes() {\n return new Map(self.deletions.map(d => [d.edgeIndex, d.bypassed]))\n },\n get hiddenEdgeIndexes() {\n return new Set(\n self.showDeletionEdges ? [] : self.deletions.map(d => d.edgeIndex),\n )\n },\n // The interval the reference-position ramp runs over, for the key beside\n // the drawing, and undefined when no key should be drawn. Two tutorials\n // carry \"red to magenta is left to right of the cut window\" as a sentence\n // in prose because nothing on screen said it.\n //\n // Read off the ramp the drawing is actually painted with rather than\n // re-derived from the domain, so the key cannot come to describe a\n // different interval from the hues beside it \u2014 and so a graph that states\n // no domain, whose interval has to be measured, does not pay for a second\n // neighbour walk to say what the first one already worked out.\n get referenceRampDomain() {\n const ramp = self.referenceRamp\n return ramp\n ? { start: ramp.start, end: ramp.start + ramp.span }\n : undefined\n },\n // A node's reference interval, an allele's between its flanks\n nodeSpan(nodeId: string) {\n const { nodeById, nodeNeighbors: neighbors } = self\n return nodeById && neighbors\n ? nodeReferenceSpan({ nodeId, nodeById, neighbors })\n : undefined\n },\n }))\n .views(self => ({\n // The reference interval under the pointer: the hovered node's, or the\n // hovered bubble's\n get hoveredSpan() {\n const nodeId = self.hoveredNode\n const bubble = self.hoveredBubble\n return nodeId !== null\n ? self.nodeSpan(nodeId)\n : bubble\n ? { start: bubble.start, end: bubble.end }\n : undefined\n },\n }))\n .views(self => ({\n // The hovered span, for a connected linear view to highlight. Only a\n // graph cut from a track has one: a whole-file import has no region, and\n // its stable names need not name anything in a loaded assembly.\n get hoverHighlight() {\n const region = self.graphRegion\n const span = self.hoveredSpan\n return region && span\n ? {\n refName: region.refName,\n assemblyName: region.assemblyName,\n ...span,\n }\n : undefined\n },\n }))\n .views(self => ({\n // Every assembly this graph names a segment from, with the locus each one\n // contributes here. rGFA's SN tag is what makes this knowable: the graph\n // states its own contributors, so the view can offer a way out to each of\n // them without consulting an alignment.\n //\n // Gaps larger than the backbone span split a contributor's segments into\n // separate loci and the widest wins, so a sample that also contributes\n // sequence from a distant duplication is launched at the locus on screen\n // rather than at the union of the two.\n get contributingAssemblies() {\n const graph = self.graph\n const backbone = graph ? backboneNodes(graph) : []\n return graph\n ? contributingAssemblies(graph, {\n maxGap: backbone.length > 0 ? backboneSpan(backbone) : Infinity,\n })\n : []\n },\n }))\n .views(self => ({\n // A node's PanSN sample as an assembly this session can open, or\n // undefined. The graph's spelling and the assembly's need not agree:\n // HPRC writes `CHM13` where the assembly is UCSC's `hs1`. The track the\n // graph was cut from states that pairing in its `assemblyNameToPanSN`,\n // so its map is read first, then `assemblyManager`'s names and aliases.\n //\n // `has` before `get`, deliberately: `get` reports an unknown name to\n // `Core-handleUnrecognizedAssembly`, which asks every installed plugin to\n // go supply it, and this is a probe run for hundreds of haplotypes per\n // graph.\n get assemblyResolver() {\n const { assemblyManager } = getSession(self)\n const panSN = self.sourceAdapter?.assemblyNameToPanSN as\n Record<string, string> | undefined\n const byPrefix = new Map(\n Object.entries(panSN ?? {}).map(([asm, prefix]) => [prefix, asm]),\n )\n const loaded = (name: string | undefined) =>\n name !== undefined && assemblyManager.has(name)\n ? (assemblyManager.get(name)?.name ?? name)\n : undefined\n return (sample: string) =>\n loaded(byPrefix.get(sample)) ?? loaded(sample)\n },\n }))\n .views(self => ({\n // The contributors a view can actually be opened on: those naming an\n // assembly this session has loaded. Every strain of an E. coli pangenome\n // demo is its own assembly, so all of them resolve; an HPRC graph names\n // hundreds of haplotypes that no session loads, so only the reference\n // does.\n get launchableAssemblies() {\n return resolveContributors(\n withReferenceRegion(self.contributingAssemblies, self.graphRegion),\n self.assemblyResolver,\n )\n },\n // Whether there is a linear view this graph may draw a highlight into \u2014\n // the paired one, or the session's only one on the reference assembly.\n // Read by the node menu, which offers the item only when it would land\n // somewhere.\n get canHighlightInLinearView() {\n const region = self.graphRegion\n return (\n region !== undefined &&\n linearViewTarget({\n views: [...getSession(self).views],\n connectedViewId: self.connectedViewId,\n assemblyName: region.assemblyName,\n }) !== undefined\n )\n },\n // Where one node can be opened: on its own assembly, and on the reference\n // the graph was cut against. Both are padded to a readable window \u2014 a\n // base-level allele is a few bp, and a linear view framed on exactly that\n // shows no context at all.\n nodeLaunchTargets(nodeId: string) {\n const node = self.nodeById?.get(nodeId)\n const own = node ? nodeOwnLocation(node) : undefined\n const ownAssembly = own\n ? resolveLocationAssembly(self.assemblyResolver, own)\n : undefined\n const region = self.graphRegion\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n const span =\n region && nodeById && neighbors\n ? nodeReferenceSpan({ nodeId, nodeById, neighbors })\n : undefined\n const onReference =\n region && span\n ? {\n sample: region.assemblyName,\n haplotype: undefined,\n refName: region.refName,\n ...span,\n }\n : undefined\n return {\n own:\n own && ownAssembly\n ? { location: paddedLocation(own), assembly: ownAssembly }\n : undefined,\n reference:\n region && onReference\n ? {\n location: paddedLocation(onReference),\n assembly: region.assemblyName,\n }\n : undefined,\n // Unpadded: a mark is of the node, the same span its hover band\n // draws. The padding above is room to read a view opened on it.\n highlight:\n region && onReference\n ? { location: onReference, assembly: region.assemblyName }\n : undefined,\n }\n },\n }))\n .views(self => ({\n // The linear view this pane draws inside, when a display hosts it: x is\n // that view's window and the cut is re-made when the window leaves it.\n // A pane that is a view of its own has none.\n get host(): LinearHost | undefined {\n let view: unknown\n try {\n view = getContainingView(self)\n } catch {\n return undefined\n }\n return isLinearHost(view) ? view : undefined\n },\n }))\n .views(self => ({\n // Whether the drawing's x is reference bp a host can place. Walk rows\n // put the backbone at its bp too, but their bars are lengths, so the\n // mode's `cutMargins` is what says the drawing is a picture.\n get xIsReferenceBp() {\n return (\n self.layoutResult?.referenceAxis === true &&\n layoutModeByValue(self.chosenLayoutMode).cutMargins\n )\n },\n }))\n .views(self => ({\n // Whether the host places x. Only a layout whose x is reference bp can\n // take the window's transform; force, ordered and walk rows draw in\n // their own coordinates inside the track, and a popped bubble is a\n // picture of its own.\n get hostPlacesX() {\n const { host, graphRegion: region } = self\n return (\n host !== undefined &&\n host.initialized &&\n region !== undefined &&\n self.xIsReferenceBp &&\n self.popStack.length === 0 &&\n !host.dynamicBlocks.contentBlocks.some(\n b => b.refName === region.refName && b.reversed,\n )\n )\n },\n }))\n .views(self => ({\n get hostFrame() {\n const { host, graphRegion } = self\n return self.hostPlacesX && host && graphRegion\n ? hostFrame(host, graphRegion)\n : undefined\n },\n get tubeMapPicture() {\n const drawing = self.layoutResult?.tubeMap\n return drawing ? tubeMapPicture(drawing.layout) : undefined\n },\n get tubeMapReference() {\n const drawing = self.layoutResult?.tubeMap\n return drawing && self.drawnGraph\n ? referenceBoxes(self.drawnGraph, drawing.layout)\n : undefined\n },\n // the folded variants, as ticks on the tubes of the walks carrying them\n get tubeMapDeviations() {\n const coarse = self.layoutResult?.tubeMap?.coarse\n const layout = self.layoutResult?.tubeMap?.layout\n return coarse && layout\n ? deviationMarks(coarse.graph, layout, coarse.deviations)\n : []\n },\n // the reference boxes a linear view's connectors tie to its bp, which\n // the reference axis already puts under that bp\n get tubeMapReferenceNodes() {\n const layout = self.layoutResult\n return layout?.tubeMap && !layout.referenceAxis && self.drawnGraph\n ? referenceNodes(self.drawnGraph, layout.tubeMap.layout)\n : undefined\n },\n }))\n .views(self => ({\n // A linear view has the genes in a track of their own, at their bp\n get tubeMapGenes() {\n const reference = self.tubeMapReference\n return self.showGenes && !self.host && reference && self.backboneGenes\n ? tubeMapGenes(reference, self.backboneGenes)\n : []\n },\n // A drawing of nodes in its own coordinates inside a linear view, whose\n // reference segments the strip can put back at their bp. A tube map has\n // its own bands, and walk rows' bars are lengths, not nodes.\n get referenceStripApplies() {\n const { host, layoutResult } = self\n return (\n !!host?.initialized &&\n !!layoutResult &&\n !layoutResult.tubeMap &&\n !self.hostPlacesX &&\n self.chosenLayoutMode !== 'walkrows'\n )\n },\n }))\n .views(self => ({\n get referenceStripBlocks() {\n const { graph } = self\n return self.showReferenceStrip && self.referenceStripApplies && graph\n ? referenceStripBlocks(graph, {\n colorScheme: self.effectiveColorScheme,\n referenceRamp: self.referenceRamp,\n walkNodes: self.walkLift?.nodeIds,\n walkColors: self.walkLift?.walks.find(w => w.reference)?.colors,\n })\n : []\n },\n // Read off the live blocks, so the strip follows every frame of a pan\n // in the linear view\n get referenceStripFrame() {\n const { host, graphRegion } = self\n return host?.initialized && graphRegion\n ? hostFrame(host, graphRegion)\n : undefined\n },\n }))\n .views(self => ({\n get referenceStripShown() {\n return (\n self.referenceStripBlocks.length > 0 &&\n self.referenceStripFrame !== undefined\n )\n },\n // Whether the graph draws reference past the window's edges. A boolean\n // rather than the bp, which moves on every frame of a pan and would\n // resize the legend with it.\n get referenceStripOverhangs() {\n const frame = self.referenceStripFrame\n if (!frame || self.referenceStripBlocks.length === 0) {\n return false\n }\n const { left, right } = stripOverhang(\n self.referenceStripBlocks,\n frame,\n self.paneWidth,\n )\n return left > 0 || right > 0\n },\n }))\n .views(self => ({\n // The lit span on the strip, and where the graph drew what it is of:\n // the hovered node, the hovered bubble's name, or the selected node\n get referenceStripLit() {\n if (!self.referenceStripShown) {\n return undefined\n }\n const toScreen = (p: { x: number; y: number }) => ({\n x: p.x * self.scaleX + self.translateX,\n y: p.y * self.scaleY + self.translateY,\n })\n const bubble = self.hoveredNode === null ? self.hoveredBubble : null\n if (bubble) {\n const halo = self.bubbleHalos.find(h => sameBubble(h.bubble, bubble))\n return {\n start: bubble.start,\n end: bubble.end,\n anchor: halo ? toScreen(halo.labelAt) : undefined,\n }\n }\n const nodeId = self.hoveredNode ?? self.selectedNode\n const span = nodeId === null ? undefined : self.nodeSpan(nodeId)\n return span && nodeId !== null\n ? {\n ...span,\n anchor: nodeAnchor(self.nodePositions?.[nodeId], toScreen),\n }\n : undefined\n },\n referenceStripNodeAt(sx: number, sy: number) {\n const frame = self.referenceStripFrame\n return self.referenceStripShown && frame\n ? stripBlockAt(self.referenceStripBlocks, frame, sx, sy)\n : undefined\n },\n // the strip, and the gap the fit leaves under it\n get referenceStripZonePx() {\n return self.referenceStripShown ? REFERENCE_STRIP_ZONE_PX : 0\n },\n }))\n .views(self => ({\n // `paneHeight` replaces the built-in ceiling rather than adding a second\n // clamp under it, and the floor still wins: a pane shorter than\n // MIN_CANVAS_HEIGHT leaves no room to hover a node and read its tooltip,\n // which is the reason that floor exists.\n get paneCeiling() {\n return Math.max(MIN_CANVAS_HEIGHT, self.paneHeight ?? MAX_CANVAS_HEIGHT)\n },\n // Room over a tube map for the rows its genes need, one inside the\n // padding and one more for each further gene that overlaps it, and\n // room for the reference strip\n get fitPadTop() {\n const rows = tubeMapGeneRows(self.tubeMapGenes)\n return (\n FIT_PADDING +\n Math.max(0, rows - 1) * GENE_ROW_PX +\n self.referenceStripZonePx\n )\n },\n // A tube map on its own axis reads by panning along it, as in\n // sequenceTubeMap. Rather than shrink a long cut to a strip, the fit\n // stops where its tubes are MIN_FIT_TUBE_PX wide, or at whatever fits\n // the tallest pane, with the cut's left end on screen.\n get minFitScale() {\n const bounds = self.layoutBounds\n const layout = self.layoutResult\n const tubePx = layout?.tubeMap?.layout.tracks[0]?.width\n return bounds && bounds.h > 0 && tubePx && !layout.referenceAxis\n ? Math.min(\n MIN_FIT_TUBE_PX / tubePx,\n (this.paneCeiling - this.fitPadTop - FIT_PADDING) / bounds.h,\n )\n : 0\n },\n // The pane is as tall as the drawing, rather than a fixed box the drawing\n // floats in. A row layout's rows are px, so its height is a sum; an\n // isotropic layout has no height of its own and takes its aspect ratio\n // at the width's fit. Neither reads `scale`, so the fit reads this\n // without feeding back into it.\n // Lifted walks' legend rows carry their scales, which makes the legend\n // wide, so the fit leaves it that width rather than draw under it\n get fitPadRight() {\n return self.walkLift\n ? Math.max(FIT_PADDING, self.legendSize.width + 2 * LEGEND_INSET_PX)\n : FIT_PADDING\n },\n // How the facet panels tile the pane: columns, and each panel's drawing\n // size, as tall as its drawing needs at the panel's width but no taller\n // than the pane's ceiling leaves room for\n get facetGrid() {\n const panels = self.facetPanels\n const bounds = self.layoutBounds\n if (!panels || !bounds || !(bounds.w > 0)) {\n return undefined\n }\n const columns =\n bounds.w > FACET_WIDE * bounds.h\n ? 1\n : Math.min(panels.length, FACET_MAX_COLUMNS)\n const rows = Math.ceil(panels.length / columns)\n const width = Math.floor(\n (self.paneWidth - (columns - 1) * FACET_GAP_PX) / columns,\n )\n const drawn = self.pixelRows\n ? bounds.h\n : (bounds.h * (width - 2 * FACET_PAD_PX)) / bounds.w\n const room =\n (this.paneCeiling -\n rows * FACET_TITLE_PX -\n (rows - 1) * FACET_GAP_PX) /\n rows\n const height = Math.floor(\n Math.max(\n MIN_FACET_PANEL_PX,\n Math.min(drawn + 2 * FACET_PAD_PX, room),\n ),\n )\n return {\n columns,\n width,\n height,\n total: rows * (height + FACET_TITLE_PX) + (rows - 1) * FACET_GAP_PX,\n }\n },\n get canvasHeight() {\n const bounds = self.layoutBounds\n const usableWidth = self.paneWidth - FIT_PADDING - this.fitPadRight\n const ceiling = this.paneCeiling\n if (!bounds) {\n return ceiling\n }\n const grid = this.facetGrid\n if (grid) {\n return Math.max(MIN_CANVAS_HEIGHT, grid.total)\n }\n // never shorter than the legend, which a flat drawing would clip\n const floor = Math.max(\n MIN_CANVAS_HEIGHT,\n self.legendSize.height +\n 2 * LEGEND_INSET_PX +\n self.referenceStripZonePx,\n )\n if (self.pixelRows) {\n return Math.min(\n ceiling,\n Math.max(floor, bounds.h + this.fitPadTop + FIT_PADDING),\n )\n }\n return bounds.w > 0 && usableWidth > 0\n ? Math.min(\n ceiling,\n Math.max(\n floor,\n bounds.h * Math.max(usableWidth / bounds.w, this.minFitScale) +\n this.fitPadTop +\n FIT_PADDING,\n ),\n )\n : ceiling\n },\n // Where the fit puts the drawing, or undefined until there is a layout\n // and a measured canvas to fit it into\n get fittedTransform() {\n const bounds = self.layoutBounds\n return bounds\n ? fitTransform(\n bounds,\n self.paneWidth,\n this.canvasHeight,\n self.pixelRows,\n {\n minScale: this.minFitScale,\n padLeft: self.fitPadLeft,\n padTop: this.fitPadTop,\n padRight: this.fitPadRight,\n },\n )\n : undefined\n },\n }))\n .views(self => ({\n get overlayLabels() {\n return layoutLabels(self)\n },\n get tubeMapFrame() {\n const drawing = self.layoutResult?.tubeMap\n return drawing\n ? tubeMapFrame(drawing, {\n scaleX: self.scaleX,\n translateX: self.translateX,\n scaleY: self.scaleY,\n translateY: self.translateY,\n usableHeight: self.canvasHeight - self.fitPadTop - FIT_PADDING,\n })\n : undefined\n },\n }))\n .views(self => ({\n // the reference ruler under the tubes, unless the linear view's ruler\n // is already the axis\n get tubeMapRulerBoxes() {\n const reference = self.tubeMapReference\n return reference && !self.hostPlacesX\n ? rulerBoxes(reference)\n : undefined\n },\n // The connectors run from the top of the pane down to the tubes' top\n get connectorZoneBottom() {\n const picture = self.tubeMapPicture\n const frame = self.tubeMapFrame\n return picture && frame ? frame.y(picture.bounds.minY) - 2 : 0\n },\n // Read off the live blocks, so the bands' tops follow every frame of a\n // pan in the linear view and their bottoms every pan of the tubes\n get tubeMapConnectors() {\n const nodes = self.tubeMapReferenceNodes\n const frame = self.tubeMapFrame\n const { host, graphRegion } = self\n const bp =\n nodes && frame && host?.initialized && graphRegion\n ? hostFrame(host, graphRegion)\n : undefined\n return nodes && frame && bp\n ? tubeMapConnectors(\n nodes,\n b => b * bp.scale + bp.translateX,\n frame.x,\n self.paneWidth,\n )\n : []\n },\n }))\n .views(self => ({\n // What the tube map's legend has to explain: that on the own axis a\n // box is as wide as the log of its length, the fold its walks' ticks\n // stand for, the reads' strands, and the marks on the reads in view\n get tubeMapKeys() {\n const layout = self.layoutResult\n const reads = layout?.tubeMap?.layout.reads ?? []\n const picture = self.tubeMapPicture\n const frame = self.tubeMapFrame\n const shown =\n picture && frame && mismatchesLegible(frame.yScale)\n ? picture.mismatches.filter(m =>\n mismatchOnScreen(m, { x: frame.x, width: self.paneWidth }),\n )\n : []\n return {\n logWidths: layout?.tubeMap !== undefined && !layout.referenceAxis,\n foldBp:\n self.tubeMapDeviations.length > 0 ? self.tubeMapFold : undefined,\n forwardReads: reads.some(r => !r.is_reverse),\n reverseReads: reads.some(r => r.is_reverse),\n mismatches: new Set(shown.map(m => m.kind)),\n }\n },\n tubeMapNodeAt(sx: number, sy: number) {\n const drawing = self.layoutResult?.tubeMap\n const frame = self.tubeMapFrame\n return (\n (drawing && frame ? tubeMapNodeAt(drawing, frame, sx, sy) : null) ??\n connectorAt(\n self.tubeMapConnectors,\n self.connectorZoneBottom,\n sx,\n sy,\n ) ??\n null\n )\n },\n }))\n .actions(self => ({\n setError(error: unknown) {\n self.error = error\n self.isLoading = false\n self.statusMessage = ''\n },\n finishLoading() {\n self.isLoading = false\n self.statusMessage = ''\n },\n setStatusMessage(message: string) {\n self.statusMessage = message\n },\n setFetchMs(ms: number) {\n self.lastFetchMs = ms\n },\n setLayoutMs(ms: number) {\n self.lastLayoutMs = ms\n },\n setGeometryMetrics(\n ms: number,\n strokeCount: number,\n built: { scale: number; bounds: Bounds },\n ) {\n self.lastGeometryMs = ms\n self.lastGeometryStrokeCount = strokeCount\n self.builtViewport = { ...built, viewportDirty: self.viewportDirty }\n self.geometryVersion++\n },\n markPainted() {\n self.paintedViewport = self.builtViewport\n },\n setLayoutQuality(quality: number) {\n self.layoutQuality = quality\n },\n setLinearLayout(linear: boolean) {\n self.linearLayout = linear\n },\n setLayoutMode(mode: LayoutModeValue) {\n self.layoutMode = mode\n },\n // Re-anchor in place rather than re-parsing: the coordinate walk is\n // already recorded on the graph, and only which path counts as rank 0\n // changes. The caller recomputes the layout, the same way it does after\n // setLayoutMode. An rGFA is left alone \u2014 its coordinates are not derived.\n setReferencePath(name: string) {\n self.referencePath = name\n const graph = self.graph\n if (graph?.anchoredBy === 'paths') {\n const reanchored = anchorFromPaths(graph, name)\n inheritForceLayouts(graph, reanchored)\n self.graph = reanchored\n }\n },\n setDrawPaths(draw: boolean) {\n self.drawPaths = draw\n },\n setShowPerf(show: boolean) {\n self.showPerf = show\n },\n setColorScheme(scheme: ColorScheme) {\n self.colorScheme = scheme\n },\n setBubbleSpread(spread: BubbleSpread) {\n self.bubbleSpread = spread\n },\n setNodeWidth(width: NodeWidth) {\n self.nodeWidth = width\n },\n setShowBubbles(show: boolean) {\n self.showBubbles = show\n },\n setShowDeletionEdges(show: boolean) {\n self.showDeletionEdges = show\n },\n setShowGenes(show: boolean) {\n self.showGenes = show\n },\n setShowReferenceStrip(show: boolean) {\n self.showReferenceStrip = show\n },\n setTubeMapFold(bp: number) {\n self.tubeMapFold = bp\n },\n setGeneTrackId(trackId: string) {\n self.geneTrackId = trackId\n },\n setRepeatTrackId(trackId: string) {\n self.repeatTrackId = trackId\n },\n setRepeatKey(key: string) {\n self.repeatKey = key\n },\n setWalkLayers(layers: WalkLayer[]) {\n self.walkLayers = layers\n },\n // Lift the walks named, in that order, keeping the colour any of them\n // already had\n liftWalks(names: string[]) {\n const had = new Map(self.walkLayers.map(l => [l.walk, l]))\n self.walkLayers = names.map(walk => had.get(walk) ?? { walk })\n },\n setFacet(facet: 'none' | 'walk') {\n self.facet = facet\n },\n toggleWalk(walk: string) {\n const layers = self.walkLayers\n self.walkLayers = layers.some(l => l.walk === walk)\n ? layers.filter(l => l.walk !== walk)\n : [...layers, { walk }]\n },\n setWalkColor(walk: string, color: Partial<WalkEncoding>) {\n self.walkLayers = self.walkLayers.map(l =>\n l.walk === walk ? { ...l, color: { ...l.color, ...color } } : l,\n )\n },\n\n // Undefined restores the built-in ceiling. Nothing recomputes: the pane\n // reads canvasHeight and the drawing is placed by zoomToFit, which the\n // caller runs if it wants the drawing refitted into the new pane.\n setPaneHeight(px: number | undefined) {\n self.paneHeight = px\n },\n setWalkRowSamples(samples: string[] | undefined) {\n self.walkRowSamples = samples\n },\n // Pair with a linear view for the hover sync, without ever repointing an\n // existing pairing: a graph launched *from* an LGV is already paired with\n // it, and stealing that would break the sync the user came in on. So a\n // launch out of the graph only claims the slot when it is empty.\n //\n // A slot naming a view that has been closed is empty too. Held, the view\n // the graph opened next never got its hover, since a highlight is drawn\n // only in the paired view.\n pairWithLinearView(viewId: string) {\n const paired = self.connectedViewId\n const stillOpen =\n paired !== undefined &&\n withRows([...getSession(self).views]).some(\n view => (view as { id?: unknown }).id === paired,\n )\n if (!stillOpen) {\n self.connectedViewId = viewId\n }\n },\n setHoveredNode(nodeId: string | null) {\n self.hoveredNode = nodeId\n },\n setHoveredBubble(bubble: MinigraphBubble | null) {\n self.hoveredBubble = bubble\n },\n setPointerInPane(inside: boolean) {\n self.pointerInPane = inside\n },\n setLegendSize(size: { width: number; height: number }) {\n self.legendSize = size\n },\n setHoveredEdge(edgeIdx: number | null) {\n self.hoveredEdge = edgeIdx\n },\n setSelectedNode(nodeId: string | null) {\n self.selectedNode = nodeId\n },\n setDraggingNode(nodeId: string | null) {\n self.draggingNode = nodeId\n },\n setPanning(panning: boolean) {\n self.isPanning = panning\n },\n stopDragging() {\n self.draggingNode = null\n self.isPanning = false\n },\n // Everything that names a part of the current graph. Reset when the graph\n // is replaced, and by clearGraph.\n clearInteractionState() {\n self.hoveredNode = null\n self.hoveredBubble = null\n self.hoveredEdge = null\n self.selectedNode = null\n self.draggingNode = null\n self.isPanning = false\n },\n showNodeDetails(nodeId: string) {\n const node = self.nodeById?.get(nodeId)\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n if (node && nodeById && neighbors) {\n const session = getSession(self)\n const region = self.graphRegion\n // refName/start/end are what makes the widget show a location rather\n // than a bare id, and they are the same span the linear view\n // highlights on hover.\n const span = region\n ? nodeReferenceSpan({ nodeId, nodeById, neighbors })\n : undefined\n if (isSessionModelWithWidgets(session)) {\n session.showWidget(\n session.addWidget('BaseFeatureWidget', 'baseFeature', {\n featureData: {\n id: node.id,\n name: node.name,\n length: node.length,\n depth: node.depth,\n rank: node.stable?.rank,\n stableName: node.stable?.refName,\n // The assembly that contributed this segment, split off the\n // stable name. On an HPRC graph this is the only thing that\n // says which of 400-odd haplotypes an allele came from.\n contributingAssembly: nodeOwnLocation(node)?.sample,\n // and the haplotype, where the graph's names state one:\n // the assembly to load to open this node on its own\n // coordinates\n contributingHaplotype: nodeOwnLocation(node)?.haplotype,\n // Every assembly that traverses it, which is a different\n // question and one only a path GFA can answer. Absent on an\n // rGFA rather than approximated, so the two are never\n // confused: there `contributingAssembly` is first-seen, not\n // carriage.\n carriedBy: node.samples?.join(', '),\n ...(region && span\n ? {\n refName: region.refName,\n assemblyName: region.assemblyName,\n start: span.start,\n end: span.end,\n }\n : {}),\n },\n }),\n )\n }\n }\n },\n\n // A gesture on a hosted graph moves the linear view, and the frame\n // clock brings x back here; y stays the pane's own.\n setTransform(s: number, tx: number, ty: number) {\n const { host } = self\n if (self.viewportOwner === 'host' && host) {\n host.horizontalScroll(self.translateX - tx)\n } else {\n self.viewportOwner = 'user'\n self.scale = clampZoom(s)\n self.translateX = tx\n }\n self.translateY = ty\n },\n zoom(factor: number, centerX: number, centerY: number) {\n const { host } = self\n if (self.viewportOwner === 'host' && host) {\n host.zoomTo(host.bpPerPx / factor, centerX)\n return\n }\n self.viewportOwner = 'user'\n const t = zoomAbout(self, factor, centerX, centerY, self.pixelRows)\n self.scale = t.scale\n self.translateX = t.translateX\n self.translateY = t.translateY\n },\n setViewportDirty() {\n self.viewportRebuildPending = false\n self.viewportDirty++\n },\n // The positions themselves moved, as opposed to the window onto them.\n setPositionsDirty() {\n self.positionsVersion++\n },\n zoomToFit() {\n // A host owns x, so a fit while hosted places the rows only.\n if (self.viewportOwner === 'host') {\n const bounds = self.layoutBounds\n const usableHeight = self.canvasHeight - FIT_PADDING * 2\n if (bounds && usableHeight > 0) {\n self.translateY = fittedTranslateY(\n bounds,\n usableHeight,\n self.scaleY,\n )\n }\n return\n }\n // Nothing to fit into before the canvas is measured. The fit autorun\n // runs again once width lands, so skipping beats persisting a negative\n // scale into the session snapshot.\n const fit = self.fittedTransform\n if (fit) {\n self.scale = fit.scale\n self.translateX = fit.translateX\n self.translateY = fit.translateY\n }\n },\n clearPerfMetrics() {\n self.lastFetchMs = undefined\n self.lastLayoutMs = undefined\n self.lastGeometryMs = undefined\n self.lastGeometryStrokeCount = undefined\n self.builtViewport = undefined\n },\n }))\n .actions(self => ({\n // The frame clock. Unclamped, since clampZoom's floor is there to keep a\n // scale finite and 1 / bpPerPx already is; clamped, a whole-chromosome\n // window would stop lining up.\n hostTransform(scale: number, translateX: number) {\n const engaging = self.viewportOwner !== 'host'\n if (engaging) {\n self.viewportOwner = 'host'\n }\n self.scale = scale\n self.translateX = translateX\n if (engaging) {\n self.zoomToFit()\n }\n },\n // A drawing still on the reference stays where the host left it; one\n // whose x no longer means bp is refit.\n releaseHost() {\n if (self.viewportOwner === 'host') {\n if (self.xIsReferenceBp) {\n self.viewportOwner = 'user'\n } else {\n self.viewportOwner = 'fit'\n self.zoomToFit()\n }\n }\n },\n }))\n .actions(self => {\n // Pan/zoom can wait: the geometry on screen is still correct while the\n // gesture runs (the viewport bounds carry 20% overscan), so rebuilding is\n // deferred until the user settles.\n function scheduleViewportDirty() {\n clearTimeout(self.viewportDirtyTimer)\n self.viewportRebuildPending = true\n self.viewportDirtyTimer = setTimeout(() => {\n if (isAlive(self)) {\n self.setViewportDirty()\n }\n }, VIEWPORT_DEBOUNCE_MS)\n }\n\n // A node drag cannot wait \u2014 the node only moves on screen when its\n // geometry is rebuilt \u2014 but a bump costs a full geometry rebuild plus\n // invalidation of both hit-detection indexes, and mousemove fires in\n // bursts well above the frame rate. Coalescing to the next frame keeps the\n // drag live while bounding that work to once per frame instead of once\n // per event.\n function requestPositionsDirtyFrame() {\n cancelAnimationFrame(self.positionsDirtyFrame)\n self.positionsDirtyFrame = requestAnimationFrame(() => {\n if (isAlive(self)) {\n self.setPositionsDirty()\n }\n })\n }\n\n return {\n // Moves the position objects IN PLACE, which is the one thing\n // jbrowse-components' upload invariant says not to do (\"per-region\n // upload values must be freshly constructed, never mutated \u2014 backends\n // diff by reference identity\"), so it is worth saying why this is not\n // that case and what it costs instead.\n //\n // Nothing here diffs by identity: the geometry is rebuilt wholesale and\n // handed over as one batch, so `nodePositions` identity is not a\n // protocol between the model and the backend the way a per-region map\n // is. What identity DOES drive is the fit: the zoom-to-fit autorun and\n // `layoutBounds` \u2014 and so `canvasHeight` \u2014 key off `layoutResult`, and\n // they mean \"a new LAYOUT arrived\", not \"a node moved\". Publishing a\n // fresh positions record per frame of a drag would refit the drawing\n // and resize the pane under the cursor, sixty times a second.\n //\n // So the change is announced by `positionsVersion` instead, and every\n // cache that would otherwise trust identity takes it as a key. The cost\n // of that trade is that the version is a thing to remember: a caller\n // that mutates here and does not bump it gets stale curves, stale hit\n // boxes and stale labels, silently. Everything downstream of it is\n // reached from this one action.\n moveNode(nodeId: string, dx: number, dy: number) {\n const positions = self.layoutResult?.nodePositions\n if (positions?.[nodeId]) {\n for (const seg of positions[nodeId]) {\n seg.x += dx\n seg.y += dy\n }\n requestPositionsDirtyFrame()\n }\n },\n scheduleViewportDirty,\n }\n })\n .actions(self => {\n let loadController: AbortController | undefined\n\n function callEngine(request: EngineRequest) {\n const { rpcManager } = getSession(self)\n return rpcManager.call(getRpcSessionId(self), 'GraphComputeLayout', {\n ...request,\n signal: loadController?.signal,\n // A StatusCallback takes an RpcStatus, not a string \u2014 it may be a\n // bare label, a phase, or a phase that threw. `statusMessageText` is\n // core's reader for the human-facing line.\n statusCallback: status => {\n self.setStatusMessage(statusMessageText(status) ?? '')\n },\n }) as Promise<{ result: LayoutResult; duration: number }>\n }\n\n // The engine's inputs, and only those: the graph, plus what `callLayout`\n // puts in `options`. The reference path is there because the seeds are a\n // function of it; the colour scheme and the anchored modes' own settings\n // are absent because none of them reaches the engine.\n function forceLayoutKey(graph: Graph) {\n return engineKey(graph, {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n bubbleSpread: self.bubbleSpread,\n })\n }\n\n // Single dispatch point for every layout mode. A mode that returns a\n // result computed it locally; one that returns undefined can't draw this\n // graph and hands off to the remote FMMM engine, which is also how\n // 'force' is expressed. See LAYOUT_MODES.\n function* computeLayout(graph: Graph) {\n const start = performance.now()\n const tubeMap = TUBE_MAP_MODES.has(self.chosenLayoutMode)\n // a tube map draws the window; the cut's context is for walk rows\n const region = self.graphRegion\n const drawn = tubeMap && region ? trimToWindow(graph, region) : graph\n // reads are placed by the cut's segment names, which a fold renames\n const coarse =\n tubeMap && self.tubeMapFold > 0 && !drawn.reads\n ? coarsenTubeMap(drawn, self.tubeMapFold)\n : undefined\n const local = layoutModeByValue(self.chosenLayoutMode).run(\n coarse?.graph ?? drawn,\n self.graphRegion,\n self.host ? self.layoutResult?.sampleRows : undefined,\n )\n if (local) {\n const result =\n coarse && local.tubeMap\n ? { ...local, tubeMap: { ...local.tubeMap, coarse } }\n : local\n return { result, duration: performance.now() - start }\n }\n const cache = forceLayoutsOf(graph)\n const key = forceLayoutKey(graph)\n const hit = cache.get(key)\n if (hit) {\n return { result: hit, duration: performance.now() - start }\n }\n const oriented = (yield forceLayout(\n graph,\n {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n bubbleSpread: self.bubbleSpread,\n },\n callEngine,\n )) as { result: LayoutResult; duration: number }\n // Under the key read BEFORE the call: the settings that produced this\n // drawing are not necessarily the ones on screen now, and filing it\n // under the current ones would serve it up as a layout it is not.\n remember(cache, key, oriented.result)\n return oriented\n }\n\n // Which layout request is the live one. A layout is async and nothing in\n // the UI waits for it, so several are routinely in flight: every control\n // in the settings dialog fires `recomputeLayout` on change, and a force\n // layout takes seconds where an anchored one is instant.\n //\n // The graph identity below is not enough to order them, because the\n // competing requests are usually layouts of the SAME graph. Left to\n // resolve order the last one to FINISH won rather than the last one\n // asked for, so switching away from a slow force layout \u2014 to an anchored\n // mode, a cheaper bubble spread, a lower quality \u2014 drew the abandoned\n // one over the top of the chosen one seconds later, with the dropdown\n // still naming the choice that was discarded.\n let liveRequest = 0\n\n // Applied under a guard because a layout is async and the user can load a\n // different graph, or ask for a different layout of it, while one is in\n // flight; the stale result must not land.\n function* layoutInto(graph: Graph) {\n const request = ++liveRequest\n const isLive = () => self.graph === graph && request === liveRequest\n const signal = loadController?.signal\n let computed\n try {\n computed = yield* computeLayout(graph)\n } catch (e) {\n // A discarded layout's failure is not the user's problem: the drawing\n // they asked for is on screen or on its way, and raising a banner\n // over it reports a graph as broken because a setting they moved on\n // from could not be drawn. An aborted one was discarded by the load\n // that replaced it, even when the graph is still the same object.\n if (!isLive() || signal?.aborted) {\n return false\n }\n throw e\n }\n const live = isLive()\n if (live) {\n self.layoutResult = computed.result\n self.setLayoutMs(computed.duration)\n }\n return live\n }\n\n // The walk a path graph's track puts on the region's assembly: the one\n // that names it, else the one chooseReferencePath infers\n function loadedReference(graph: Graph, region: SubgraphRegion) {\n const paths = graph.anchorPaths\n return graph.anchoredBy === 'paths' && paths\n ? (\n assemblyWalk(\n graph,\n self.assemblySpellings(region.assemblyName),\n ) ?? chooseReferencePath(paths, region.assemblyName)\n )?.name\n : undefined\n }\n\n // `keepSelection` for a re-cut of the same source: node ids survive one\n // where edge indexes do not, so the selection is found again by id.\n // `readsOf` fetches the reads over the parsed graph before its one\n // layout, since the tube map lays them out with the paths.\n function* parseAndLayout(\n text: string,\n name: string,\n region: SubgraphRegion | undefined,\n keepSelection = false,\n readsOf?: (graph: Graph) => Promise<GafReads>,\n ) {\n const signal = loadController?.signal\n self.setStatusMessage('Parsing GFA')\n const parsed = loadGraph(text, name, {\n referencePath: self.referencePath || region?.assemblyName,\n maxNodes: self.maxGraphNodes,\n })\n const loaded = region ? loadedReference(parsed, region) : undefined\n const graph =\n !self.referencePath &&\n loaded !== undefined &&\n loaded !== parsed.referencePath\n ? anchorFromPaths(parsed, loaded)\n : parsed\n if (readsOf) {\n self.setStatusMessage('Reading alignments')\n try {\n const { records, total } = (yield readsOf(graph)) as GafReads\n graph.reads = records\n self.readsShown = { shown: records.length, total }\n } catch (e) {\n if (signal?.aborted) {\n return\n }\n console.warn('[GraphGenomeView] no reads for this graph', e)\n self.readsShown = undefined\n }\n if (signal?.aborted) {\n return\n }\n } else {\n self.readsShown = undefined\n }\n const selected = keepSelection ? self.selectedNode : null\n self.graph = graph\n self.graphRegion = region\n self.loadedReferencePath = loaded\n self.indexBubbles = undefined\n self.geneFeatures = undefined\n self.repeatArrays = undefined\n self.popStack = []\n // hoveredEdge is an index into graph.edges and hoveredNode/selectedNode\n // are ids, so all three address the graph being replaced here. Carrying\n // them over points the tooltip and the highlight at whatever now happens\n // to sit at that index.\n self.clearInteractionState()\n if (selected !== null && self.nodeById?.has(selected)) {\n self.selectedNode = selected\n }\n self.setStatusMessage('Computing layout')\n yield* layoutInto(graph)\n }\n\n // Which load is the live one, for the reason liveRequest orders layouts:\n // every change in the settings dialog re-cuts, a remote cut takes seconds,\n // and the last one to finish is not the last one asked for.\n let liveLoad = 0\n\n function beginLoad() {\n const load = ++liveLoad\n loadController?.abort()\n loadController = new AbortController()\n self.loadCanceled = false\n return {\n isLive: () => load === liveLoad,\n signal: loadController.signal,\n }\n }\n\n // The bubble index that the hosted HPRC build keeps beside its segments,\n // `<prefix>.bubbles.bed.gz`, read through the bubble adapter over the same\n // window. A graph whose source has no such file, the ordinary case for a\n // graph of one's own, keeps its derived bubbles, so a failure here is not\n // the graph's problem. A coarse cut reads none: its nodes are the\n // bubbles, and the index over its window can run to a chromosome's rows.\n function* loadBubbles(\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n isLive: () => boolean,\n ) {\n // The track config arrives as written, so the prefix is either the\n // `uri` shorthand or the segments location it expands to.\n const prefix = bubblePrefix(adapterConfig)\n if (adapterConfig.type !== 'RgfaTabixAdapter' || prefix === undefined) {\n return\n }\n try {\n const features = (yield getSession(self).rpcManager.call(\n getRpcSessionId(self),\n 'CoreGetFeatures',\n {\n adapterConfig: {\n type: 'MinigraphBubbleAdapter',\n uri: `${prefix}.bubbles.bed.gz`,\n baseUri: adapterConfig.baseUri,\n assemblyNameToPanSN: adapterConfig.assemblyNameToPanSN,\n },\n regions: [region],\n },\n )) as Feature[]\n if (isLive()) {\n self.indexBubbles = features.map(f => ({\n refName: region.refName,\n start: f.get('start'),\n end: f.get('end'),\n segmentCount: f.get('segmentCount') as number,\n pathCount: (f.get('pathCount') as number | undefined) ?? 0,\n inversion: f.get('inversion') as boolean,\n shortestAlleleLength: f.get('shortestAlleleLength') as number,\n longestAlleleLength: f.get('longestAlleleLength') as number,\n segments: f.get('segments') as string,\n shortestAllele: undefined,\n longestAllele: undefined,\n }))\n }\n } catch (e) {\n console.warn('[GraphGenomeView] no bubble index for this graph', e)\n }\n }\n\n // A session track's features over the cut. Undefined when the session\n // has no such track or the read fails: an annotation is never the\n // graph's problem.\n function* trackFeatures(\n trackId: string | undefined,\n region: SubgraphRegion,\n what: string,\n ) {\n const session = getSession(self)\n const config = trackId\n ? session.tracks.find(t => t.trackId === trackId)\n : undefined\n if (!config) {\n return undefined\n }\n try {\n return (yield session.rpcManager.call(\n getRpcSessionId(self),\n 'CoreGetFeatures',\n {\n adapterConfig: readConfObject(config, 'adapter'),\n regions: [region],\n },\n )) as Feature[]\n } catch (e) {\n console.warn(`[GraphGenomeView] no ${what} for this graph`, e)\n return undefined\n }\n }\n\n // The genes over the cut, from the session's annotation track for the\n // assembly, so the backbone can carry its exons and names.\n function* loadGenes(region: SubgraphRegion, isLive: () => boolean) {\n const features = yield* trackFeatures(\n self.geneTrack?.trackId,\n region,\n 'genes',\n )\n if (features && isLive()) {\n self.geneFeatures = geneModelsFrom(features)\n }\n }\n\n // The tandem repeat arrays over the cut, from the session's repeat\n // track, for the walk rows to measure between and tile by.\n function* loadRepeats(region: SubgraphRegion, isLive: () => boolean) {\n const features = yield* trackFeatures(\n self.repeatTrack?.trackId,\n region,\n 'repeats',\n )\n if (features && isLive()) {\n self.repeatArrays = repeatArraysFrom(features)\n }\n }\n\n // Raises `isLoading` before any fetch, so a view waiting on a remote file\n // shows its loading state instead of the import form. Text already in\n // hand is parsed without yielding first. `region` is the window a\n // declared file was stated beside, which the parse anchors on.\n function* loadWholeGFA(\n name: string,\n source: string | ((signal: AbortSignal) => Promise<string>),\n region?: SubgraphRegion,\n ) {\n const { isLive, signal } = beginLoad()\n self.isLoading = true\n self.error = undefined\n try {\n const text =\n typeof source === 'string'\n ? source\n : ((yield source(signal)) as string)\n if (isLive()) {\n yield* parseAndLayout(text, name, region)\n }\n } catch (e) {\n if (isLive()) {\n console.error('[GraphGenomeView.loadWholeGFA]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.finishLoading()\n }\n }\n return isLive()\n }\n\n function abortLoad() {\n loadController?.abort()\n loadController = undefined\n liveLoad++\n liveRequest++\n }\n\n return {\n // Back to the import form: drop the graph and everything derived from\n // it. Any load in flight ends here too, or it would land its graph\n // afterwards.\n clearGraph() {\n abortLoad()\n self.graph = undefined\n self.graphRegion = undefined\n self.loadedReferencePath = undefined\n self.layoutResult = undefined\n self.indexBubbles = undefined\n self.geneFeatures = undefined\n self.repeatArrays = undefined\n self.popStack = []\n self.error = undefined\n self.isLoading = false\n self.loadCanceled = false\n self.statusMessage = ''\n self.clearInteractionState()\n self.clearPerfMetrics()\n },\n // The user's stop, leaving whatever is drawn under it\n stopLoad() {\n abortLoad()\n self.isLoading = false\n self.statusMessage = ''\n self.loadCanceled = true\n },\n beforeDestroy() {\n abortLoad()\n },\n loadGFA: flow(function* (text: string, name = 'Imported GFA') {\n yield* loadWholeGFA(name, text)\n }),\n loadGFAFromLocation: flow(function* (\n location: FileLocation,\n region?: SubgraphRegion,\n ) {\n self.setStatusMessage('Fetching GFA')\n const live = yield* loadWholeGFA(\n 'uri' in location\n ? (location.uri.split('/').pop() ?? 'GFA')\n : 'GFA',\n signal =>\n openLocation(location).readFile({ encoding: 'utf8', signal }),\n region,\n )\n if (region && live && self.graph) {\n yield* loadRepeats(region, () => self.graphRegion === region)\n }\n }),\n // One cut of a region, laid out with the annotations over it. Whether\n // the region may be cut is the caller's call; overlapping cuts are\n // ordered by liveLoad, so the latest one lands.\n cutSubgraph: flow(function* (\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n opts: SubgraphCutOptions = {},\n ) {\n const { isLive, signal } = beginLoad()\n self.isLoading = true\n self.error = undefined\n self.setStatusMessage('Fetching subgraph')\n try {\n const fetchStart = performance.now()\n const gfaText = (yield getSession(self).rpcManager.call(\n getRpcSessionId(self),\n 'GetSubgraph',\n { adapterConfig, region, opts, signal },\n )) as string\n if (!isLive()) {\n return\n }\n self.setFetchMs(performance.now() - fetchStart)\n if (!gfaText) {\n throw new Error(\n 'Adapter returned no GFA \u2014 region may be outside indexed data or the adapter does not implement getSubgraph',\n )\n }\n yield* parseAndLayout(\n gfaText,\n locLabel(region),\n region,\n true,\n namesReads(adapterConfig)\n ? graph =>\n getSession(self).rpcManager.call(\n getRpcSessionId(self),\n 'GetGraphReads',\n {\n adapterConfig,\n nodeNames: graph.nodes.map(n => n.name),\n signal,\n },\n )\n : undefined,\n )\n if (!isLive()) {\n return\n }\n // Independent remote reads, each landing as it arrives. A coarse\n // cut reads no bubble index: its nodes are the bubbles, and the\n // index over its window can run to a chromosome's rows.\n self.setStatusMessage('Reading annotations')\n yield Promise.all([\n opts.tier === 'coarse'\n ? undefined\n : flow(loadBubbles)(adapterConfig, region, isLive),\n flow(loadGenes)(region, isLive),\n flow(loadRepeats)(region, isLive),\n ])\n } catch (e) {\n if (isLive()) {\n console.error('[GraphGenomeView.cutSubgraph]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.finishLoading()\n }\n }\n }),\n // Re-read one annotation track alone, for a track change. The graph,\n // its layout and any open bubble stay as they are. Live only while the\n // track it read is still the one chosen, so a slow read cannot land\n // over the pick that followed it.\n reloadRepeats: flow(function* () {\n const region = self.graphRegion\n const trackId = self.repeatTrack?.trackId\n if (region) {\n yield* loadRepeats(\n region,\n () =>\n self.graphRegion === region &&\n self.repeatTrack?.trackId === trackId,\n )\n }\n }),\n reloadGenes: flow(function* () {\n const region = self.graphRegion\n const trackId = self.geneTrack?.trackId\n if (region) {\n yield* loadGenes(\n region,\n () =>\n self.graphRegion === region &&\n self.geneTrack?.trackId === trackId,\n )\n }\n }),\n // Open one bubble: the graph becomes the segments the bubble row names,\n // drawn in the layout the reader is in. The graph it came from stays\n // behind it, one click away, and the popped graph gets its own derived\n // bubbles, so a superbubble opens progressively.\n popBubble: flow(function* (bubble: MinigraphBubble) {\n const graph = self.graph\n if (!graph) {\n return\n }\n const sub = bubbleSubgraph(graph, bubbleSegmentIds(bubble))\n if (sub.nodes.length === 0) {\n getSession(self).notify(\n 'None of the segments of this bubble are in the cut; widen the graph context to open it',\n 'info',\n )\n return\n }\n const { isLive } = beginLoad()\n self.popStack = [\n ...self.popStack,\n {\n graph,\n layoutMode: self.chosenLayoutMode,\n label: graph.name,\n indexBubbles: self.indexBubbles,\n },\n ]\n self.indexBubbles = undefined\n const label = `${BUBBLE_KIND_NAMES[classifyBubble(bubble, self.repeatArrays).kind]} at ${bubble.refName}:${bubble.start.toLocaleString()}`\n self.graph = { ...sub, name: label }\n self.clearInteractionState()\n self.viewportOwner = 'fit'\n self.isLoading = true\n try {\n if (yield* layoutInto(self.graph)) {\n self.finishLoading()\n }\n } catch (e) {\n if (isLive()) {\n self.error = e\n self.finishLoading()\n }\n }\n }),\n unpopBubble: flow(function* () {\n const from = self.poppedFrom\n if (!from) {\n return\n }\n self.popStack = self.popStack.slice(0, -1)\n self.graph = from.graph\n self.layoutMode = from.layoutMode\n self.indexBubbles = from.indexBubbles\n self.clearInteractionState()\n self.viewportOwner = 'fit'\n self.isLoading = true\n try {\n if (yield* layoutInto(from.graph)) {\n self.finishLoading()\n }\n } catch (e) {\n self.error = e\n self.finishLoading()\n }\n }),\n recomputeLayout: flow(function* () {\n const graph = self.graph\n if (!graph) {\n return\n }\n self.isLoading = true\n self.setStatusMessage('Computing layout')\n\n try {\n // A superseded request leaves the spinner to the request that\n // replaced it: clearing it here reports \"done\" while the layout the\n // user actually asked for is still being computed, which is the\n // common case when a cheap choice follows an expensive one.\n if (yield* layoutInto(graph)) {\n self.finishLoading()\n }\n } catch (e) {\n console.error('[GraphGenomeView.recomputeLayout]', e)\n self.error = e\n self.finishLoading()\n }\n }),\n }\n })\n .actions(self => ({\n startRenderingBackend(backend: Renderer) {\n if (!self.autorunsInstalled) {\n // Autorun: keep the view fitted to the graph until the user moves it.\n // Reading fittedTransform tracks everything the fit depends on, so\n // it re-fits as the layout arrives, the canvas is measured and the\n // genes over a tube map claim their rows. A manual pan/zoom (or a\n // restored-session transform) makes the viewport the user's, and a\n // host makes it the linear view's.\n addDisposer(\n self,\n autorun(() => {\n if (\n self.fittedTransform &&\n untracked(() => self.viewportOwner === 'fit')\n ) {\n self.zoomToFit()\n }\n }),\n )\n\n // Autorun: mirror a connected linear view's hover onto the graph. An\n // LGV writes `{hoverPosition, hoverFeature}` to session.hovered on\n // every mousemove; neither field names the source view, so the guard\n // is that the position lies in the region this graph was cut from.\n // A pointer over the pane itself is the host's too, at a bp its x\n // only means on a reference-axis layout, so there the pane's own hit\n // test is the hover.\n //\n // Only `hovered` is tracked \u2014 the graph reads are untracked, so a\n // geometry rebuild can't re-fire this and clobber a hover the canvas\n // itself set. Assigning an unchanged id doesn't notify, so a hover\n // that travels within one segment costs nothing downstream.\n addDisposer(\n self,\n autorun(() => {\n const hover = readLgvHover(getSession(self).hovered)\n untracked(() => {\n const region = self.graphRegion\n const graph = self.graph\n if (region && graph && !self.pointerInPane) {\n self.setHoveredNode(\n hover && hoverInRegion(hover, region)\n ? nodeForLgvHover({ hover, nodes: graph.nodes })\n : null,\n )\n }\n })\n }),\n )\n\n // Reaction: drop a hover the pointer cannot still be over.\n //\n // A hover is set by a mousemove on the canvas, and here the drawing\n // moves under a STATIONARY cursor on three axes \u2014 the wheel zoom, the\n // toolbar's zoom and fit buttons, and a pan \u2014 none of which fire a\n // pointer event. Left alone the tooltip goes on naming the node that\n // used to be under the cursor, `hoverHighlight` goes on publishing its\n // span, and the paired linear view paints a band over the wrong\n // sequence until the mouse happens to move.\n //\n // This is jbrowse-components' `installClearHoverOnViewportChange`\n // (BaseLinearDisplay), whose three axes are bpPerPx / offsetPx /\n // scrollTop; a graph view's are its own transform. Its rule is worth\n // restating rather than just citing: a sticky canvas gets no\n // mousemove and no mouseleave for any of them. Not imported because\n // it lives in the LGV plugin and reads a containing LGV, and this\n // plugin deliberately takes no runtime dependency on that plugin\n // (see hoverSync/index.tsx).\n //\n // A `reaction`, not an autorun, for the reason stated there: the\n // effect touches hover state, and an autorun would re-enter itself.\n // Selection is untouched \u2014 a click is a choice, and the content\n // moving does not unmake it.\n addDisposer(\n self,\n reaction(\n () => `${self.scale}-${self.translateX}-${self.translateY}`,\n () => {\n self.setHoveredNode(null)\n self.setHoveredBubble(null)\n self.setHoveredEdge(null)\n },\n { name: 'GraphClearHoverOnViewportChange' },\n ),\n )\n\n // Autorun: a zoom, or a pan that leaves the window the last build\n // covered, schedules a debounced rebuild. A pan inside it costs a\n // repaint and nothing else. Skips the first run.\n let firstViewport = true\n addDisposer(\n self,\n autorun(() => {\n const { scale } = self\n const viewport = paneViewportOf(self)\n if (firstViewport) {\n firstViewport = false\n return\n }\n const built = untracked(() => self.builtViewport)\n if (built?.scale === scale && contains(built.bounds, viewport)) {\n return\n }\n self.scheduleViewportDirty()\n }),\n )\n\n // Autorun: hover/select are draw-time colour overrides, stated whole\n // on every change, so there is nothing to restore and nothing to go\n // stale when a rebuild renumbers the batch. Tracks `geometryVersion`\n // because an upload drops the renderer's edge highlight.\n addDisposer(\n self,\n autorun(() => {\n const b = self.currentRenderingBackend as Renderer | undefined\n const hoveredNode = self.hoveredNode\n const hoveredEdge = self.hoveredEdge\n const selectedNode = self.selectedNode\n dependOn(self.geometryVersion)\n if (b) {\n const nodes = new Map<string, number>()\n if (selectedNode !== null) {\n nodes.set(selectedNode, SELECT_BRIGHTEN)\n }\n if (hoveredNode !== null && hoveredNode !== selectedNode) {\n nodes.set(hoveredNode, HOVER_BRIGHTEN)\n }\n b.setNodeHighlights(nodes)\n b.setEdgeHighlight(hoveredEdge, HOVER_BRIGHTEN)\n self.renderNow()\n }\n }),\n )\n }\n\n // The second argument is a SETUP THUNK, not the callbacks themselves:\n // anything it closes over lives exactly as long as the callbacks that\n // read it, which is what lets a per-backend memo work.\n self.attachRenderingBackend<Renderer>(backend, () => ({\n // Autorun: rebuild geometry when graph data or display options\n // change. scale/translate are untracked so they don't trigger a full\n // rebuild \u2014 only the debounced viewportDirty flag does.\n upload: (b: Renderer) => {\n b.resize(self.paneWidth, self.canvasHeight)\n const nodeById = self.nodeById\n if (self.layoutResult?.tubeMap) {\n dependOn(self.viewportDirty)\n b.uploadGeometry(EMPTY_BATCH)\n self.setGeometryMetrics(0, 0, {\n scale: untracked(() => self.scale),\n bounds: untracked(() => paneViewportOf(self)),\n })\n return true\n }\n if (self.nodePositions && self.graph && nodeById) {\n // The window moved (debounced pan/zoom), or the positions did (a\n // node drag, coalesced to a frame). Both change the drawing;\n // everything below reads them untracked or not at all.\n dependOn(self.viewportDirty, self.positionsVersion)\n const geometryStart = performance.now()\n const viewportBounds = untracked(() =>\n padded(paneViewportOf(self), VIEWPORT_PANES_BUILT),\n )\n const batch = buildGeometry({\n nodePositions: self.nodePositions,\n graph: self.graph,\n nodeById,\n colorScheme: self.effectiveColorScheme,\n contigThickness: self.contigThickness,\n connectorThickness: self.connectorThickness,\n drawPaths: self.effectiveDrawPaths,\n nodeWidth: self.nodeWidth,\n highlight: self.walkLift,\n // Untracked, so a zoom does not eagerly rebuild geometry \u2014 the\n // debounced viewportDirty bump drives the scale-dependent\n // rebuild (flatness, arrow visibility, viewport culling), same\n // as pan.\n axis: untracked(() => self.axisScale),\n linearLayout: self.linearLayout,\n viewportBounds,\n // Where each node sits on the reference and what interval the\n // hue spans. Held against the graph rather than derived here,\n // the same way `deletions` is and for the same reason \u2014 see\n // the `referenceRamp` view.\n referenceRamp: self.referenceRamp,\n deletions: self.deletionEdgeIndexes,\n hiddenEdges: self.hiddenEdgeIndexes,\n // Read tracked above; passed here so the shared edge-curve\n // cache can tell a drag from a pan.\n version: self.positionsVersion,\n })\n b.uploadGeometry(batch)\n self.setGeometryMetrics(\n performance.now() - geometryStart,\n batch.nodeStrokes.length,\n { scale: untracked(() => self.scale), bounds: viewportBounds },\n )\n return true\n }\n // Nothing reached the backend, so the autorun may skip the redraw\n // it would otherwise force. Safe despite the resize above: with no\n // positions there is nothing to draw, which is the same case\n // `render` below returns false for.\n return false\n },\n // Autorun: re-render on pan/zoom/darkMode without rebuilding geometry\n render: (b: Renderer) => {\n if (!self.nodePositions) {\n return false\n }\n // getDpr(), never a bare `devicePixelRatio`: it is capped at\n // MAX_DPR so this canvas costs what every other canvas in the app\n // costs on a 3x display (the square of the ratio, i.e. 9x the\n // pixels of 1x against the 4x everything else pays), and it is the\n // same read `syncCanvasSize` sizes the backing store with \u2014 two\n // call sites reading the global separately can disagree, and then\n // the geometry lands at a different scale from the canvas under it.\n const dpr = getDpr()\n b.updateTransform({\n scaleX: self.scaleX * dpr,\n scaleY: self.scaleY * dpr,\n translateX: self.translateX * dpr,\n translateY: self.translateY * dpr,\n // Handed over rather than read again by the backend: the\n // thicknesses in the vertex buffer are css px and are expanded\n // after this transform, so they need the same ratio the fields\n // above were already multiplied by. See TransformUniform.dpr.\n dpr,\n })\n b.render(self.darkMode ? [0.12, 0.12, 0.12, 1] : [1, 1, 1, 1])\n self.markPainted()\n return true\n },\n }))\n },\n }))\n .actions(self => ({\n // A layout picked from a menu: the mode, then the drawing it makes.\n switchLayout(mode: LayoutModeValue) {\n self.setLayoutMode(mode)\n return self.recomputeLayout()\n },\n cancelLoad() {\n if (self.canCancelLoad) {\n self.stopLoad()\n self.graph = undefined\n }\n },\n // loads the source again, for a host that has one\n retryLoad() {},\n afterAttach() {\n // A restored session that already carries a non-default transform is\n // the user's own view \u2014 mark it so the fit autorun leaves it alone.\n if (!self.isDefaultViewport) {\n self.viewportOwner = 'user'\n }\n },\n }))\n .actions(self => ({\n // Move the linear view already on screen, or open one if there is none,\n // and pair with whichever it was. A view being created gets the session's\n // annotation for the assembly it opens on (see launchTracks), led by the\n // graph's own segments track when the launch is on the reference \u2014 the one\n // assembly that track is configured for.\n //\n // Only a view on the reference is paired with. A node's hover band is on\n // that assembly, so paired with a view on another the band fails that\n // view's assembly check and the reference view's id check, and is drawn\n // nowhere.\n showInLinearView(target: { location: GraphLocation; assembly: string }) {\n const session = getSession(self)\n const onReference = target.assembly === self.graphRegion?.assemblyName\n const viewId = showInLinearView({\n session,\n location: target.location,\n assembly: target.assembly,\n connectedViewId: self.connectedViewId,\n tracks: launchTracks({\n session,\n assemblyName: target.assembly,\n first: onReference ? self.sourceTrackId : undefined,\n }),\n })\n if (onReference) {\n self.pairWithLinearView(viewId)\n }\n },\n // Mark the node's reference interval in the linear view beside the graph.\n // Not an action that opens anything: with no view to mark, the menu does\n // not offer it (see nodeLaunchMenuItems).\n highlightInLinearView(target: {\n location: GraphLocation\n assembly: string\n }) {\n highlightInLinearView({\n session: getSession(self),\n location: target.location,\n assembly: target.assembly,\n connectedViewId: self.connectedViewId,\n })\n },\n showSyntenyView(trackId: string) {\n launchSyntenyView({\n session: getSession(self),\n contributors: self.launchableAssemblies,\n trackId,\n graphTrackId: self.sourceTrackId,\n })\n },\n }))\n .views(self => ({\n // Synteny datasets that could fill the panels of a multi-genome launch.\n // Below two openable contributors there is nothing to compare, so the scan\n // is skipped rather than run and discarded.\n get syntenyLaunchTracks() {\n const samples = self.launchableAssemblies.map(c => c.sample)\n return samples.length >= 2\n ? launchableSyntenyTracks(getSession(self), samples)\n : []\n },\n }))\n .views(self => ({\n // The graph's way out, in the same shared \"Launch view\" submenu every\n // other view contributes to. Until this existed the triangle had two edges:\n // a linear view could open a graph or a synteny view of a locus, and the\n // graph could open nothing at all.\n graphMenuItems(): MenuItem[] {\n const walks = self.walkChoices\n return [\n {\n label: 'Layout',\n subMenu: LAYOUT_MODES.map(mode => ({\n type: 'radio' as const,\n label: mode.label,\n checked: self.chosenLayoutMode === mode.value,\n disabled: self.graph ? !mode.available(self.graph) : false,\n onClick: () => {\n void self.switchLayout(mode.value)\n },\n })),\n },\n {\n label: 'Color',\n subMenu: COLOR_SCHEMES.map(scheme => ({\n type: 'radio' as const,\n label: scheme.label,\n checked: self.chosenColorScheme === scheme.value,\n onClick: () => {\n self.setColorScheme(scheme.value)\n },\n })),\n },\n ...(walks.length > 0\n ? [\n {\n label: 'Walk',\n subMenu: [\n {\n label: 'None',\n onClick: () => {\n self.setWalkLayers([])\n },\n },\n ...(self.walkLayers.length > 1\n ? [\n {\n type: 'checkbox' as const,\n label: 'Side by side',\n checked: self.facet === 'walk',\n onClick: () => {\n self.setFacet(\n self.facet === 'walk' ? 'none' : 'walk',\n )\n },\n },\n ]\n : []),\n ...walks.map(walk => ({\n type: 'checkbox' as const,\n label: walk.label,\n checked: self.walkLayers.some(l => l.walk === walk.name),\n onClick: () => {\n self.toggleWalk(walk.name)\n },\n })),\n ...(self.walkLift?.walks ?? []).map(lifted => ({\n label: `Colour ${walks.find(w => w.name === lifted.name)?.label ?? lifted.name}`,\n subMenu: [\n { type: 'subHeader' as const, label: 'Colour by' },\n ...WALK_FIELDS.map(field => ({\n type: 'radio' as const,\n label: field.label,\n checked: lifted.encoding.field === field.value,\n onClick: () => {\n self.setWalkColor(lifted.name, {\n field: field.value,\n })\n },\n })),\n { type: 'subHeader' as const, label: 'Palette' },\n // the rainbow is the reference-position ramp\n ...WALK_SCHEMES.filter(\n scheme =>\n scheme.value !== 'rainbow' ||\n lifted.encoding.field === 'reference',\n ).map(scheme => ({\n type: 'radio' as const,\n label: scheme.label,\n checked: lifted.encoding.scheme === scheme.value,\n onClick: () => {\n self.setWalkColor(lifted.name, {\n scheme: scheme.value,\n })\n },\n })),\n ],\n })),\n ],\n },\n ]\n : []),\n ...(TUBE_MAP_MODES.has(self.chosenLayoutMode)\n ? [\n {\n label: 'Fold variants',\n subMenu: TUBE_MAP_FOLDS.map(({ bp, label }) => ({\n type: 'radio' as const,\n label,\n checked: self.tubeMapFold === bp,\n disabled: bp > 0 && self.graph?.reads !== undefined,\n onClick: () => {\n self.setTubeMapFold(bp)\n void self.recomputeLayout()\n },\n })),\n },\n ]\n : []),\n ...(self.chosenLayoutMode === 'walkrows' &&\n self.repeatChoices.length > 0\n ? [\n {\n label: 'Repeat',\n subMenu: [\n {\n type: 'radio' as const,\n label: 'Whole window',\n checked: self.repeatKey === '',\n onClick: () => {\n self.setRepeatKey('')\n },\n },\n ...self.repeatChoices.map(({ key, name, unit }) => ({\n type: 'radio' as const,\n label: `${name} \u00B7 ${unit.toLocaleString()} bp unit`,\n checked: self.repeatKey === key,\n onClick: () => {\n self.setRepeatKey(key)\n },\n })),\n ],\n },\n ]\n : []),\n ...(self.hostPlacesX\n ? []\n : [\n {\n label: 'Zoom in',\n onClick: () => {\n self.zoom(1.5, self.paneWidth / 2, self.canvasHeight / 2)\n },\n },\n {\n label: 'Zoom out',\n onClick: () => {\n self.zoom(\n 1 / 1.5,\n self.paneWidth / 2,\n self.canvasHeight / 2,\n )\n },\n },\n {\n label: 'Zoom to fit',\n onClick: () => {\n self.zoomToFit()\n },\n },\n ]),\n {\n type: 'checkbox',\n label: 'Mark bubbles',\n checked: self.showBubbles,\n onClick: () => {\n self.setShowBubbles(!self.showBubbles)\n },\n },\n {\n type: 'checkbox',\n label: 'Show deletion edges',\n checked: self.showDeletionEdges,\n onClick: () => {\n self.setShowDeletionEdges(!self.showDeletionEdges)\n },\n },\n {\n type: 'checkbox',\n label: 'Genes on the backbone',\n checked: self.showGenes,\n onClick: () => {\n self.setShowGenes(!self.showGenes)\n },\n },\n ...(self.referenceStripApplies\n ? [\n {\n type: 'checkbox' as const,\n label: 'Reference strip at bp',\n checked: self.showReferenceStrip,\n onClick: () => {\n self.setShowReferenceStrip(!self.showReferenceStrip)\n },\n },\n ]\n : []),\n ]\n },\n launchMenuItems(): MenuItem[] {\n const items: MenuItem[] = []\n for (const item of graphLaunchMenuItems({\n contributors: self.launchableAssemblies,\n syntenyTracks: self.syntenyLaunchTracks,\n onShowLinear: target => {\n self.showInLinearView(target)\n },\n onShowSynteny: trackId => {\n self.showSyntenyView(trackId)\n },\n })) {\n pushLaunchViewMenuItem(items, item)\n }\n return items\n },\n }))\n}\n\nexport type GraphPaneModel = Instance<ReturnType<typeof GraphPaneMixin>>\n", "import type { GeneModel } from '@jbrowse/bandage-core/genes/genePins'\nimport type { Feature } from '@jbrowse/core/util'\n\n// The adapters whose tracks hold gene models, in the order the session's\n// tracks are searched when no track is named.\nexport const GENE_ADAPTER_TYPES = new Set([\n 'Gff3TabixAdapter',\n 'Gff3Adapter',\n 'GtfTabixAdapter',\n 'GtfAdapter',\n 'BigBedAdapter',\n 'NCListAdapter',\n])\n\nconst GENE_TRACK_HINT = /gene|refseq|gencode|ensembl|annotation/i\n\n// Which of the session's tracks to read genes from: the named one, else the\n// first gene-bearing track on the assembly whose name says it is annotation,\n// else the first gene-bearing track at all.\nexport function pickGeneTrack<\n T extends { trackId: string; name?: string; adapterType: string },\n>(tracks: T[], named: string) {\n if (named) {\n return tracks.find(t => t.trackId === named)\n }\n const candidates = tracks.filter(t => GENE_ADAPTER_TYPES.has(t.adapterType))\n return (\n candidates.find(t =>\n GENE_TRACK_HINT.test(`${t.trackId} ${t.name ?? ''}`),\n ) ?? candidates[0]\n )\n}\n\ntype FeatureLike = Feature | Record<string, unknown>\n\nfunction field(f: FeatureLike, name: string): unknown {\n return typeof (f as Feature).get === 'function'\n ? (f as Feature).get(name)\n : (f as Record<string, unknown>)[name]\n}\n\nfunction exonsOf(f: FeatureLike, into: { start: number; end: number }[]) {\n const type = field(f, 'type')\n if (type === 'exon') {\n into.push({\n start: field(f, 'start') as number,\n end: field(f, 'end') as number,\n })\n }\n const children = field(f, 'subfeatures') as FeatureLike[] | undefined\n for (const child of children ?? []) {\n exonsOf(child, into)\n }\n}\n\nfunction merged(intervals: { start: number; end: number }[]) {\n const sorted = [...intervals].sort((a, b) => a.start - b.start)\n const out: { start: number; end: number }[] = []\n for (const iv of sorted) {\n const last = out.at(-1)\n if (last && iv.start <= last.end) {\n last.end = Math.max(last.end, iv.end)\n } else {\n out.push({ ...iv })\n }\n }\n return out\n}\n\n// Genes from a track's features: every top-level feature, named by the first\n// of gene_name, name and id it carries, with the exons found anywhere under it\n// merged. A feature with no exons is one exon, its whole span.\nexport function geneModelsFrom(features: FeatureLike[]): GeneModel[] {\n const genes: GeneModel[] = []\n for (const f of features) {\n const name =\n (field(f, 'gene_name') as string | undefined) ??\n (field(f, 'name') as string | undefined) ??\n (field(f, 'id') as string | undefined)\n const start = field(f, 'start') as number\n const end = field(f, 'end') as number\n if (!name || !(end > start)) {\n continue\n }\n const exons: { start: number; end: number }[] = []\n exonsOf(f, exons)\n genes.push({\n name,\n refName: field(f, 'refName') as string,\n start,\n end,\n strand: (field(f, 'strand') as number | undefined) ?? 0,\n exons: exons.length ? merged(exons) : [{ start, end }],\n })\n }\n return genes\n}\n", "import { types } from '@jbrowse/mobx-state-tree'\n\n// Persisted choices that read a value outside `values` as unset. A plain\n// `types.enumeration` throws on one, so a session saved by a build that had an\n// option this one dropped failed to load at all; here it opens on the default.\nfunction knownOrUnset<T extends string>(values: readonly T[]) {\n return (value: string | undefined) =>\n values.includes(value as T) ? (value as T) : undefined\n}\n\nexport function lenientMaybeEnum<T extends string>(values: T[]) {\n return types.snapshotProcessor(types.maybe(types.enumeration(values)), {\n preProcessor: knownOrUnset(values),\n })\n}\n\nexport function lenientOptionalEnum<T extends string>(\n values: T[],\n fallback: T,\n) {\n return types.snapshotProcessor(\n types.optional(types.enumeration(values), fallback),\n { preProcessor: knownOrUnset(values) },\n )\n}\n", "import type { Feature } from '@jbrowse/core/util'\n\nexport interface RepeatCall {\n bp: number\n // TRGT's `SD`. Zero means the allele is a copy of the one the reads did\n // support, so its length states nothing about a second haplotype; absent\n // where the genotyper writes no such field.\n spanningReads?: number\n // the PanSN haplotype a phased genotype puts this allele on\n haplotype?: number\n}\n\n// A tandem repeat array as the walk rows need it: its span on the reference,\n// which is what the bars measure between, and its unit length, which is what\n// they tile by. Read off whatever repeat annotation the session has, since the\n// tools all state the same two facts under different names: VCF 4.5's\n// `<CNV:TR>` allele gives `RUL` and `RUS` (one entry per repeat sequence,\n// grouped by `RN`), UCSC simpleRepeat and TRF give `period` and the consensus\n// `sequence`, TRGT gives `MOTIFS` and `TRID`, ExpansionHunter `RU` and `REPID`,\n// HipSTR and GangSTR `PERIOD`, vamos its `motifs`. A VCF record's INFO is\n// searched as well as its top level, and a list's first entry is the unit the\n// bars tile by: for a compound array that is the first repeat sequence, which\n// is the expanding one at every such locus in the common catalogues.\nexport interface RepeatArray {\n key: string\n name: string\n refName: string\n start: number\n end: number\n unit: number\n motif?: string\n // Each sample's genotyped alleles, from a genotyper's per-sample `AL`\n // (TRGT) or a genotype over VCF 4.5 <CNV:TR> alleles; absent from a\n // catalogue.\n calls?: Record<string, RepeatCall[]>\n}\n\nexport const REPEAT_ADAPTER_TYPES = new Set([\n 'BedAdapter',\n 'BedTabixAdapter',\n 'BigBedAdapter',\n 'VcfAdapter',\n 'VcfTabixAdapter',\n])\n\nconst REPEAT_TRACK_HINT =\n /repeat|tandem|\\bstr\\b|vntr|trf|trgt|vamos|expansion|microsat/i\n\nexport function pickRepeatTrack<\n T extends { trackId: string; name?: string; adapterType: string },\n>(tracks: T[], named: string) {\n if (named) {\n return tracks.find(t => t.trackId === named)\n }\n const candidates = tracks.filter(t => REPEAT_ADAPTER_TYPES.has(t.adapterType))\n return candidates.find(t =>\n REPEAT_TRACK_HINT.test(`${t.trackId} ${t.name ?? ''}`),\n )\n}\n\ntype FeatureLike = Feature | Record<string, unknown>\n\nfunction own(f: FeatureLike, name: string): unknown {\n return typeof (f as Feature).get === 'function'\n ? (f as Feature).get(name)\n : (f as Record<string, unknown>)[name]\n}\n\n// A TRGT repeat catalogue is a BED whose name column packs the record:\n// `ID=HTT;MOTIFS=CAG,CCG;STRUC=(CAG)nCAACAG(CCG)n`. Read as fields when it\n// has that shape.\nconst PACKED_NAME = /^\\w+=[^;]*(;\\w+=[^;]*)*$/\n\nfunction packedName(f: FeatureLike) {\n const name = own(f, 'name')\n if (typeof name !== 'string' || !PACKED_NAME.test(name)) {\n return undefined\n }\n return Object.fromEntries(\n name.split(';').map(pair => {\n const i = pair.indexOf('=')\n return [pair.slice(0, i), pair.slice(i + 1)]\n }),\n )\n}\n\n// A field at the feature's top level, in a VCF record's INFO, or in a packed\n// name column, in that order.\nfunction field(f: FeatureLike, name: string): unknown {\n const top = own(f, name)\n if (top !== undefined) {\n return top\n }\n const info = own(f, 'INFO') as Record<string, unknown> | undefined\n return info?.[name] ?? packedName(f)?.[name]\n}\n\nconst MOTIF_FIELDS = [\n 'RUS',\n 'MOTIFS',\n 'RU',\n 'motifs',\n 'motif',\n 'sequence',\n 'consensus',\n]\nconst PERIOD_FIELDS = [\n 'RUL',\n 'period',\n 'PERIOD',\n 'consensusSize',\n 'unit',\n 'unitLength',\n]\nconst NAME_FIELDS = ['TRID', 'REPID', 'VARID', 'ID', 'name', 'id']\n\n// The first entry of a list-valued field, whether it arrived parsed or as\n// the comma-joined text a VCF INFO or a BED column holds; VCF's missing \".\"\n// counts as absent.\nfunction first(f: FeatureLike, names: string[]) {\n for (const name of names) {\n const value = field(f, name)\n const one = String((Array.isArray(value) ? value[0] : value) ?? '')\n .split(',')[0]!\n .trim()\n if (one !== '' && one !== '.') {\n return one\n }\n }\n return undefined\n}\n\nconst IUPAC = /^[ACGTURYSWKMBDHVN]+$/i\n\n// The unit in bp: a stated period, else the length of the first motif.\nexport function repeatUnitOf(f: FeatureLike) {\n const period = Number(first(f, PERIOD_FIELDS))\n if (Number.isFinite(period) && period > 0) {\n return Math.round(period)\n }\n const motif = first(f, MOTIF_FIELDS)\n return motif && IUPAC.test(motif) ? motif.length : undefined\n}\n\n// A per-sample list field, parsed or as the comma-joined text a VCF column\n// holds. A missing entry stays missing: read as 0, an absent `SD` would make\n// every allele of every genotyper that writes no such field unspanned.\nfunction numbers(value: unknown) {\n const raw: unknown[] = Array.isArray(value)\n ? value\n : String(value ?? '').split(',')\n return raw.map(one => {\n const n =\n one === '' || one === null || one === undefined ? NaN : Number(one)\n return Number.isFinite(n) ? n : undefined\n })\n}\n\nfunction strings(value: unknown) {\n const raw: unknown[] = Array.isArray(value)\n ? value\n : String(value ?? '').split(',')\n return raw.map(one => {\n const text = String(one ?? '').trim()\n return text === '' || text === '.' ? undefined : text\n })\n}\n\nconst TANDEM_REPEAT = '<CNV:TR>'\n\n// Each ALT allele's length, off VCF 4.5's flattened list-of-lists: RN says how\n// many RUS/RUL/RUC/RB entries each allele takes, and the allele's bases are\n// its runs' RB, or unit length times count where RB is missing. An allele that\n// is not <CNV:TR>, or has a run stating neither, has none.\nfunction tandemAlleleBp(f: FeatureLike) {\n const alts = strings(own(f, 'ALT'))\n if (!alts.includes(TANDEM_REPEAT)) {\n return undefined\n }\n const rn = numbers(field(f, 'RN'))\n const rus = strings(field(f, 'RUS'))\n const rul = numbers(field(f, 'RUL'))\n const ruc = numbers(field(f, 'RUC'))\n const rb = numbers(field(f, 'RB'))\n let k = 0\n return alts.map((alt, i) => {\n const n = rn[i] ?? (alt === TANDEM_REPEAT ? 1 : 0)\n let bp = 0\n let known = 0\n for (let j = 0; j < n; j++, k++) {\n const motif = rus[k]\n const unitLength =\n rul[k] ?? (motif && IUPAC.test(motif) ? motif.length : undefined)\n const count = ruc[k]\n const runBp =\n rb[k] ??\n (unitLength && count !== undefined\n ? Math.round(unitLength * count)\n : undefined)\n if (runBp !== undefined) {\n bp += runBp\n known++\n }\n }\n return alt === TANDEM_REPEAT && n > 0 && known === n ? bp : undefined\n })\n}\n\n// A sample's alleles through its genotype: 0 is the reference's, an index\n// past it the ALT allele's. A phased genotype's k-th allele is PanSN\n// haplotype k, the order vg deconstruct writes an assembly's haplotypes in.\nfunction genotyped(\n gt: string | undefined,\n alleleBp: (number | undefined)[],\n referenceBp: number,\n) {\n const phased = gt?.includes('|')\n return (gt ?? '').split(/[/|]/).flatMap((index, k): RepeatCall[] => {\n const i = Number(index)\n const bp = i > 0 ? alleleBp[i - 1] : referenceBp\n if (!Number.isInteger(i) || bp === undefined) {\n return []\n }\n return [{ bp, ...(phased ? { haplotype: k + 1 } : {}) }]\n })\n}\n\nfunction callsOf(\n f: FeatureLike,\n alleleBp: (number | undefined)[] | undefined,\n referenceBp: number,\n) {\n const samples = own(f, 'samples') as\n Record<string, Record<string, unknown>> | undefined\n const called: Record<string, RepeatCall[]> = {}\n for (const [sample, fields] of Object.entries(samples ?? {})) {\n const reads = numbers(fields.SD)\n const lengths = numbers(fields.AL).flatMap((bp, i) =>\n bp !== undefined && bp > 0\n ? [reads[i] === undefined ? { bp } : { bp, spanningReads: reads[i] }]\n : [],\n )\n const calls =\n lengths.length > 0 || !alleleBp\n ? lengths\n : genotyped(strings(fields.GT)[0], alleleBp, referenceBp)\n if (calls.length > 0) {\n called[sample] = calls\n }\n }\n return Object.keys(called).length > 0 ? called : undefined\n}\n\n// A <CNV:TR> record's POS is the base before the array and SVLEN the\n// reference allele's length, while a VCF feature starts at that padding base.\nfunction spanOf(f: FeatureLike, tandem: boolean) {\n const start = field(f, 'start') as number\n const end = field(f, 'end') as number\n const svlen = numbers(field(f, 'SVLEN'))[0]\n return tandem && svlen !== undefined\n ? { start: start + 1, end: start + 1 + svlen }\n : { start, end }\n}\n\nexport function repeatArraysFrom(features: FeatureLike[]): RepeatArray[] {\n const arrays: RepeatArray[] = []\n for (const f of features) {\n const alleleBp = tandemAlleleBp(f)\n const { start, end } = spanOf(f, alleleBp !== undefined)\n const unit = repeatUnitOf(f)\n if (!(end > start) || unit === undefined) {\n continue\n }\n const refName = field(f, 'refName') as string\n const motif = first(f, MOTIF_FIELDS)\n const stated = first(f, NAME_FIELDS)\n arrays.push({\n key: `${refName}:${start}-${end}`,\n name:\n (stated && !PACKED_NAME.test(stated) ? stated : undefined) ??\n (motif && motif.length <= 12\n ? `(${motif})n`\n : `${refName}:${(start + 1).toLocaleString()}-${end.toLocaleString()}`),\n refName,\n start,\n end,\n unit,\n motif,\n calls: callsOf(f, alleleBp, end - start),\n })\n }\n return arrays.sort((a, b) => a.start - b.start)\n}\n", "import { isBackbone } from '@jbrowse/bandage-core/anchoredNodes'\n\nimport type { GraphNode } from '@jbrowse/bandage-core/types'\n\n// What a LinearGenomeView writes to `session.hovered` on every mousemove (see\n// LinearGenomeViewContainer): the bp under the cursor, plus the feature under it\n// if the topmost track had one. Nothing names the source VIEW, so a graph view\n// filters by whether the position falls in the region it was cut from \u2014 but the\n// position does name the source ASSEMBLY, and that turns out to be the half\n// that matters.\nexport interface LgvHover {\n refName: string\n // The 0-based base under the pointer, the convention `stable.start` and the\n // cut region are in. Not the LGV's own `coord`, which is 1-based for display.\n coord: number\n // The assembly the hovered view is showing. `hoverPosition` is a\n // `PxToBpResult`, i.e. the displayed region the pointer landed in spread flat,\n // so this is that region's own `assemblyName`.\n //\n // Read because refName alone does not identify a locus in a session holding\n // several assemblies, which is every session these graphs are launched in. A\n // PanSN name reduces to a bare contig \u2014 the E. coli pangenome loads five\n // strains each with one refName `chr`, and HPRC's hg38 and a GenArk haplotype\n // both call it `chr6` \u2014 so a synteny stack's rows all answer to the same\n // name at unrelated coordinates. Without this the hover on ANY row lit up a\n // node in the graph, and the node then published its own reference interval\n // back into every linear view.\n //\n // Optional because the channel is typed `unknown` and read structurally: a\n // hover that states no assembly is taken at its word rather than dropped.\n assemblyName?: string\n featureName?: string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\ninterface FeatureLike {\n get: (key: string) => unknown\n}\n\nfunction isFeatureLike(value: unknown): value is FeatureLike {\n return isRecord(value) && typeof value.get === 'function'\n}\n\n// A display hovers either a Feature, read with `get`, or a plain hit item with\n// a `name` of its own, which is what the canvas feature display hands over.\n// Reading only the first left the exact segment match dead on that display.\nfunction hoveredName(feature: unknown) {\n const name = isFeatureLike(feature)\n ? feature.get('name')\n : isRecord(feature)\n ? feature.name\n : undefined\n return typeof name === 'string' ? name : undefined\n}\n\n// `hovered` is typed `unknown` on the session by design \u2014 it is a shared channel\n// every view writes its own shape to \u2014 so this reads the LGV's shape out of it\n// structurally rather than asserting a type onto it.\nexport function readLgvHover(hovered: unknown): LgvHover | undefined {\n let result: LgvHover | undefined\n if (isRecord(hovered) && isRecord(hovered.hoverPosition)) {\n const { refName, coord, coord0, assemblyName } = hovered.hoverPosition\n if (typeof refName === 'string' && typeof coord === 'number') {\n result = {\n refName,\n // `coord` is 1-based; a host that states the 0-based `coord0` is\n // taken at its word. Read as 0-based, the last base of every node lit\n // the next one, and a 1 bp node could not be reached at all.\n coord: typeof coord0 === 'number' ? coord0 : coord - 1,\n assemblyName:\n typeof assemblyName === 'string' ? assemblyName : undefined,\n featureName: hoveredName(hovered.hoverFeature),\n }\n }\n }\n return result\n}\n\n// Whether a hover is one this graph can answer: the same assembly, the same\n// stable sequence, and inside the window the cut was made for.\n//\n// The assembly is checked FIRST and is the whole reason this is not just a\n// coordinate test. See LgvHover.assemblyName: a graph cut from K12 and a\n// synteny row on Sakai both call their sequence `chr`, so on refName and\n// coordinate alone every row of the stack matched and the graph highlighted a\n// K12 node at Sakai's offset. A hover that names no assembly still passes,\n// which is what a hand-written channel writer and the older LGVs get.\nexport function hoverInRegion(\n hover: LgvHover,\n region: { refName: string; assemblyName: string; start: number; end: number },\n) {\n return (\n (hover.assemblyName === undefined ||\n hover.assemblyName === region.assemblyName) &&\n hover.refName === region.refName &&\n hover.coord >= region.start &&\n hover.coord < region.end\n )\n}\n\n// The graph node a linear-view hover points at.\n//\n// The feature under the cursor is the exact answer when the graph's own segments\n// track is what's hovered: an RgfaTabixAdapter feature's name is the segment id,\n// which is `GraphNode.name` (`GraphNode.id` carries a strand suffix, so the\n// match has to be on name). Any other track \u2014 genes, bubbles \u2014 supplies only a\n// coordinate, which still identifies the backbone segment covering it: the\n// backbone tiles the reference exactly once, whether rGFA's rank 0 declared it\n// or a reference path's own steps did.\nexport function nodeForLgvHover({\n hover,\n nodes,\n}: {\n hover: LgvHover\n nodes: GraphNode[]\n}) {\n let byName: string | undefined\n let byCoord: string | undefined\n for (const node of nodes) {\n if (node.name === hover.featureName) {\n byName = node.id\n } else if (\n byCoord === undefined &&\n isBackbone(node) &&\n hover.coord >= node.stable.start &&\n hover.coord < node.stable.start + node.length\n ) {\n byCoord = node.id\n }\n }\n // An exact segment match wins over the coordinate it happens to sit at.\n return byName ?? byCoord ?? null\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\n\nimport { locLabel, locString } from './contributors'\nimport { linearViewTarget } from './linearViewTarget'\n\nimport type { Contributor, GraphLocation } from './contributors'\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nexport interface TrackScanSession {\n tracks: AnyConfigurationModel[]\n assemblies: AnyConfigurationModel[]\n}\n\nexport interface GraphLaunchSession extends TrackScanSession {\n addView: (type: string, snapshot?: Record<string, unknown>) => { id: string }\n // The session's own loader for a view whose state model is registered\n // lazily (JBrowse 4's LinearSyntenyView); absent on an older core, where\n // addView is enough\n launchView?: (\n type: string,\n snapshot?: Record<string, unknown>,\n ) => Promise<{ id: string }>\n assemblyNames: string[]\n views: unknown[]\n // optional, as a hand-built session in a test has none\n notifyError?: (message: string, error?: unknown) => void\n}\n\n// A launch is a promise nothing waits on. Let go, a refusal moved nothing and\n// said nothing; this is where it is told to the user instead.\nfunction reportFailure(\n session: GraphLaunchSession,\n what: string,\n launch: Promise<unknown>,\n) {\n launch.catch((error: unknown) => {\n console.error(`[GraphGenomeView] ${what}`, error)\n session.notifyError?.(`${what}: ${error}`, error)\n })\n}\n\n// A node is often shorter than a line of text \u2014 a base-level allele can be a\n// handful of bp \u2014 and a linear view opened on exactly that span shows the span\n// and no context at all. Padding to a minimum window puts the node in the middle\n// of something readable.\nconst MIN_LAUNCH_SPAN_BP = 1000\n\nexport function paddedLocation(\n loc: GraphLocation,\n minSpan = MIN_LAUNCH_SPAN_BP,\n): GraphLocation {\n const pad = Math.max(0, Math.ceil((minSpan - (loc.end - loc.start)) / 2))\n return { ...loc, start: Math.max(0, loc.start - pad), end: loc.end + pad }\n}\n\n// Restate the rank-0 contributor in the terms of the region the graph was cut\n// from. Two separate corrections, both required:\n//\n// - the *assembly*. A backbone segment's SN is the graph's own spelling of the\n// reference, which need not be the name of any loaded assembly: the HPRC\n// graph says `GRCh38#0#chr6` in a session whose assembly is `hg38`. Taking\n// the PanSN sample there resolves to nothing and the graph offers no way out\n// at all. The cut region is authoritative \u2014 the adapter resolved that\n// assembly to that stable sequence to cut the subgraph in the first place.\n// The backbone's `haplotype` (`GRCh38#0`) goes with it, since\n// resolveLocationAssembly tries that before the sample.\n// - the *locus*. The cut region is what the user framed in the linear view, and\n// it is the span every other panel of a synteny launch is compared against;\n// the union of backbone segments differs from it whenever the window ends\n// inside a bubble.\n//\n// A subgraph is cut on one reference, so there is at most one rank-0\n// contributor. Must run before contributors are resolved against the session's\n// assemblies, or the reference is filtered out by the name this replaces.\nexport function withReferenceRegion(\n contributors: Contributor[],\n region:\n | { refName: string; assemblyName: string; start: number; end: number }\n | undefined,\n): Contributor[] {\n return region\n ? contributors.map(c =>\n c.rank === 0\n ? {\n ...c,\n sample: region.assemblyName,\n haplotype: undefined,\n refName: region.refName,\n start: region.start,\n end: region.end,\n }\n : c,\n )\n : contributors\n}\n\n// Show a location in a linear view: move the one already on screen if there is\n// one to move, open a pane only if there isn't.\n//\n// This is the difference between \"flip to the linear view\" and \"accumulate\n// panes\". The pangenome sessions this ships in are a linear view above a graph,\n// and there the answer to \"where is this node?\" should be that the linear view\n// scrolls to it \u2014 see linearViewTarget for which view counts as the one to move.\n//\n// `tracks` is only applied to a view being created; a view already open keeps\n// whatever the user has turned on. Returns the id of the view that ended up\n// showing the location, so the graph can pair with it for the hover sync.\nexport function showInLinearView({\n session,\n location,\n assembly,\n connectedViewId,\n tracks = [],\n}: {\n session: GraphLaunchSession\n location: GraphLocation\n assembly: string\n connectedViewId?: string\n tracks?: string[]\n}) {\n const existing = linearViewTarget({\n views: session.views,\n connectedViewId,\n assemblyName: assembly,\n })\n let viewId: string\n if (existing) {\n reportFailure(\n session,\n `Could not show ${assembly} ${locLabel(location)}`,\n Promise.resolve(existing.navToLocString(locString(location), assembly)),\n )\n viewId = existing.id\n } else {\n viewId = session.addView('LinearGenomeView', {\n displayName: `${assembly} \u2014 ${locLabel(location)}`,\n init: { assembly, loc: locString(location), tracks },\n }).id\n }\n return viewId\n}\n\n// Mark where a node sits on the reference, in the linear view already on\n// screen, instead of scrolling that view to it. Review: \"need to be able to\n// just highlight lineargenomeview coords\".\n//\n// The other half of the pair above, and the half that survives being let go of:\n// hover sync draws a band for as long as the pointer is over the node, where a\n// highlight is written into the view's own list and stays until it is removed.\n// It also keeps the reader's frame \u2014 the interesting comparison at a pangenome\n// locus is usually the whole window, not the 200 bp the node occupies.\n//\n// Nothing to mark when no linear view on that assembly is ours to move (see\n// linearViewTarget); the caller offers the item only when there is.\nexport function highlightInLinearView({\n session,\n location,\n assembly,\n connectedViewId,\n}: {\n session: GraphLaunchSession\n location: GraphLocation\n assembly: string\n connectedViewId?: string\n}) {\n const target = linearViewTarget({\n views: session.views,\n connectedViewId,\n assemblyName: assembly,\n })\n target?.addToHighlights?.({\n refName: location.refName,\n start: location.start,\n end: location.end,\n assemblyName: assembly,\n })\n return target !== undefined\n}\n\n// Which assemblies a track config says it covers, for deciding whether a panel\n// can draw it at all. A trackId naming nothing in the session reads as covering\n// nothing, so a stale id drops the lane rather than adding one the panel would\n// fail to load.\nfunction assemblyNamesOfTrack(\n session: GraphLaunchSession,\n trackId: string | undefined,\n) {\n if (trackId === undefined) {\n return []\n }\n const track = session.tracks.find(\n t => readConfObject(t, 'trackId') === trackId,\n )\n const names: unknown = track ? readConfObject(track, 'assemblyNames') : []\n return Array.isArray(names) ? names.filter(n => typeof n === 'string') : []\n}\n\n// One panel per contributing assembly, each framed on the locus that assembly\n// contributes here, with the graph's own reference panel on top.\n//\n// The panel loci come from the graph rather than from the synteny track: rGFA\n// states where each contributor's sequence sits on its own coordinates, so the\n// launch needs no mate discovery, no PAF lookup and no dialog. That is the whole\n// point of launching from the graph instead of from a linear view.\n//\n// Panels carry THE GRAPH'S OWN TRACK and nothing else, which is a narrower rule\n// than the single-view launch's (see launchTracks, which scans the session for\n// the assembly's annotation). Tried that here: a gene track per panel takes\n// about 160px of a row that is otherwise a ruler, so on five strains the\n// annotation is most of the viewport and the ribbons \u2014 the thing the launch\n// exists to draw \u2014 are squeezed into the gaps, and `collapseEmptyRows` is here\n// to prevent exactly that.\n//\n// The graph's own track does not have that shape. It goes only in the panels\n// whose assembly its config declares, which is where the graph HAS coordinates:\n// the HPRC segments track names two, so both its panels get one lane, while the\n// E. coli one names K12 alone and a five-strain launch adds a single lane rather\n// than five. And it is the lane that makes the band readable, since the ribbons\n// otherwise connect two bare rulers and the reader has nothing to match across\n// them.\nexport function launchSyntenyView({\n session,\n contributors,\n trackId,\n graphTrackId,\n}: {\n session: GraphLaunchSession\n contributors: Contributor[]\n trackId?: string\n graphTrackId?: string\n}) {\n const graphTrackAssemblies = assemblyNamesOfTrack(session, graphTrackId)\n // The panels the chosen track aligns, in the order given. A pairwise track is\n // offered beside an all-vs-all, two assemblies being a synteny view, and over\n // every contributor it sat on each level and could draw on one at most. A\n // track that states no assemblies keeps them all.\n const aligned = assemblyNamesOfTrack(session, trackId)\n const covered = contributors.filter(c => aligned.includes(c.sample))\n const panels = covered.length >= 2 ? covered : contributors\n const snapshot = {\n init: {\n views: panels.map(c => ({\n assembly: c.sample,\n loc: locString(c),\n ...(graphTrackId !== undefined &&\n graphTrackAssemblies.includes(c.sample)\n ? { tracks: [graphTrackId] }\n : {}),\n })),\n // One entry per LEVEL, not one for the track. `init.tracks` is 2D \u2014 the\n // gap between views[i] and views[i+1] \u2014 and a flat `[trackId]` is read as\n // the level-0 shorthand, so on a five-strain launch only the top band got\n // the alignment and the four below it opened as bare rulers. The same\n // all-vs-all track fills every level: it carries all the pairs.\n tracks: trackId ? panels.slice(1).map(() => [trackId]) : [],\n collapseEmptyRows: true,\n },\n }\n // launchView where the session has it: a synteny view's state model is\n // loaded on demand and addView throws until it is\n if (session.launchView) {\n reportFailure(\n session,\n 'Could not open the synteny view',\n session.launchView('LinearSyntenyView', snapshot),\n )\n } else {\n session.addView('LinearSyntenyView', snapshot)\n }\n}\n", "export interface NavigableLinearView {\n id: string\n assemblyNames: string[]\n navToLocString: (locString: string, assemblyName?: string) => unknown\n // The other way of answering \"where is this node\": mark it rather than scroll\n // to it. Optional, and not part of isLinearView's test \u2014 navigating is what\n // makes a view a target here, and a view that cannot be marked can still be\n // moved.\n addToHighlights?: (highlight: {\n refName: string\n start: number\n end: number\n assemblyName: string\n }) => unknown\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n// Reads `session.views` structurally, the same way graphViewHighlights does: the\n// members needed here are declared by LinearGenomeView, not by the\n// AbstractViewModel the session array is typed as.\nfunction isLinearView(view: unknown): view is NavigableLinearView {\n return (\n isRecord(view) &&\n view.type === 'LinearGenomeView' &&\n typeof view.id === 'string' &&\n typeof view.navToLocString === 'function' &&\n Array.isArray(view.assemblyNames)\n )\n}\n\n// The view a graph was launched from is not always in `session.views`: a row of\n// a LinearSyntenyView, LinearComparativeView or BreakpointSplitView sits in\n// that view's own `views[]`, so the candidates are the session's views plus one\n// level of rows.\nexport function withRows(views: unknown[]) {\n return views.flatMap(view =>\n isRecord(view) && Array.isArray(view.views)\n ? [view, ...view.views]\n : [view],\n )\n}\n\n// The linear view a \"show me this\" from the graph should move, rather than\n// opening a pane beside it. Three cases, in order:\n//\n// - the view this graph is paired with (`connectedViewId`), which is either\n// the view the graph was launched from or the one it later opened itself.\n// Explicit, so it wins.\n// - failing that, the only linear view in the session showing this assembly.\n// The pangenome sessions this view ships in are a linear view above a graph\n// with no pairing recorded, and there \"the linear view\" is unambiguous \u2014 the\n// same reasoning hoverSync's isConnected already applies to highlights.\n// - two or more candidates and no pairing: nothing. Guessing which of the\n// user's views to scroll is worse than opening one that is ours to move.\n//\n// Assembly-gated throughout: a linear view on a different assembly cannot show\n// this location at all.\nexport function linearViewTarget({\n views,\n connectedViewId,\n assemblyName,\n}: {\n views: unknown[]\n connectedViewId: string | undefined\n assemblyName: string\n}) {\n const candidates = withRows(views)\n .filter(view => isLinearView(view))\n .filter(view => view.assemblyNames.includes(assemblyName))\n const connected = candidates.find(view => view.id === connectedViewId)\n return connected ?? (candidates.length === 1 ? candidates[0] : undefined)\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\n\nimport type { TrackScanSession } from './launchFromGraph'\n\nconst ANNOTATION_TRACK_TYPE = 'FeatureTrack'\n\n// What to turn on in a linear view the graph opens on `assemblyName`.\n//\n// A launch used to carry the graph's own track and nothing else, and that track\n// is configured for the reference alone \u2014 so `Launch view` \u2192 `CFT073 chr:\u2026`\n// landed on `No tracks active`, which answers \"where is this strain's sequence?\"\n// with an empty pane. What the session does hold for a contributing assembly is\n// that assembly's own annotation, and on a pangenome config that is one track\n// per strain (`CFT073_genes`), which is exactly what makes the landing readable.\n//\n// Annotation tracks only, not everything configured for the assembly. A launch\n// is a jump to a locus, and an alignments or coverage track turned on behind the\n// user's back fetches data nobody asked for; a FeatureTrack at a 50 kb window is\n// a tabix range query.\n//\n// `first` is the graph's own track when the launch is on the reference, so the\n// segments the graph drew sit at the top of the view rather than wherever config\n// order puts them.\nexport function launchTracks({\n session,\n assemblyName,\n first,\n}: {\n session: TrackScanSession\n assemblyName: string\n first?: string\n}) {\n const found: string[] = []\n for (const track of session.tracks) {\n const type: unknown = readConfObject(track, 'type')\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n type === ANNOTATION_TRACK_TYPE &&\n Array.isArray(assemblyNames) &&\n assemblyNames.includes(assemblyName) &&\n typeof trackId === 'string' &&\n trackId !== first\n ) {\n found.push(trackId)\n }\n }\n return first ? [first, ...found] : found\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { getTrackName } from '@jbrowse/core/util/tracks'\nimport { getEnv, isStateTreeNode } from '@jbrowse/mobx-state-tree'\n\nimport type { TrackScanSession } from './launchFromGraph'\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nexport interface LaunchableSyntenyTrack {\n trackId: string\n name: string\n // how many of the requested assemblies this track covers, so a whole-set\n // all-vs-all sorts above a pairwise alignment covering two of them\n coverage: number\n}\n\nconst SYNTENY_TRACK_TYPE = 'SyntenyTrack'\nconst SYNTENY_ADAPTER_CATEGORY = 'Synteny adapters'\n\n// A SyntenyTrack, or any track reading a synteny adapter (a GraphTrack over a\n// gbz-base database). Core's isSyntenyTrack makes the same test on newer hosts.\nfunction isSyntenyTrack(track: AnyConfigurationModel) {\n if (readConfObject(track, 'type') === SYNTENY_TRACK_TYPE) {\n return true\n }\n const adapterType: unknown = track.adapter?.type\n if (typeof adapterType !== 'string' || !isStateTreeNode(track)) {\n return false\n }\n const { pluginManager } = getEnv<{ pluginManager?: PluginManager }>(track)\n return (\n !!pluginManager?.hasAdapterType(adapterType) &&\n pluginManager.getAdapterType(adapterType).adapterMetadata?.category ===\n SYNTENY_ADAPTER_CATEGORY\n )\n}\n\n// Synteny datasets in the session that align at least two of `assemblyNames`.\n//\n// Two is the floor because that is the floor for a synteny view; more is better,\n// and an all-vs-all covering every contributing assembly is what makes the\n// multi-panel launch worth offering at all.\nexport function launchableSyntenyTracks(\n session: TrackScanSession,\n assemblyNames: string[],\n) {\n const wanted = new Set(assemblyNames)\n const found: LaunchableSyntenyTrack[] = []\n for (const track of session.tracks) {\n const trackAssemblies: unknown = readConfObject(track, 'assemblyNames')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n isSyntenyTrack(track) &&\n typeof trackId === 'string' &&\n Array.isArray(trackAssemblies)\n ) {\n const coverage = trackAssemblies.filter(name => wanted.has(name)).length\n if (coverage >= 2) {\n found.push({ trackId, name: getTrackName(track, session), coverage })\n }\n }\n }\n return found.sort((a, b) => b.coverage - a.coverage)\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6\"\n}), 'Settings');"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["require_dataAdapterCache", "__commonJSMin", "exports", "module", "require_pluggableElementTypes", "__commonJSMin", "exports", "module", "require_SvgIcon", "__commonJSMin", "exports", "module", "require_configuration", "__commonJSMin", "exports", "module", "require_colorBits", "__commonJSMin", "exports", "module", "require_mobx", "__commonJSMin", "exports", "module", "require_canvas2dUtils", "__commonJSMin", "exports", "module", "require_RenderLifecycleMixin", "__commonJSMin", "exports", "module", "import_dataAdapterCache", "import_pluggableElementTypes", "isReadsAdapter", "adapter", "isNamed", "location", "key", "value", "namesReads", "adapterConfig", "GetGraphReads", "args", "nodeNames", "sessionId", "signal", "dataAdapter", "import_SvgIcon", "svgPath", "points", "round", "v", "p", "i", "projectLine", "x1", "y1", "x2", "y2", "distance", "d", "vx", "vy", "curvePointAt", "c", "u", "a", "b", "e", "curveMidpoint", "curves", "mid", "curveBounds", "minX", "minY", "maxX", "maxY", "subCurve", "t0", "t1", "p0", "p1", "dt", "d0", "curveTangentAt", "d1", "dashCurves", "dashWorld", "out", "polyLen", "spans", "translateCurves", "dx", "dy", "attachment", "segments", "side", "last", "TANGENT_SPAN_FACTOR", "MAX_TANGENT", "spanOf", "min", "max", "s", "distanceToSpan", "x", "span", "facingSide", "other", "tieBreak", "startGap", "endGap", "facingSides", "fromSegments", "toSegments", "APEX_FRACTION", "BOW_CLEARANCE", "ALONG_FRACTION", "MAX_ROW_BOW_PX", "bowAround", "p1x", "p1y", "p2x", "p2y", "toward", "maxBow", "chord", "ux", "uy", "reach", "minAlong", "maxAlong", "perp", "along", "uncapped", "size", "yToXOf", "scaleX", "scaleY", "PATH_RIBBON_SPACING", "drawnDirection", "yToX", "len", "pathRibbonOffsets", "count", "axis", "spacing", "dir", "offsets", "scaleYOf", "unscaleYOf", "SELF_LOOP_MIN_PX", "SELF_LOOP_MAX_PX", "selfLoopReach", "scale", "length", "nodePx", "selfLinkOf", "edge", "hairpinCurves", "attach", "offsetX", "offsetY", "at", "inward", "px", "py", "nearX", "nearY", "apexX", "apexY", "sideX", "sideY", "computeEdgeCurves", "selfLink", "bypassed", "maxBowIn", "sides", "fromAttach", "toAttach", "fromEnd", "toStart", "segDirX", "segDirY", "ext", "perpX", "perpY", "midX", "midY", "cp1x", "cp1y", "cp2x", "cp2y", "fromPrev", "toNext", "dist", "tangentProj", "prev", "towardX", "towardY", "towardToX", "towardToY", "cx1", "cy1", "cx2", "cy2", "bulge", "chordLen", "bulgeX", "bulgeY", "spreadX", "spreadY", "MIN_DELETION_BP", "bypassedPoints", "nodePositions", "bypassed", "id", "deletionArcCurves", "deletion", "axis", "from", "to", "computeEdgeCurves", "backboneBySequence", "graph", "bySequence", "node", "isBackbone", "list", "a", "b", "bypassedNodes", "nodes", "start", "end", "i", "firstNodeAtOrAfter", "skipsBetween", "edge", "forwards", "fromStrand", "toStrand", "leavesRight", "arrivesLeft", "deletionEdges", "byId", "n", "backbone", "found", "edgeIndex", "left", "right", "bp", "refName", "ownStrand", "id", "flip", "strand", "sidesOf", "edge", "from", "to", "mergeRuns", "graph", "sides", "at", "attach", "side", "ei", "key", "list", "joinAfter", "a", "out", "joined", "runOf", "runs", "node", "into", "pred", "run", "next", "byId", "n", "nodes", "members", "backbone", "isBackbone", "first", "b", "sum", "seen", "edges", "splitRuns", "positions", "weightOf", "line", "arc", "arcPrefix", "total", "weights", "m", "weightSum", "member", "k", "share", "slice", "polylineSlice", "polylineMidpoint", "pointAt", "i", "s", "span", "t", "points", "labelWidth", "text", "overlaps", "a", "b", "offFrame", "box", "frame", "insideFrame", "occupancy", "reserved", "placed", "p", "placeLabels", "candidates", "take", "out", "c", "w", "direction", "tries", "y", "MIN_DELETION_LABEL_PX", "PX_PER_NODE_LABEL", "MIN_NODE_LABELS", "LEADER_GAP_PX", "LABEL_CHAR_PX", "LABEL_PAD_PX", "LABEL_HEIGHT_PX", "LABEL_GAP_PX", "MAX_LABEL_OVERHANG", "labelHalfWidth", "text", "LABEL_HALF_HEIGHT", "MIN_LABEL_HALF_WIDTH", "boxAt", "halfW", "x", "y", "rowLabelBox", "screenY", "clamp", "v", "lo", "hi", "formatBp", "bp", "deletionText", "arcOutward", "curves", "apex", "scaleX", "scaleY", "first", "last", "dx", "dy", "len", "boxSupport", "box", "dir", "boxRayCrossing", "halfH", "drawnLength", "segments", "total", "i", "a", "b", "runBounds", "nodeIds", "nodePositions", "minX", "minY", "maxX", "maxY", "id", "p", "nodeOrderCache", "labelOrder", "nodeLengths", "cached", "order", "arcCache", "arcPlacements", "deletions", "axis", "version", "arcs", "deletion", "deletionArcCurves", "curveMidpoint", "curveBounds", "sizeLabelCandidates", "alleleDeletions", "translateX", "translateY", "width", "height", "deletionCandidates", "nodeCandidates", "extent", "arcX", "arcY", "fits", "bow", "offset", "away", "leader", "crossing", "labelledByDeletion", "alleleRuns", "run", "drawn", "shownLeft", "shownRight", "shownTop", "shownBottom", "polylineMidpoint", "screenX", "nodeLabelBudget", "placeSizeLabels", "candidates", "take", "budget", "labels", "label", "HALO_FACTOR", "LEGEND_INSET_PX", "ROUTE_STACK", "GENE_PIN_DROP_PX", "GENE_STACK", "TOPMOST_BASELINE", "byExtent", "items", "extent", "a", "b", "layoutLabels", "m", "width", "height", "translateX", "translateY", "scaleX", "scaleY", "frame", "screen", "p", "reserved", "label", "y", "rowLabelBox", "stripPx", "take", "occupancy", "onRows", "rowsTop", "r", "halo", "byBubble", "h", "bubbles", "placeLabels", "x", "baseline", "genes", "pin", "sizeCandidates", "sizeLabelCandidates", "deletionLabels", "placeSizeLabels", "routes", "route", "nodeLabels", "nodeLabelBudget", "import_colorBits", "import_colorBits", "import_colorBits", "fadeAbgr", "c", "alpha", "brightenAbgr", "factor", "MAX_BACKBONE_HOPS", "buildNeighbors", "graph", "neighbors", "add", "from", "to", "list", "edge", "flankingSpan", "nodeId", "nodeById", "seen", "frontier", "minEnd", "maxStart", "hop", "next", "id", "adjacent", "neighborId", "neighbor", "isBackbone", "nodeReferenceSpan", "node", "referenceMidpoints", "midpoints", "span", "cache", "baseEdgeCurves", "nodePositions", "graph", "axis", "deletions", "version", "scaleX", "scaleY", "cached", "curves", "ei", "edge", "from", "to", "bypassed", "computeEdgeCurves", "selfLinkOf", "bypassedPoints", "EDGE_DEFAULT_COLOR", "EDGE_PATH_FALLBACK_COLOR", "EDGE_DELETION_COLOR", "DELETION_THICKNESS_FACTOR", "LIFT_BACKDROP_COLOR", "FADED_ALPHA", "DELETION_DASH_PX", "MIN_PATH_STRIPE_PX", "MIN_WALK_LANE_PX", "MIN_SCALE_FOR_OFFSET", "ARROW_LENGTH_BASE_PX", "ARROW_LENGTH_PER_EDGE_PX", "ARROW_HALF_WIDTH_BASE_PX", "ARROW_HALF_WIDTH_PER_EDGE_PX", "MIN_ARROW_FIT", "MIN_ARROW_SCALE", "ARROW_CURVE_STEPS", "ABUTTING_PX", "walkX", "walkY", "walkPx", "backFromEnd", "curves", "scale", "yToX", "untilPx", "most", "last", "n", "px", "ci", "c", "k", "t", "u", "a", "b", "d", "e", "x", "walkPointAt", "i", "from", "to", "f", "arrowheadFor", "edgeHalfWidth", "nodeHalfWidth", "color", "fromSegments", "toSegments", "length", "lengthPx", "inset", "room", "polylinePx", "fit", "tip", "base", "dx", "dy", "endTangent", "intoDirection", "segments", "first", "final", "pointNormalsOf", "rawPoints", "points", "p", "pointNormals", "nx", "ny", "len", "dx1", "dy1", "len1", "dx2", "dy2", "len2", "nx1", "ny1", "nx2", "ny2", "dot", "offsetPolyline", "normals", "distance", "packNorm", "r", "g", "hslToRgb", "h", "s", "l", "m", "DEPTH_GRADIENT", "STABLE_RANK_GRADIENT", "NODE_LENGTH_GRADIENT", "sampleGradient", "stops", "pos", "hashColor", "str", "alpha", "nameHue", "PATH_SATURATION", "PATH_LIGHTNESS", "REFERENCE_RAMP_MAX_HUE", "REFERENCE_RAMP_SATURATION", "REFERENCE_RAMP_LIGHTNESS", "REFERENCE_RAMP_ALT_RGB", "REFERENCE_RAMP_ALT_COLOR", "REFERENCE_RAMP_ALT_CSS", "computeReferenceRamp", "graph", "domain", "midpoints", "referenceMidpoints", "min", "max", "mid", "computeColorSchemeRange", "minDepth", "maxDepth", "minLength", "maxLength", "maxRank", "getNodeColor", "node", "nodeIndex", "colorScheme", "range", "ramp", "frac", "hue", "into", "candidates", "y", "isPolylineInBounds", "bounds", "hit", "isBezierInBounds", "cMinX", "cMaxX", "cMinY", "cMaxY", "paletteCache", "pathPaletteOf", "palette", "pathColorByIndex", "pathIndexByName", "paths", "pathHueAt", "slotsCache", "NO_SLOTS", "pathSlotsOf", "slots", "pathIdx", "path", "nodeId", "visits", "buildGeometry", "options", "nodePositions", "nodeById", "contigThickness", "connectorThickness", "drawPaths", "nodeWidth", "highlight", "axis", "linearLayout", "viewportBounds", "referenceRamp", "deletions", "hiddenEdges", "version", "depthNorm", "meanDepth", "yToXOf", "sharedCurves", "baseEdgeCurves", "nodeStrokes", "nodeStrokeRuns", "arrows", "arrowRuns", "edgeCurves", "edgeCurveRuns", "colorRange", "nodeIndexMap", "pathCount", "pathColorByName", "name", "nodePathSlots", "showArrows", "ei", "edge", "baseCurves", "numPaths", "isDeletion", "onWalk", "edgeThickness", "ribbons", "plainEdgeColor", "edgeColor", "fadeAbgr", "toNode", "intoHalfWidth", "nodeWidthPx", "buildSingleEdge", "offsetX", "offsetY", "arrowColor", "translateCurves", "dash", "dashCurves", "head", "edgeCurveStart", "arrowStart", "laneWalks", "fromNode", "width", "laneWidth", "worldPerScreenPx", "walk", "lane", "offset", "offsets", "pathRibbonOffsets", "arrowRibbon", "ribbonColor", "edgeCurveCount", "arrowCount", "faded", "own", "nodeThickness", "start", "slotWidth", "drawable", "lanes", "laneColor", "slot", "stripe", "count", "WALK_FIELDS", "WALK_SCHEMES", "FAMILIES", "isField", "v", "f", "isScheme", "s", "resolveEncoding", "layer", "reference", "picked", "alone", "askedField", "askedScheme", "field", "family", "scheme", "BASE_T", "toLinear", "c", "toGamma", "oklab", "hex", "r", "g", "b", "l", "m", "oklabToRgb", "L", "a", "schemeRgb", "x", "hslToRgb", "REFERENCE_RAMP_MAX_HUE", "from", "to", "schemeCss", "schemeColor", "encodedColor", "t", "NO_VALUE_COLOR", "REFERENCE_RAMP_ALT_COLOR", "encodingSwatchCss", "field", "scheme", "schemeCss", "BASE_T", "_", "i", "t", "nodeOwnLocation", "node", "isAnchored", "anchoredNodeLocation", "panSNSample", "panSNHaplotype", "panSNContig", "widestCluster", "spans", "maxGap", "sorted", "a", "b", "best", "current", "span", "groupKey", "loc", "contributingAssemblies", "graph", "groups", "key", "group", "bySample", "candidate", "existing", "resolveLocationAssembly", "resolve", "resolveContributors", "contributors", "c", "sample", "locString", "locLabel", "import_jsx_runtime", "CompareArrows_default", "_jsx", "import_jsx_runtime", "HighlightAlt_default", "_jsx", "import_jsx_runtime", "LineStyle_default", "_jsx", "LINEAR_LABEL", "SYNTENY_LABEL", "NO_SYNTENY_TRACK", "oneOrMany", "label", "icon", "entries", "entryLabel", "onSelect", "disabledHelpText", "nameTheOnlyEntry", "first", "empty", "entry", "graphLaunchMenuItems", "contributors", "syntenyTracks", "onShowLinear", "onShowSynteny", "widest", "LineStyle_default", "c", "locLabel", "CompareArrows_default", "track", "nodeLaunchMenuItems", "own", "reference", "highlight", "onHighlight", "HighlightAlt_default", "target", "canonicalStrand", "node", "stepName", "strand", "stepStarts", "lengths", "total", "starts", "head", "i", "tail", "j", "mismatchesByStep", "record", "byStep", "stepAt", "pos", "atEnd", "s", "start", "length", "add", "mismatch", "list", "last", "op", "parseCs", "at", "left", "take", "tubeMapReads", "graph", "records", "firstId", "nodeByName", "n", "reads", "nodes", "step", "kept", "first", "mismatches", "names", "lastLength", "k", "FORWARD_READ_COLORS", "REVERSE_READ_COLORS", "readColor", "read", "colors", "parsePanSN", "refName", "parts", "haplotype", "collectRun", "seed", "byId", "adj", "claimed", "members", "anchors", "queue", "id", "node", "neighborId", "neighbor", "anchorSpan", "a", "sorted", "b", "first", "last", "start", "end", "runDistances", "entry", "m", "dist", "member", "reach", "known", "runOffsets", "ordered", "rank", "i", "offsets", "offset", "projectAlleles", "graph", "isAnchored", "n", "buildNeighbors", "alleles", "span", "max", "MIN_DELETION_BP", "MIN_ALLELE_SPAN_FRACTION", "placeOffReference", "graph", "span", "rowY", "positions", "minSpan", "alleles", "projectAlleles", "byId", "n", "alleleDeletions", "allele", "skipped", "drawn", "scale", "nodeId", "i", "node", "offset", "even", "at", "bp", "y", "neighbors", "buildNeighbors", "queue", "fromId", "fromEnd", "nextId", "next", "rankRows", "graph", "present", "node", "a", "b", "r", "i", "anchoredLayout", "region", "backbone", "backboneNodes", "span", "referenceSpan", "rows", "nodePositions", "backbonePositions", "alleleDeletions", "placeOffReference", "rowLabels", "rank", "row", "GUTTER", "LANE_PX", "nodeWidth", "node", "compare", "a", "b", "compareKeys", "UNREACHED", "referenceOrder", "graph", "byId", "n", "key", "orderKeys", "placeByWalks", "backbone", "isNode", "startOf", "next", "prev", "i", "head", "insertAfter", "at", "id", "after", "insertBefore", "before", "path", "rising", "falling", "last", "start", "ids", "from", "leading", "lead", "placed", "placeBySearch", "adjacent", "queue", "ka", "kb", "nb", "searched", "sided", "seed", "run", "farHalf", "hops", "deadEnd", "touchesLeft", "other", "k", "e", "isBackbone", "rank", "layerGraph", "order", "isRef", "preds", "succs", "link", "layerOf", "p", "s", "minSucc", "t", "lo", "hi", "layers", "orderedLayout", "layer", "layerWidth", "w", "layerX", "x", "l", "y", "free", "ideal", "taken", "lane", "d", "nodePositions", "cx", "ny", "contributingSamples", "graph", "keep", "carried", "node", "isOffReference", "sample", "parsePanSN", "held", "row", "a", "b", "sampleRowLayout", "region", "backbone", "backboneNodes", "samples", "span", "referenceSpan", "rowOf", "s", "i", "nodePositions", "backbonePositions", "spareRow", "alleleDeletions", "placeOffReference", "stable", "rowLabels", "emptyTrackShapes", "DEBUG", "DEFAULT_TRACK_COLORS", "configFrom", "options", "track", "config", "bed", "nodes", "tracks", "reads", "nodeMap", "nodesPerOrder", "nodeOrders", "assignments", "extraLeft", "extraRight", "maxOrder", "shapes", "emptyTrackShapes", "imageBounds", "trackForRuler", "coarsenedEdgeMeta", "layoutTubeMap", "inputNodes", "inputTracks", "inputReads", "copies", "deepCopy", "node", "i", "hadInputReads", "filterReads", "t", "straightenTrack", "generateNodeMap", "generateTrackIndexSequences", "generateNodeWidth", "generateNodeSuccessors", "generateNodeOrder", "reverseReversedReads", "mergeNodes", "generateNodeDegree", "getMaxOrder", "switchNodeOrientation", "coarsenHaplotypes", "rulerIndex", "refIndex", "altHaplotypes", "_", "bands", "buildCoarsenedSyntheticBands", "calculateTrackWidth", "generateLaneAssignment", "addTrackFeatures", "drawCoarsenedReads", "generateReadOnlyNodeAttributes", "placeReads", "generateNodeXCoords", "generateSVGShapesFromPath", "getImageDimensions", "generateTrackColor", "highlight", "generateTrackAlpha", "val", "isReverse", "nodeName", "s", "forward", "reverse", "flip", "isForwardIndex", "n", "index", "nodesToInvert", "currentSequence", "cur", "firstForwardIndex", "j", "orderY", "setMapToMax", "step", "map", "key", "value", "existing", "READ_WIDTH", "assignReadsToNodes", "read", "idx", "firstNode", "element", "pathIdx", "elemNode", "generateBasicPathsForReads", "sortedNodes", "compareNodesByOrder", "readsBySource", "source", "bucket", "allSources", "b", "topMargin", "placeReadSet", "bottomY", "calculateBottomY", "elementsWithoutNode", "len", "prevSegment", "lastValidPrev", "seg", "nextSegment", "firstValidNext", "previousVisitToNode", "nextVisitToNode", "betweenCycleReverseTraversal", "compareNoNodeReads", "segment", "readIDs", "toPlace", "incomingReads", "readID", "outgoingReads", "internalReads", "startY", "incomingKeys", "entry", "path", "decisionStep", "foundY", "k", "y", "compareIncomingReadKeys", "currentY", "occupiedUntil", "readElement", "setOccupiedUntil", "maxY", "compareReadOutgoingSegmentsByGoingTo", "occupiedFrom", "firstNodeOffset", "occUntil", "incReadElementIndices", "incRead", "incReadPathElement", "compareInternalReads", "readIdx", "currentRead", "finalNodeCoverLength", "heightIncrease", "adjustVertically3", "a", "pathIndex", "readA", "readB", "segmentA", "segmentB", "prevNodeA", "prevNodeB", "aPrev", "bPrev", "readIndexA", "pathIndexA", "readIndexB", "pathIndexB", "previousValidYA", "previousValidYB", "lastPathIndexA", "lastPathIndexB", "initialNodeA", "initialNodeB", "nodeA", "nodeB", "nextNodeIdx", "beginDiff", "compareTrackByInitialOrdering", "trackA", "trackB", "pathA", "pathB", "AIndex", "BIndex", "trackAOrder", "trackBOrder", "idxA", "idxB", "aFirst", "bFirst", "aLast", "bLast", "currentNodeIndex", "currentNodeIsForward", "currentNode", "previousNode", "previousNodeIsForward", "pos", "sequenceNew", "reverseMismatches", "nodeByName", "temp", "firstLen", "lastLen", "mismatches", "sequenceLength", "mm", "getReverseComplement", "COMPLEMENT", "result", "tracksOrReads", "rawNodeName", "fwdIdx", "switched", "nodeIndex", "NODE_HORIZONTAL_SLACK", "bounds", "successorSets", "predecessorSets", "addEdges", "current", "follower", "generateNodeOrderOfSingleTrack", "sequence", "forwardOrder", "backwardOrder", "minOrder", "order", "increaseOrderForAllNodes", "generateNodeOrderTrackBeginning", "anchorIndex", "anchorSeqVal", "currentOrder", "increment", "UNREACHABLE_ORDER", "fillUnassignedOrders", "orders", "filled", "modifiedSequence", "rightIndex", "leftIndex", "tracksAndReads", "reachability", "uninvert", "increaseOrderForSuccessors", "isSuccessor", "finalOrders", "first", "second", "scratch", "stamp", "generation", "stack", "childIndex", "max", "v", "amount", "startingNode", "tabuNode", "newOrder", "increasedOrders", "queue", "head", "currentNodeOrder", "successor", "predecessor", "pivotPath", "countPaths", "switchNodeOrientationForPaths", "paths", "toSwitch", "pivotNames", "prevOrder", "nextOrder", "addToSwitchScore", "score", "scores", "delta", "currentX", "nextX", "extra", "calculateExtraSpace", "leftSideEdges", "rightSideEdges", "fallAngleAdjustment", "prevSeg", "yDifference", "segmentNumber", "prevSegmentPerOrderPerTrack", "assignmentByOrderAndNode", "trackNo", "addToAssignment", "generateSingleLaneAssignment", "segmentID", "assignment", "getIdealLanesAndCoords", "currentLane", "potentialAdjustmentValues", "prevNameIsNull", "prevTrack", "compareByIdealLane", "adjustVertically", "verticalAdjustment", "minAdjustmentCost", "moveBy", "getVerticalAdjustmentCost", "adjustBy", "assignmentNode", "aNode", "nodeStart", "nodeEnd", "feature", "line", "allAreFour", "NARROW_WIDTH", "WIDE_WIDTH", "getReadXStart", "offset", "clampedXCoordinateOfBaseWithinNode", "getReadXEnd", "cover", "getXCoordinateOfBaseWithinNode", "base", "nodeLeftX", "nodeRightX", "nodePixelCoordinatesInX", "clamped", "xStart", "xEnd", "yStart", "yEnd", "trackColor", "trackAlpha", "dummy", "reversalFlag", "orderStartX", "orderEndX", "colorHighlight", "colorForCurrentHighlight", "createFeatureRectangle", "generateTurnaround", "COARSENED_ID_BASE", "buildCoarsenedSyntheticBands", "source", "unit", "coarsenedEdgeMeta", "edges", "ignoreStrand", "config", "item", "seq", "i", "sSigned", "dSigned", "sIdx", "dIdx", "srcNode", "nodes", "dstNode", "key", "a", "b", "existing", "maxEdgeCount", "e", "BAND_MIN_WIDTH", "READ_WIDTH", "BAND_MAX_WIDTH", "widthForCount", "c", "synthetic", "edge", "id", "COARSENED_ID_BASE", "label", "srcLen", "tallestName", "tallestH", "n", "h", "DEBUG", "createFeatureRectangle", "node", "nodeXStart", "nodeXEnd", "highlight", "track", "rectXStart", "yStart", "trackColor", "reversalFlag", "currentHighlight", "co", "featureXStart", "featureXEnd", "nodeWidth", "feature", "generateTrackColor", "shapes", "MIN_BEND_WIDTH", "generateTurnaround", "dir", "x", "yEnd", "trackWidth", "trackID", "order", "type", "trackName", "extra", "extraRight", "extraLeft", "offset", "apex", "radius", "stem", "yTop", "yBottom", "stubNear", "stubFar", "horizontal", "segTop", "inner", "outer", "d", "nodePixelCoordinatesInX", "nodeLeftX", "nodeRightX", "generateNodeWidth", "charWidth", "len", "mergeNodes", "nodeName", "nodeName2", "predSets", "succSets", "tracksAndReads", "reads", "tracks", "isReverse", "forward", "nodeMap", "reverse", "pred", "s", "succ", "sortedNodes", "compareNodesByOrder", "mergeOffset", "mergeOrigin", "offsetOf", "originOf", "origin", "predecessor", "mergeableWithPred", "nodeByName", "read", "sequenceNew", "mergeableWithSucc", "predLength", "mismatch", "firstEntry", "firstOffset", "donor", "nodeIndex", "index", "predecessorIndex", "successor", "successorIndex", "filterReads", "readList", "focusNames", "groupBy", "items", "key", "groups", "item", "k", "group", "compareCurvesByXYStartValue", "a", "b", "curvePaths", "curves", "type", "groupedCurves", "curve", "curveGroup", "adjustValue", "adjustIncrement", "xAdjusted", "xNextAdjusted", "d", "groupKeys", "aParts", "bParts", "a0", "a1", "b0", "b1", "flattenedGroups", "nodeOutlinePath", "node", "x", "y", "visitStrands", "graph", "strands", "segment", "visits", "visit", "key", "list", "tubeMapTracks", "nodeById", "n", "paths", "path", "index", "reference", "p", "pathOrigin", "visitName", "seen", "sequence", "id", "node", "k", "strand", "canonicalStrand", "stepName", "tubeMapNodes", "hasTubeMapPaths", "STACK_PX", "MIN_TUBE_PX", "MAX_TUBE_PX", "tubeWidth", "trackCount", "runTubeMap", "tracks", "reads", "tubeMapReads", "tubeColor", "pathGreyCssColor", "pathCssColor", "pathColors", "_", "i", "layout", "layoutTubeMap", "track", "readColor", "drawnNodes", "nodes", "centreline", "yOffset", "x0", "x1", "y", "extentOf", "tubeMapLayout", "run", "nodePositions", "minY", "maxY", "tubeMapColumns", "byOrder", "columns", "end", "order", "a", "b", "bp0", "bp1", "graphNode", "isBackbone", "first", "c", "column", "pathsReachBackbone", "tubeMapReferenceLayout", "sampleOf", "path", "panSNSample", "labelOf", "sliceBetween", "nodeIds", "span", "region", "flanked", "i0", "i1", "bestEnd", "bestStart", "id", "i", "s", "ids", "walkRows", "graph", "unit", "paths", "reference", "p", "pathOrigin", "lengthOf", "n", "referenceStart", "cut", "pos", "len", "rowOf", "complete", "runs", "bp", "offReferenceBp", "step", "last", "at", "forward", "backward", "meetsReference", "placedWalks", "a", "READOUT_ROOM", "walkRowsExtent", "walks", "longest", "row", "walkRowLayout", "graph", "region", "backbone", "backboneNodes", "walkRows", "nodePositions", "backbonePositions", "rowLabels", "FORCE_LAYOUT_LABEL", "hasBackbone", "graph", "isBackbone", "hasWalks", "hasTubeMapBackbone", "hasTubeMapPaths", "hasAlleles", "isOffReference", "LAYOUT_MODES", "anchoredLayout", "sampleRowLayout", "walkRowLayout", "orderedLayout", "tubeMapLayout", "tubeMapReferenceLayout", "LAYOUT_MODE_VALUES", "m", "modeUsesLayoutEngine", "value", "layoutModeByValue", "REPEAT_GROWTH", "coveringRepeat", "b", "repeats", "span", "growth", "r", "SUPERBUBBLE_SEGMENTS", "formatBp", "bp", "formatBpRange", "shortest", "longest", "low", "high", "unit", "routes", "count", "SATURATED_PATH_COUNT", "classifyBubble", "c", "classifyShape", "refSpan", "pathCount", "segmentCount", "inversion", "range", "repeat", "name", "alleles", "BUBBLE_KIND_COLORS", "BUBBLE_KIND_NAMES", "bubbleSegmentIds", "parseTag", "tag", "tags", "nameEnd", "typeEnd", "name", "type", "val", "parseTags", "fields", "field", "stableCoordinate", "node", "SN", "SO", "SR", "GT", "LT", "parseWalkBody", "body", "segments", "strand", "start", "push", "end", "i", "ch", "parseGFA", "file", "graph", "rawLine", "line", "rest", "len", "seq", "tagfields", "gfa1", "first", "source", "target", "cigar", "strand1", "strand2", "sample", "hap", "contig", "path", "surveySegments", "gfaGraph", "canonical", "traversals", "claim", "id", "strand", "traverse", "link", "gfaPath", "segment", "walk", "seg", "anchorablePaths", "p", "pathOrigin", "w", "s", "carriedSamples", "gfaNode", "sm", "samples", "positive", "v", "statedDepth", "tags", "length", "count", "makeNode", "depth", "stableCoordinate", "convertGFAToGraph", "name", "nodes", "edges", "nodeId", "segmentId", "paths", "edgeToPathsMap", "edgeKey", "a", "b", "recordPathEdges", "nodeIds", "i", "key", "edge", "pathIds", "lengths", "n", "anchoring", "surveyPaths", "orientToReference", "graph", "positions", "backbone", "node", "isBackbone", "points", "segments", "mid", "weight", "sum", "p", "cx", "cy", "cr", "sxr", "syr", "angle", "c", "s", "out", "id", "x", "y", "below", "above", "LANE_GAP", "referenceSeeds", "graph", "scaling", "opts", "drawn", "node", "drawnNodeLength", "drawnOf", "id", "backbone", "isBackbone", "a", "b", "seeds", "x", "adjacent", "buildNeighbors", "depth", "queue", "at", "next", "neighbour", "unreached", "seededNodes", "loadGraph", "text", "name", "opts", "graph", "anchorGraph", "convertGFAToGraph", "parseGFA", "engineRequest", "scaling", "settings", "anchored", "isBackbone", "seededNodes", "engineKey", "forceLayout", "engine", "spread", "spreadFor", "merged", "mergeRuns", "result", "duration", "layoutScaling", "drawn", "n", "drawnNodeLength", "positions", "splitRuns", "id", "orientToReference", "layoutExtent", "nodePositions", "minX", "minY", "maxX", "maxY", "segments", "seg", "FIT_PADDING", "MIN_ZOOM", "MAX_ZOOM", "clampZoom", "zoom", "fittedTranslateY", "bounds", "usableHeight", "yScale", "padTop", "leftoverY", "fitTransform", "paneWidth", "paneHeight", "pixelRows", "minScale", "padLeft", "padRight", "padBottom", "usableWidth", "fitX", "fitY", "scale", "leftoverX", "drawingBounds", "layout", "region", "extent", "import_colorBits", "REFERENCE_STRIP_PX", "REFERENCE_STRIP_ZONE_PX", "OVERHANG_PX", "referenceStripBlocks", "graph", "colorScheme", "referenceRamp", "walkNodes", "walkColors", "range", "computeColorSchemeRange", "out", "node", "index", "isBackbone", "own", "LIFT_BACKDROP_COLOR", "getNodeColor", "fadeAbgr", "FADED_ALPHA", "a", "b", "span", "frame", "bp0", "bp1", "HIT_SLOP_PX", "stripOverhang", "blocks", "width", "lo", "hi", "below", "above", "block", "stripBlockAt", "sx", "sy", "hit", "best", "x0", "x1", "dist", "nodeAnchor", "segments", "toScreen", "p", "drawOverhang", "ctx", "tipX", "inward", "mid", "baseX", "drawReferenceStrip", "lit", "darkMode", "ink", "overhang", "w", "left", "referenceBoxes", "graph", "layout", "nodeById", "n", "byRefName", "node", "graphNode", "isBackbone", "refName", "start", "boxes", "a", "b", "tubeX", "bp", "isEnd", "lo", "hi", "mid", "bp1", "box", "span", "tubeSpan", "end", "rulerBoxes", "best", "TICK_PX", "LABEL_GAP_PX", "TARGET_TICK_PX", "MIN_BRACKET_PX", "rulerInk", "darkMode", "niceStep", "raw", "pow", "m", "placeLabels", "candidates", "placed", "text", "at", "scaleMarks", "x", "width", "measure", "first", "last", "from", "to", "bpSpan", "step", "ticks", "positions", "sx", "boxMarks", "shown", "s0", "s1", "label", "position", "spans", "lengths", "boundaries", "length", "formatBp", "ends", "rulerMarks", "referenceAxis", "drawTubeMapRuler", "ctx", "frame", "top", "marks", "ink", "y", "x0", "x1", "referenceNodes", "graph", "layout", "nodeById", "n", "out", "node", "graphNode", "isBackbone", "tubeMapConnectors", "nodes", "bpToScreen", "tubeToScreen", "width", "bottom0", "bottom1", "HIT_SLOP_PX", "connectorAt", "connectors", "zoneBottom", "sx", "sy", "t", "hit", "best", "c", "a", "b", "dist", "band", "ctx", "bottom", "sides", "drawTubeMapConnectors", "highlightNode", "darkMode", "ink", "lit", "boxesOf", "graph", "layout", "nodeById", "n", "boxes", "node", "index", "g", "isBackbone", "bp0", "a", "b", "boxAt", "bp", "visited", "lo", "hi", "mid", "box", "deviationMarks", "deviations", "marks", "track", "devs", "segment", "d", "bp1", "at", "deviationInk", "darkMode", "drawDeviationMarks", "ctx", "x", "y", "width", "m", "left", "right", "top", "readMismatches", "read", "layout", "marks", "entries", "entry", "i", "nodeIndex", "forward", "node", "pathIndex", "y", "base", "at", "pos", "getXCoordinateOfBaseWithinNode", "mm", "x0", "softClip", "x1", "tubeMapMismatches", "parsePath", "d", "tokens", "commands", "i", "num", "op", "extentOf", "x0", "x1", "c", "shapeOf", "color", "alpha", "layerOf", "layout", "type", "shapes", "rects", "r", "curves", "curvePaths", "corners", "tubeMapPicture", "nodes", "node", "nodeOutlinePath", "tubeMapMismatches", "m", "trace", "ctx", "x", "y", "cx", "cy", "fillByColor", "items", "visible", "addTo", "byColor", "item", "key", "list", "FULL_OUTLINE_PX", "MIN_OUTLINE_ALPHA", "outlineAlpha", "screenWidth", "drawTubeMap", "picture", "frame", "width", "layer", "left", "top", "s", "stroke", "fill", "lit", "drawMismatches", "MISMATCH_FONT_PX", "MIN_MISMATCH_FONT_PX", "mismatchesLegible", "yScale", "mismatchOnScreen", "marks", "darkMode", "fontPx", "tubeMapGenes", "byRefName", "genes", "out", "gene", "boxes", "first", "last", "start", "end", "tubeSpan", "e", "GENE_ROW_PX", "MAX_GENE_ROWS", "EXON_PX", "NAME_FONT", "NAME_GAP_PX", "tubeMapGeneRows", "edges", "g", "a", "b", "depth", "most", "step", "drawTubeMapGenes", "ctx", "frame", "bottom", "top", "x", "width", "darkMode", "rows", "rowEnds", "ink", "placed", "s0", "s1", "arrow", "label", "mid", "half", "left", "right", "row", "glyphY", "e0", "axisScaleOf", "scale", "pixelRows", "viewportOf", "t", "axis", "width", "height", "padded", "v", "panes", "w", "h", "contains", "outer", "inner", "zoomAbout", "factor", "centerX", "centerY", "clampZoom", "ratio", "isLinearHost", "view", "v", "widest", "blocks", "best", "b", "hostFrame", "region", "block", "bpPerPx", "left", "hostWindow", "displayed", "hostCut", "window", "capBp", "margins", "visible", "margin", "start", "end", "over", "cutHolds", "cut", "exact", "agrees", "walkBp", "callBp", "nearest", "walks", "calls", "W", "C", "cost", "paired", "j", "together", "dropWalk", "dropCall", "out", "byLength", "items", "bp", "item", "i", "a", "b", "backed", "call", "scored", "row", "pairSample", "rows", "taken", "c", "unphased", "ranked", "r", "stages", "pool", "group", "free", "open", "pick", "k", "withCalls", "bySample", "sample", "called", "WHOLE_FRACTION", "NAMED_WALKS", "MIN_CHIPPED_BP", "bubbleHalos", "graph", "bubbles", "positions", "walkLabel", "name", "repeats", "byName", "n", "halos", "bubble", "parts", "minX", "maxX", "minY", "nodeIds", "ends", "bubbleSegmentIds", "node", "line", "isBackbone", "svgPath", "p", "labelAt", "anchor", "s", "sharing", "route", "id", "routes", "rarest", "at", "far", "d", "routeText", "classifyBubble", "names", "shown", "more", "formatBp", "bubblesFromGraph", "graph", "isBackbone", "byId", "n", "order", "referenceOrder", "indexOf", "id", "i", "reach", "succ", "linked", "e", "a", "b", "lo", "hi", "from", "to", "backbone", "ends", "boundaries", "node", "ia", "ib", "walkIndex", "p", "at", "bubbles", "start", "i0", "end", "i1", "interior", "best", "cur", "t", "add", "prev", "SATURATED_PATH_COUNT", "walked", "walkRoutes", "crossed", "routes", "walksLeave", "refStart", "refEnd", "chain", "chainBroken", "k", "partial", "fallback", "startId", "endId", "seen", "min", "max", "left", "steps", "bp", "key", "route", "bubbleSubgraph", "graph", "segmentIds", "keep", "nodes", "n", "ids", "paths", "p", "id", "e", "genePins", "graph", "genes", "positions", "byRefName", "node", "isBackbone", "refName", "entry", "nodes", "b", "pins", "gene", "backbone", "parts", "at", "atDistance", "covered", "mid", "reach", "i", "firstNodeAtOrAfter", "nodeStart", "nodeEnd", "line", "exon", "a", "stretch", "polylineSlice", "svgPath", "pinBp", "distance", "p", "trimToWindow", "graph", "window", "inWindow", "n", "isBackbone", "paths", "lengthOf", "spans", "path", "first", "last", "id", "i", "span", "kept", "trimmed", "droppedBp", "nodeIds", "sum", "e", "trimVisits", "shiftOrigins", "visits", "nameOf", "cursor", "out", "p", "origin", "pathOrigin", "segment", "list", "key", "at", "visit", "keptVisits", "origins", "pieces", "dropped", "readWalk", "path", "nodeById", "strands", "visitName", "pathOrigin", "seen", "runs", "open", "gaps", "id", "node", "k", "strand", "canonicalStrand", "gap", "last", "isBackbone", "start", "end", "inverted", "joins", "run", "i", "isSmall", "sigma", "strictlyInside", "cuts", "a", "b", "lo", "hi", "mid", "referenceGaps", "backbone", "prev", "findCuts", "walks", "cut", "bp", "cause", "first", "changed", "sorted", "x", "y", "spans", "g", "r", "mergeReference", "merged", "group", "close", "bp0", "bp1", "name", "coarsenTubeMap", "graph", "paths", "n", "visitStrands", "p", "sortedCuts", "starts", "m", "mergedAt", "members", "coarseOf", "routes", "routeFor", "key", "s", "deviations", "coarsePaths", "pathVisits", "edges", "depth", "steps", "devs", "continuing", "append", "structural", "fold", "home", "i0", "i1", "small", "j", "pos", "visits", "next", "edge", "nodes", "isReal", "c", "referenceKnots", "columns", "bpToScreen", "real", "first", "screen", "i", "left", "right", "short", "capLeft", "capRight", "fromLeft", "fromRight", "knots", "s0", "s1", "trimLeft", "trimRight", "warpX", "tx", "last", "lo", "hi", "mid", "a", "b", "span", "MIN_TUBE_Y_SCALE", "NODE_BOX_PAD", "squeeze", "drawing", "usableHeight", "minY", "maxY", "h", "tubeMapFrame", "view", "scaleX", "translateX", "translateY", "yScale", "y", "ty", "columns", "knots", "referenceKnots", "bp", "tx", "warpX", "tubeMapNodeAt", "x", "sx", "sy", "hit", "node", "referenceOf", "graph", "p", "pathOrigin", "walkHighlight", "name", "path", "byId", "n", "edgeAt", "e", "i", "edgeIndexes", "a", "b", "index", "lengthOf", "id", "bp", "sum", "progress", "before", "length", "reference", "originName", "origin", "panSNContig", "laneColors", "walk", "encoding", "ramp", "colors", "node", "mid", "t", "encodedColor", "reversedNodes", "reversed", "walkPath", "referencePath", "visits", "own", "v", "theirs", "walkLift", "layers", "seen", "lifted", "layer", "picked", "walks", "resolveEncoding", "w", "import_configuration", "import_ui", "import_util", "import_io", "import_mobx_state_tree", "import_RenderLifecycleMixin", "import_canvas2dUtils", "import_mobx", "GENE_ADAPTER_TYPES", "GENE_TRACK_HINT", "pickGeneTrack", "tracks", "named", "t", "candidates", "field", "f", "name", "exonsOf", "into", "children", "child", "merged", "intervals", "sorted", "a", "b", "out", "iv", "last", "geneModelsFrom", "features", "genes", "start", "end", "exons", "import_mobx_state_tree", "knownOrUnset", "values", "value", "lenientMaybeEnum", "lenientOptionalEnum", "fallback", "REPEAT_ADAPTER_TYPES", "REPEAT_TRACK_HINT", "pickRepeatTrack", "tracks", "named", "t", "own", "f", "name", "PACKED_NAME", "packedName", "pair", "i", "field", "top", "MOTIF_FIELDS", "PERIOD_FIELDS", "NAME_FIELDS", "first", "names", "value", "one", "IUPAC", "repeatUnitOf", "period", "motif", "numbers", "n", "strings", "text", "TANDEM_REPEAT", "tandemAlleleBp", "alts", "rn", "rus", "rul", "ruc", "rb", "k", "alt", "bp", "known", "j", "unitLength", "count", "runBp", "genotyped", "gt", "alleleBp", "referenceBp", "phased", "index", "callsOf", "samples", "called", "sample", "fields", "reads", "lengths", "calls", "spanOf", "tandem", "start", "end", "svlen", "repeatArraysFrom", "features", "arrays", "unit", "refName", "stated", "a", "b", "isRecord", "value", "isFeatureLike", "hoveredName", "feature", "name", "readLgvHover", "hovered", "result", "refName", "coord", "coord0", "assemblyName", "hoverInRegion", "hover", "region", "nodeForLgvHover", "nodes", "byName", "byCoord", "node", "isBackbone", "import_configuration", "isRecord", "value", "isLinearView", "view", "withRows", "views", "linearViewTarget", "connectedViewId", "assemblyName", "candidates", "reportFailure", "session", "what", "launch", "error", "MIN_LAUNCH_SPAN_BP", "paddedLocation", "loc", "minSpan", "pad", "withReferenceRegion", "contributors", "region", "c", "showInLinearView", "location", "assembly", "connectedViewId", "tracks", "existing", "linearViewTarget", "viewId", "locLabel", "locString", "highlightInLinearView", "target", "assemblyNamesOfTrack", "trackId", "track", "t", "names", "n", "launchSyntenyView", "graphTrackId", "graphTrackAssemblies", "aligned", "covered", "panels", "snapshot", "import_configuration", "ANNOTATION_TRACK_TYPE", "launchTracks", "session", "assemblyName", "first", "found", "track", "type", "assemblyNames", "trackId", "import_configuration", "import_tracks", "import_mobx_state_tree", "SYNTENY_TRACK_TYPE", "SYNTENY_ADAPTER_CATEGORY", "isSyntenyTrack", "track", "adapterType", "pluginManager", "launchableSyntenyTracks", "session", "assemblyNames", "wanted", "found", "trackAssemblies", "trackId", "coverage", "name", "a", "b", "MAX_CANVAS_HEIGHT", "MIN_CANVAS_HEIGHT", "FACET_GAP_PX", "FACET_TITLE_PX", "FACET_PAD_PX", "FACET_MAX_COLUMNS", "FACET_WIDE", "MIN_FACET_PANEL_PX", "MIN_FIT_TUBE_PX", "TUBE_MAP_MODES", "TUBE_MAP_FOLDS", "SEGMENTS_SUFFIX", "bubblePrefix", "adapterConfig", "uri", "HOVER_BRIGHTEN", "SELECT_BRIGHTEN", "VIEWPORT_DEBOUNCE_MS", "VIEWPORT_PANES_BUILT", "EMPTY_BATCH", "dependOn", "_values", "MAX_GRAPH_REGION_BP", "formatSpanBp", "bp", "DEFAULT_MAX_GRAPH_NODES", "geometryPainted", "model", "paneViewportOf", "viewportOf", "forceLayouts", "FORCE_LAYOUT_CACHE_SIZE", "forceLayoutsOf", "graph", "cache", "inheritForceLayouts", "from", "to", "remember", "key", "result", "GraphPaneMixin", "lenientMaybeEnum", "LAYOUT_MODE_VALUES", "COLOR_SCHEME_VALUES", "lenientOptionalEnum", "BUBBLE_SPREAD_VALUES", "NODE_WIDTH_VALUES", "self", "m", "modeUsesLayoutEngine", "n", "paths", "tubeMap", "pathColorsLegible", "pathLegend", "walkLift", "computeReferenceRamp", "lift", "ramp", "w", "layer", "l", "panel", "resolveColorScheme", "bubblesFromGraph", "track", "region", "t", "GENE_ADAPTER_TYPES", "REPEAT_ADAPTER_TYPES", "pickRepeatTrack", "r", "graphBackbone", "assemblyName", "assemblyManager", "assembly", "panSN", "names", "backbone", "geneAssembly", "geneFeatures", "binds", "backboneAssembly", "featuresOnBackbone", "buildNeighbors", "nodeInk", "deletionEdges", "repeat", "bars", "walkRows", "samples", "rows", "a", "b", "reference", "paired", "withCalls", "row", "i", "FIT_PADDING", "rowLabelBox", "positions", "genePins", "labels", "c", "bubbleHalos", "name", "axisScaleOf", "layout", "drawingBounds", "walkRowsExtent", "pickGeneTrack", "d", "nodeId", "nodeById", "neighbors", "nodeReferenceSpan", "bubble", "span", "backboneNodes", "contributingAssemblies", "backboneSpan", "byPrefix", "asm", "prefix", "loaded", "sample", "resolveContributors", "withReferenceRegion", "linearViewTarget", "node", "own", "nodeOwnLocation", "ownAssembly", "resolveLocationAssembly", "onReference", "paddedLocation", "view", "isLinearHost", "layoutModeByValue", "host", "graphRegion", "hostFrame", "drawing", "tubeMapPicture", "referenceBoxes", "coarse", "deviationMarks", "referenceNodes", "tubeMapGenes", "layoutResult", "referenceStripBlocks", "frame", "left", "right", "stripOverhang", "toScreen", "p", "halo", "h", "sameBubble", "nodeAnchor", "sx", "sy", "stripBlockAt", "REFERENCE_STRIP_ZONE_PX", "tubeMapGeneRows", "GENE_ROW_PX", "bounds", "tubePx", "LEGEND_INSET_PX", "panels", "columns", "width", "drawn", "room", "height", "usableWidth", "ceiling", "grid", "floor", "fitTransform", "layoutLabels", "tubeMapFrame", "rulerBoxes", "picture", "nodes", "tubeMapConnectors", "reads", "shown", "mismatchesLegible", "mismatchOnScreen", "tubeMapNodeAt", "connectorAt", "error", "message", "ms", "strokeCount", "built", "quality", "linear", "mode", "reanchored", "anchorFromPaths", "draw", "show", "scheme", "spread", "trackId", "layers", "had", "walk", "facet", "color", "px", "viewId", "withRows", "inside", "size", "edgeIdx", "panning", "session", "s", "tx", "ty", "clampZoom", "factor", "centerX", "centerY", "zoomAbout", "usableHeight", "fittedTranslateY", "fit", "scale", "translateX", "engaging", "scheduleViewportDirty", "requestPositionsDirtyFrame", "dx", "dy", "seg", "loadController", "callEngine", "request", "rpcManager", "status", "forceLayoutKey", "engineKey", "computeLayout", "start", "trimToWindow", "coarsenTubeMap", "local", "hit", "oriented", "forceLayout", "liveRequest", "layoutInto", "isLive", "signal", "computed", "e", "live", "loadedReference", "assemblyWalk", "chooseReferencePath", "parseAndLayout", "text", "keepSelection", "readsOf", "parsed", "loadGraph", "records", "total", "selected", "liveLoad", "beginLoad", "load", "loadBubbles", "features", "f", "trackFeatures", "what", "config", "loadGenes", "geneModelsFrom", "loadRepeats", "repeatArraysFrom", "loadWholeGFA", "source", "abortLoad", "location", "opts", "fetchStart", "gfaText", "locLabel", "namesReads", "sub", "bubbleSubgraph", "bubbleSegmentIds", "label", "BUBBLE_KIND_NAMES", "classifyBubble", "backend", "hover", "readLgvHover", "hoverInRegion", "nodeForLgvHover", "firstViewport", "viewport", "contains", "hoveredNode", "hoveredEdge", "selectedNode", "geometryStart", "viewportBounds", "padded", "batch", "buildGeometry", "dpr", "target", "showInLinearView", "launchTracks", "highlightInLinearView", "launchSyntenyView", "launchableSyntenyTracks", "walks", "LAYOUT_MODES", "COLOR_SCHEMES", "lifted", "WALK_FIELDS", "field", "WALK_SCHEMES", "unit", "items", "item", "graphLaunchMenuItems", "import_jsx_runtime", "Settings_default", "_jsx"]
|
|
7
|
+
}
|