jbrowse-plugin-graphgenomeviewer 4.0.19 → 4.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +2 -1
  2. package/dist/chunks/{GbzBaseSyntenyAdapter-KKSNCZXY.js → GbzBaseSyntenyAdapter-7ZKFAEJG.js} +2 -2
  3. package/dist/chunks/{GbzBaseSyntenyAdapter-KKSNCZXY.js.map → GbzBaseSyntenyAdapter-7ZKFAEJG.js.map} +2 -2
  4. package/dist/chunks/{GraphGenomeView-37F7MLFQ.js → GraphGenomeView-CLUMXH4K.js} +2 -2
  5. package/dist/chunks/{GraphTrackSettingsDialog-2QV6TPX7.js → GraphTrackSettingsDialog-QF5OMTCH.js} +2 -2
  6. package/dist/chunks/{LinearGraphDisplay-CJHA56RB.js → LinearGraphDisplay-4CMVCEM5.js} +2 -2
  7. package/dist/chunks/chunk-3FGJLRG2.js +2 -0
  8. package/dist/chunks/chunk-3FGJLRG2.js.map +7 -0
  9. package/dist/chunks/chunk-3SZJ4AFI.js +3 -0
  10. package/dist/chunks/chunk-3SZJ4AFI.js.map +7 -0
  11. package/dist/chunks/{chunk-5E7TVPCI.js → chunk-CLRXQ6BM.js} +2 -2
  12. package/dist/chunks/chunk-VIFXYTIS.js +2 -0
  13. package/dist/chunks/chunk-VIFXYTIS.js.map +7 -0
  14. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +2 -2
  15. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +3 -3
  16. package/package.json +2 -2
  17. package/src/GbzBaseSyntenyAdapter/GbzBaseSyntenyAdapter.ts +1 -1
  18. package/src/GetSubgraph.ts +3 -0
  19. package/src/GraphGenomeView/components/BubbleHalos.tsx +3 -4
  20. package/src/GraphGenomeView/components/GraphCanvas.tsx +51 -15
  21. package/src/GraphGenomeView/components/LabelLayer.tsx +5 -6
  22. package/src/GraphGenomeView/components/WalkSelect.tsx +26 -13
  23. package/src/GraphGenomeView/model.test.ts +48 -0
  24. package/src/GraphGenomeView/model.ts +75 -21
  25. package/src/GraphGenomeView/viewModel.ts +15 -0
  26. package/src/LinearGraphDisplay/LinearGraphDisplay.test.ts +23 -2
  27. package/src/LinearGraphDisplay/model.ts +7 -2
  28. package/src/version.ts +1 -1
  29. package/dist/chunks/chunk-BI3M6FZD.js +0 -2
  30. package/dist/chunks/chunk-BI3M6FZD.js.map +0 -7
  31. package/dist/chunks/chunk-IUIIYSGF.js +0 -2
  32. package/dist/chunks/chunk-IUIIYSGF.js.map +0 -7
  33. package/dist/chunks/chunk-MNOQXDMY.js +0 -3
  34. package/dist/chunks/chunk-MNOQXDMY.js.map +0 -7
  35. /package/dist/chunks/{GraphGenomeView-37F7MLFQ.js.map → GraphGenomeView-CLUMXH4K.js.map} +0 -0
  36. /package/dist/chunks/{GraphTrackSettingsDialog-2QV6TPX7.js.map → GraphTrackSettingsDialog-QF5OMTCH.js.map} +0 -0
  37. /package/dist/chunks/{LinearGraphDisplay-CJHA56RB.js.map → LinearGraphDisplay-4CMVCEM5.js.map} +0 -0
  38. /package/dist/chunks/{chunk-5E7TVPCI.js.map → chunk-CLRXQ6BM.js.map} +0 -0
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["global-externals:@mui/material/SvgIcon", "global-externals:mobx", "global-externals:@jbrowse/core/util/colorBits", "global-externals:@jbrowse/render-core/canvas2dUtils", "../../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", "../../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/bubbles/classifyBubble.ts", "../../packages/core/src/tubeMap/reads.ts", "../../packages/core/src/referenceSpan.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/renderer/GeometryBuilder.ts", "../../packages/core/src/renderer/colorBits.ts", "../../packages/core/src/util/edgeCurves.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", "../../src/GraphGenomeView/repeats/walkCalls.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[\"@mui/material/SvgIcon\"];", "module.exports = JBrowseExports[\"mobx\"];", "module.exports = JBrowseExports[\"@jbrowse/core/util/colorBits\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/canvas2dUtils\"];", "'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 { 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 { 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", "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 { 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 { 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 `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 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 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 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 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 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 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 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 { 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 { WalkHighlight } 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 highlighted walk's links: darker than a plain link and heavier, so the\n// route reads as one stroke through the faded rest. The alpha the rest fades\n// to keeps every colour scheme's hues, only dimmer, on any background.\nconst EDGE_WALK_COLOR = packAbgr(30, 30, 36, 245)\nconst WALK_EDGE_THICKNESS_FACTOR = 1.8\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// 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 // one walk lifted out: its nodes keep their colour and its links draw dark\n // and heavy, everything else fades to a fraction of its alpha\n highlight?: WalkHighlight\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\nconst 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) *\n (isDeletion ? DELETION_THICKNESS_FACTOR : 1) *\n (onWalk ? WALK_EDGE_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 = !highlight\n ? plainEdgeColor\n : onWalk\n ? EDGE_WALK_COLOR\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 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 && edge.pathIds![pathIdx] !== highlight.name\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 own = getNodeColor(\n node,\n nodeIndexMap.get(nodeId) ?? 0,\n colorScheme,\n colorRange,\n )\n const faded = highlight !== undefined && !highlight.nodeIds.has(nodeId)\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 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 { 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 { abgrToCssRgba } from '@jbrowse/core/util/colorBits'\n\nimport { isBackbone } from './anchoredNodes'\nimport {\n FADED_ALPHA,\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 }: {\n colorScheme: ResolvedColorScheme\n referenceRamp?: ReferenceRamp\n walkNodes?: ReadonlySet<string>\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 = 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 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", "\"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_SvgIcon", "__commonJSMin", "exports", "module", "require_mobx", "__commonJSMin", "exports", "module", "require_colorBits", "__commonJSMin", "exports", "module", "require_canvas2dUtils", "__commonJSMin", "exports", "module", "import_SvgIcon", "svgPath", "points", "round", "v", "p", "i", "projectLine", "x1", "y1", "x2", "y2", "distance", "d", "vx", "vy", "curvePointAt", "c", "t", "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", "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", "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", "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", "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", "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", "l", "p", "s", "minSucc", "t", "lo", "hi", "layers", "orderedLayout", "layer", "layerWidth", "w", "layerX", "x", "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", "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", "_", "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", "i", "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", "import_colorBits", "import_colorBits", "fadeAbgr", "c", "alpha", "brightenAbgr", "factor", "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", "EDGE_WALK_COLOR", "WALK_EDGE_THICKNESS_FACTOR", "FADED_ALPHA", "DELETION_DASH_PX", "MIN_PATH_STRIPE_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", "y", "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", "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", "offsets", "pathRibbonOffsets", "arrowRibbon", "offset", "ribbonColor", "edgeCurveCount", "arrowCount", "own", "faded", "width", "nodeThickness", "start", "slotWidth", "drawable", "worldPerScreenPx", "slot", "stripe", "count", "import_colorBits", "REFERENCE_STRIP_PX", "REFERENCE_STRIP_ZONE_PX", "OVERHANG_PX", "referenceStripBlocks", "graph", "colorScheme", "referenceRamp", "walkNodes", "range", "computeColorSchemeRange", "out", "node", "index", "isBackbone", "own", "getNodeColor", "fadeAbgr", "FADED_ALPHA", "b", "span", "frame", "bp0", "bp1", "a", "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", "agrees", "walkBp", "callBp", "nearest", "walks", "calls", "W", "C", "cost", "paired", "i", "j", "together", "dropWalk", "dropCall", "out", "byLength", "items", "bp", "item", "a", "b", "backed", "call", "scored", "row", "pairSample", "rows", "taken", "c", "unphased", "ranked", "r", "stages", "pool", "group", "free", "open", "pick", "k", "withCalls", "bySample", "sample", "called", "import_jsx_runtime", "Settings_default", "_jsx"]
7
- }