jbrowse-plugin-graphgenomeviewer 4.0.2 → 4.0.4

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 (26) hide show
  1. package/README.md +6 -5
  2. package/dist/chunks/AddTrackWorkflow-I4EPGFPJ.js +2 -0
  3. package/dist/chunks/{AddTrackWorkflow-TPCKXM2H.js.map → AddTrackWorkflow-I4EPGFPJ.js.map} +2 -2
  4. package/dist/chunks/{GraphGenomeView-WPKSNN4I.js → GraphGenomeView-BQYWWF6Z.js} +2 -2
  5. package/dist/chunks/LinearGraphDisplay-7O3VQHKU.js +2 -0
  6. package/dist/chunks/{LinearGraphDisplay-7POYIRIB.js.map → LinearGraphDisplay-7O3VQHKU.js.map} +2 -2
  7. package/dist/chunks/chunk-5WJPRX4N.js +2 -0
  8. package/dist/chunks/{chunk-A4ZWFDQY.js.map → chunk-5WJPRX4N.js.map} +2 -2
  9. package/dist/chunks/{chunk-SDTCVLC3.js → chunk-RSAY56Y4.js} +2 -2
  10. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +2 -2
  11. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +3 -3
  12. package/package.json +1 -1
  13. package/src/GraphAddTrackWorkflow/buildTrackConfig.test.ts +4 -0
  14. package/src/GraphAddTrackWorkflow/buildTrackConfig.ts +13 -1
  15. package/src/GraphGenomeView/host.ts +26 -7
  16. package/src/GraphGenomeView/layoutModes.ts +10 -0
  17. package/src/GraphGenomeView/model.ts +15 -4
  18. package/src/LinearGraphDisplay/LinearGraphDisplay.test.ts +73 -21
  19. package/src/LinearGraphDisplay/components/LinearGraphDisplay.tsx +1 -0
  20. package/src/LinearGraphDisplay/model.ts +36 -0
  21. package/src/version.ts +1 -1
  22. package/dist/chunks/AddTrackWorkflow-TPCKXM2H.js +0 -2
  23. package/dist/chunks/LinearGraphDisplay-7POYIRIB.js +0 -2
  24. package/dist/chunks/chunk-A4ZWFDQY.js +0 -2
  25. /package/dist/chunks/{GraphGenomeView-WPKSNN4I.js.map → GraphGenomeView-BQYWWF6Z.js.map} +0 -0
  26. /package/dist/chunks/{chunk-SDTCVLC3.js.map → chunk-RSAY56Y4.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["global-externals:@mui/material/SvgIcon", "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", "../../src/GraphGenomeView/renderer/GeometryBuilder.ts", "../../src/GraphGenomeView/renderer/colorBits.ts", "../../src/GraphGenomeView/referenceSpan.ts", "../../src/GraphGenomeView/util/geometry.ts", "../../src/GraphGenomeView/deletionEdges.ts", "../../src/GraphGenomeView/util/edgeCurves.ts", "../../src/launchFromGraph/contributors.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/CompareArrows.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/HighlightAlt.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/LineStyle.mjs", "../../src/launchFromGraph/graphMenuItems.ts", "../../src/GraphGenomeView/bubbles/classifyBubble.ts", "../../src/GraphGenomeView/layout/mergeRuns.ts", "../../src/GraphGenomeView/layout/walkRows.ts", "../../src/GraphGenomeView/layout/walkRowLayout.ts", "../../src/alleleProjection/projectAlleles.ts", "../../src/GraphGenomeView/layout/placeOffReference.ts", "../../src/GraphGenomeView/layout/anchoredLayout.ts", "../../src/GraphGenomeView/layout/orderedLayout.ts", "../../src/GraphGenomeView/layout/sampleRowLayout.ts", "../../src/GraphGenomeView/layout/variantMapLayout.ts", "../../src/GraphGenomeView/layoutModes.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[\"@jbrowse/core/util/colorBits\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/canvas2dUtils\"];", "'use client';\n\nexport { createSvgIcon as default } from '@mui/material/SvgIcon';", "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\nconst 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// Half-extent of an arrowhead, in SCREEN px: the renderer expands it after the\n// transform (see TransformUniform), so an arrowhead is the same size at every\n// zoom.\nconst ARROWHEAD_SIZE = 12\nconst MIN_ARROW_SCALE = 0.45\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_COLOR = packAbgr(60, 65, 72, 255)\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// A cubic's tangent at t=1 runs from its last control point to its endpoint, so\n// an arrowhead's angle needs no tessellation. A control point sitting exactly on\n// the endpoint states no direction there; the chord is the only thing left.\n//\n// Exported for the test that checks it against a numerically differentiated\n// curve: arrow angles previously came from the last two tessellated points, and\n// this has to reproduce that direction to keep arrowheads pointing along the edge.\n// The angle an arrowhead points, which is a SCREEN angle: the head is expanded\n// from a screen-px size (addArrowhead offsets by `normal * size` after the\n// transform), so its direction has to be the drawn one too. `yToX` is what puts\n// the y difference in the same units as the x one; on an isotropic layout it is\n// 1 and the ratio inside atan2 is unchanged.\nexport function endTangent(c: BezierCurve, yToX = 1) {\n const dx = c.x1 - c.cx1\n const dy = (c.y1 - c.cy1) * yToX\n return Math.hypot(dx, dy) > 0\n ? Math.atan2(dy, dx)\n : Math.atan2((c.y1 - c.y0) * yToX, c.x1 - c.x0)\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 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 if (showArrows && arrowColor !== undefined) {\n const last = curves[curves.length - 1]!\n arrows.push({\n x: last.x1,\n y: last.y1,\n angle: endTangent(last, yToX),\n size: ARROWHEAD_SIZE,\n color: arrowColor,\n })\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 is a deep path absent from\n// ReExports/list.ts and therefore BUNDLED rather than bound to the host: safe on\n// every released JBrowse, whatever its own core exports.\n//\n// This module used to carry hand-copied versions of all of them, on the stated\n// grounds that the published `@jbrowse/core` does not export that subpath. That\n// is true and beside the point for a bundled path, and the copies cost nothing\n// to drop: esbuild tree-shakes the css colour parsing the rest of that module\n// pulls in, so the built plugin is byte-identical either way (37,723 B). What\n// the copies did cost is a second statement of the bit layout, which has to\n// agree with core's for a colour to survive a round trip through it.\n\n// Scale a packed color's channels, clamped at full brightness and leaving alpha\n// alone. factor === 1 returns the color unchanged.\n// The same colour at a fraction of its alpha, so a faded node reads as the\n// same ink through it on any background.\nexport function fadeAbgr(c: number, alpha: number) {\n return packAbgr(\n abgrRed(c),\n abgrGreen(c),\n abgrBlue(c),\n Math.round(abgrAlpha(c) * alpha),\n )\n}\n\nexport function brightenAbgr(c: number, factor: number) {\n return packAbgr(\n Math.min(255, Math.round(abgrRed(c) * factor)),\n Math.min(255, Math.round(abgrGreen(c) * factor)),\n Math.min(255, Math.round(abgrBlue(c) * factor)),\n abgrAlpha(c),\n )\n}\n", "import { isBackbone } from './anchoredNodes'\n\nimport type { Graph, GraphNode } from './types'\n\n// How long an allele chain can be and still be located from both ends. An\n// allele is a run of off-reference segments between two backbone flanks, so the\n// two flanks sit at *different* hop depths from any segment in the middle and\n// the walk has to keep going after it finds the first one. Minigraph alleles are\n// short runs, so this reaches both flanks throughout the fixtures; a longer run\n// degrades to the flanks it did reach rather than to nothing.\nconst MAX_BACKBONE_HOPS = 4\n\nexport interface ReferenceSpan {\n start: number\n end: number\n}\n\n// Undirected adjacency over node ids. Edges carry the topology in both\n// directions for this purpose: finding the backbone a node hangs off doesn't\n// care which way the L line was written.\nexport function buildNeighbors(graph: Graph) {\n const neighbors = new Map<string, string[]>()\n function add(from: string, to: string) {\n const list = neighbors.get(from)\n if (list) {\n list.push(to)\n } else {\n neighbors.set(from, [to])\n }\n }\n for (const edge of graph.edges) {\n add(edge.from, edge.to)\n add(edge.to, edge.from)\n }\n return neighbors\n}\n\n// The reference interval an allele branches into: between the end of the\n// backbone segment upstream of it and the start of the one downstream.\n// `[min(ends), max(starts)]` picks those two out of the reached flanks without\n// needing to know which is which, and it degrades usefully in both directions \u2014\n// a pure insertion, whose flanks abut, collapses to the single reference point\n// it is inserted at, and a bubble with only one flank inside the window yields\n// that flank's own span.\nfunction flankingSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}) {\n const seen = new Set([nodeId])\n let frontier = [nodeId]\n let minEnd = Infinity\n let maxStart = -Infinity\n\n // Backbone nodes are terminal \u2014 they are collected, never queued \u2014 so the walk\n // cannot cross the reference into a neighbouring bubble. It stays inside the\n // allele it started in and stops on its own at the flanks.\n for (let hop = 0; hop < MAX_BACKBONE_HOPS && frontier.length > 0; hop++) {\n const next: string[] = []\n for (const id of frontier) {\n const adjacent = neighbors.get(id)\n if (adjacent) {\n for (const neighborId of adjacent) {\n if (!seen.has(neighborId)) {\n seen.add(neighborId)\n const neighbor = nodeById.get(neighborId)\n if (neighbor && isBackbone(neighbor)) {\n minEnd = Math.min(minEnd, neighbor.stable.start + neighbor.length)\n maxStart = Math.max(maxStart, neighbor.stable.start)\n } else {\n next.push(neighborId)\n }\n }\n }\n }\n }\n frontier = next\n }\n\n return minEnd === Infinity\n ? undefined\n : {\n start: Math.min(minEnd, maxStart),\n end: Math.max(minEnd, maxStart),\n }\n}\n\n// The reference interval to highlight for a node, in the coordinates of the\n// stable sequence the subgraph was cut from.\n//\n// A rank-0 node states its own, so the interval is exact. A rank>0 allele has\n// none: its SO is an offset on a *different* stable sequence and is not\n// comparable to the reference axis (see the axis note in\n// layout/anchoredLayout.ts), so it is located by the backbone it branches from\n// instead.\nexport function nodeReferenceSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}): ReferenceSpan | undefined {\n const node = nodeById.get(nodeId)\n return node === undefined\n ? undefined\n : isBackbone(node)\n ? { start: node.stable.start, end: node.stable.start + node.length }\n : flankingSpan({ nodeId, nodeById, neighbors })\n}\n\n// Every node's midpoint on the reference, keyed by id: what a position-based\n// colour scheme ramps over. Alleles come through the same flanking walk hover\n// sync uses, so a bubble takes the colour of the reference interval it branches\n// from instead of dropping out of the ramp \u2014 which is the whole point, since an\n// allele is exactly what a linear view cannot draw at a position of its own.\nexport function referenceMidpoints(graph: Graph) {\n const nodeById = new Map(graph.nodes.map(node => [node.id, node]))\n const neighbors = buildNeighbors(graph)\n const midpoints = new Map<string, number>()\n for (const node of graph.nodes) {\n const span = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors })\n if (span) {\n midpoints.set(node.id, (span.start + span.end) / 2)\n }\n }\n return midpoints\n}\n", "import { ROW_HEIGHT_PX } from '../layout/rowSpacing'\n\nimport type { 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\nexport function computeEdgeCurves(\n fromSegments: NodeSegment[],\n toSegments: NodeSegment[],\n isSelfLoop: boolean,\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 isSelfLoop,\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 const sides = isSelfLoop\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 (isSelfLoop) {\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 = Math.min(50, 50 * 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 { bypassedPoints } from '../deletionEdges'\nimport { computeEdgeCurves } 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 edge.from === edge.to,\n 0,\n 0,\n axis,\n bypassed ? bypassedPoints(nodePositions, bypassed) : [],\n ),\n )\n }\n }\n cache.set(nodePositions, {\n graph,\n scaleX,\n scaleY,\n deletions,\n version,\n curves,\n })\n return curves\n}\n", "import { isAnchored } from '../GraphGenomeView/anchoredNodes'\nimport { panSNContig, panSNHaplotype, panSNSample } from '../pansn'\n\nimport type { AnchoredNode } from '../GraphGenomeView/anchoredNodes'\nimport type { Graph, GraphNode } from '../GraphGenomeView/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 '../../MinigraphBubbleAdapter/bubbleLine'\n\nimport type { MinigraphBubble } from '../../MinigraphBubbleAdapter/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.\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\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(b: MinigraphBubble): BubbleClass {\n const c = classifyShape(b)\n return b.partial ? { ...c, label: `${c.label}, partial` } : c\n}\n\nfunction classifyShape(b: MinigraphBubble): 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 if (pathCount >= 8 && longest > 5 * Math.max(shortest, 1)) {\n return {\n kind: 'repeat',\n label: `${range} repeat array, ${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 { isBackbone } from '../anchoredNodes'\n\nimport type { Graph, 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 that node's only in-edge is the run's\n// only out-edge. A self loop counts on both sides and so 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\nexport function mergeRuns(graph: Graph): MergedRuns {\n const outOf = new Map<string, string[]>()\n const inOf = new Map<string, string[]>()\n const push = (map: Map<string, string[]>, key: string, value: string) => {\n const list = map.get(key)\n if (list) {\n list.push(value)\n } else {\n map.set(key, [value])\n }\n }\n for (const edge of graph.edges) {\n push(outOf, edge.from, edge.to)\n push(inOf, edge.to, edge.from)\n }\n const joins = (a: string, b: string) =>\n a !== b && outOf.get(a)?.length === 1 && inOf.get(b)?.length === 1\n const runOf = new Map<string, string>()\n const runs = new Map<string, string[]>()\n for (const node of graph.nodes) {\n const pred = inOf.get(node.id)\n if (\n runOf.has(node.id) ||\n (pred?.length === 1 && joins(pred[0]!, node.id))\n ) {\n continue\n }\n const run = [node.id]\n for (;;) {\n const next = outOf.get(run.at(-1)!)?.[0]\n if (next === undefined || runOf.has(next) || !joins(run.at(-1)!, next)) {\n break\n }\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 const internal = new Set<string>()\n for (const run of runs.values()) {\n for (let i = 1; i < run.length; i++) {\n internal.add(`${run[i - 1]}>${run[i]}`)\n }\n }\n const seen = new Set<string>()\n const edges = []\n for (const edge of graph.edges) {\n const key = `${runOf.get(edge.from)}>${runOf.get(edge.to)}`\n if (internal.has(`${edge.from}>${edge.to}`) || seen.has(key)) {\n continue\n }\n seen.add(key)\n edges.push({ from: runOf.get(edge.from)!, to: runOf.get(edge.to)! })\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", "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 purple 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}\n\nexport interface WalkRow {\n name: string\n label: string\n sample: string\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 onReference = new Set(reference.nodeIds)\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 for (const id of ids) {\n const len = lengthOf.get(id) ?? 0\n const shared = onReference.has(id)\n const last = runs.at(-1)\n if (last?.onReference === shared) {\n last.bp += len\n } else {\n runs.push({ start: bp, bp: len, onReference: shared })\n }\n bp += len\n if (!shared) {\n offReferenceBp += len\n }\n }\n return {\n name: path.name,\n label: labelOf(path),\n sample: sampleOf(path),\n bp,\n offReferenceBp,\n complete,\n runs,\n }\n }\n\n const origin = cut ? cut.start : referenceStart\n return {\n origin,\n unit,\n reference: rowOf(reference),\n rows: paths\n .filter(p => p !== reference)\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 { REFERENCE_RANK, isAnchored } from '../GraphGenomeView/anchoredNodes'\nimport { buildNeighbors } from '../GraphGenomeView/referenceSpan'\n\nimport type { AnchoredNode } from '../GraphGenomeView/anchoredNodes'\nimport type { Graph } from '../GraphGenomeView/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 (docs/layout-experiments.md, experiment 3).\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", "import { backboneNodes, backbonePositions } from '../anchoredNodes'\n\nimport type { Graph, LayoutResult } from '../types'\n\n// The reference alone, at its bp, as one line. Everything off the reference is\n// left unplaced, so the renderer draws no allele nodes: the variation is drawn\n// instead by the bubble overlay as one typed glyph per bubble, from the bubble\n// index. Links between placed backbone nodes still draw, which is how a\n// deletion keeps its arc.\nexport function variantMapLayout(graph: Graph): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n if (backbone.length === 0) {\n return undefined\n }\n return {\n nodePositions: backbonePositions(backbone),\n referenceAxis: true,\n pixelRows: true,\n }\n}\n", "import { isBackbone, isOffReference } from './anchoredNodes'\nimport { anchoredLayout } from './layout/anchoredLayout'\nimport { orderedLayout } from './layout/orderedLayout'\nimport { sampleRowLayout } from './layout/sampleRowLayout'\nimport { variantMapLayout } from './layout/variantMapLayout'\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 `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 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 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 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 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 available: hasBackbone,\n drawsLocally: hasBackbone,\n },\n {\n value: 'variants',\n label: 'Variant map',\n description:\n 'The reference as a line with one typed glyph per bubble, from the bubble index or from the graph itself: SNP, indel, deletion, inversion, repeat array. Click a glyph to open the graph inside it.',\n run: variantMapLayout,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n },\n {\n value: 'force',\n label: FORCE_LAYOUT_LABEL,\n description: 'OGDF FMMM, via the external Bandage engine.',\n run: () => undefined,\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 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\n// The walks that span the array against the alleles reads spanned, then\n// everything else against what those two left. It decides the ambiguous cases\n// on merit: which walk carries the one allele a lone spanning read supports,\n// and which allele a walk that stops inside the array is drawn against.\nfunction pairSample<R extends { bp: number; complete: boolean }>(\n rows: R[],\n calls: RepeatCall[],\n) {\n const out = new Map<number, WalkCall>()\n const taken = new Set<RepeatCall>()\n const ranked = byLength(rows, r => r.bp)\n const stages = [\n [ranked.filter(r => r.item.complete), calls.filter(backed)],\n [ranked, calls],\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, {\n ...call,\n agrees:\n row.item.complete && backed(call)\n ? agrees(row.item.bp, call.bp)\n : undefined,\n })\n }\n })\n }\n return out\n}\n\nexport function withCalls<\n R extends { sample: string; bp: number; complete: boolean },\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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",
4
+ "sourcesContent": ["module.exports = JBrowseExports[\"@mui/material/SvgIcon\"];", "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 { 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\nconst 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// Half-extent of an arrowhead, in SCREEN px: the renderer expands it after the\n// transform (see TransformUniform), so an arrowhead is the same size at every\n// zoom.\nconst ARROWHEAD_SIZE = 12\nconst MIN_ARROW_SCALE = 0.45\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_COLOR = packAbgr(60, 65, 72, 255)\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// A cubic's tangent at t=1 runs from its last control point to its endpoint, so\n// an arrowhead's angle needs no tessellation. A control point sitting exactly on\n// the endpoint states no direction there; the chord is the only thing left.\n//\n// Exported for the test that checks it against a numerically differentiated\n// curve: arrow angles previously came from the last two tessellated points, and\n// this has to reproduce that direction to keep arrowheads pointing along the edge.\n// The angle an arrowhead points, which is a SCREEN angle: the head is expanded\n// from a screen-px size (addArrowhead offsets by `normal * size` after the\n// transform), so its direction has to be the drawn one too. `yToX` is what puts\n// the y difference in the same units as the x one; on an isotropic layout it is\n// 1 and the ratio inside atan2 is unchanged.\nexport function endTangent(c: BezierCurve, yToX = 1) {\n const dx = c.x1 - c.cx1\n const dy = (c.y1 - c.cy1) * yToX\n return Math.hypot(dx, dy) > 0\n ? Math.atan2(dy, dx)\n : Math.atan2((c.y1 - c.y0) * yToX, c.x1 - c.x0)\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 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 if (showArrows && arrowColor !== undefined) {\n const last = curves[curves.length - 1]!\n arrows.push({\n x: last.x1,\n y: last.y1,\n angle: endTangent(last, yToX),\n size: ARROWHEAD_SIZE,\n color: arrowColor,\n })\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 is a deep path absent from\n// ReExports/list.ts and therefore BUNDLED rather than bound to the host: safe on\n// every released JBrowse, whatever its own core exports.\n//\n// This module used to carry hand-copied versions of all of them, on the stated\n// grounds that the published `@jbrowse/core` does not export that subpath. That\n// is true and beside the point for a bundled path, and the copies cost nothing\n// to drop: esbuild tree-shakes the css colour parsing the rest of that module\n// pulls in, so the built plugin is byte-identical either way (37,723 B). What\n// the copies did cost is a second statement of the bit layout, which has to\n// agree with core's for a colour to survive a round trip through it.\n\n// Scale a packed color's channels, clamped at full brightness and leaving alpha\n// alone. factor === 1 returns the color unchanged.\n// The same colour at a fraction of its alpha, so a faded node reads as the\n// same ink through it on any background.\nexport function fadeAbgr(c: number, alpha: number) {\n return packAbgr(\n abgrRed(c),\n abgrGreen(c),\n abgrBlue(c),\n Math.round(abgrAlpha(c) * alpha),\n )\n}\n\nexport function brightenAbgr(c: number, factor: number) {\n return packAbgr(\n Math.min(255, Math.round(abgrRed(c) * factor)),\n Math.min(255, Math.round(abgrGreen(c) * factor)),\n Math.min(255, Math.round(abgrBlue(c) * factor)),\n abgrAlpha(c),\n )\n}\n", "import { isBackbone } from './anchoredNodes'\n\nimport type { Graph, GraphNode } from './types'\n\n// How long an allele chain can be and still be located from both ends. An\n// allele is a run of off-reference segments between two backbone flanks, so the\n// two flanks sit at *different* hop depths from any segment in the middle and\n// the walk has to keep going after it finds the first one. Minigraph alleles are\n// short runs, so this reaches both flanks throughout the fixtures; a longer run\n// degrades to the flanks it did reach rather than to nothing.\nconst MAX_BACKBONE_HOPS = 4\n\nexport interface ReferenceSpan {\n start: number\n end: number\n}\n\n// Undirected adjacency over node ids. Edges carry the topology in both\n// directions for this purpose: finding the backbone a node hangs off doesn't\n// care which way the L line was written.\nexport function buildNeighbors(graph: Graph) {\n const neighbors = new Map<string, string[]>()\n function add(from: string, to: string) {\n const list = neighbors.get(from)\n if (list) {\n list.push(to)\n } else {\n neighbors.set(from, [to])\n }\n }\n for (const edge of graph.edges) {\n add(edge.from, edge.to)\n add(edge.to, edge.from)\n }\n return neighbors\n}\n\n// The reference interval an allele branches into: between the end of the\n// backbone segment upstream of it and the start of the one downstream.\n// `[min(ends), max(starts)]` picks those two out of the reached flanks without\n// needing to know which is which, and it degrades usefully in both directions \u2014\n// a pure insertion, whose flanks abut, collapses to the single reference point\n// it is inserted at, and a bubble with only one flank inside the window yields\n// that flank's own span.\nfunction flankingSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}) {\n const seen = new Set([nodeId])\n let frontier = [nodeId]\n let minEnd = Infinity\n let maxStart = -Infinity\n\n // Backbone nodes are terminal \u2014 they are collected, never queued \u2014 so the walk\n // cannot cross the reference into a neighbouring bubble. It stays inside the\n // allele it started in and stops on its own at the flanks.\n for (let hop = 0; hop < MAX_BACKBONE_HOPS && frontier.length > 0; hop++) {\n const next: string[] = []\n for (const id of frontier) {\n const adjacent = neighbors.get(id)\n if (adjacent) {\n for (const neighborId of adjacent) {\n if (!seen.has(neighborId)) {\n seen.add(neighborId)\n const neighbor = nodeById.get(neighborId)\n if (neighbor && isBackbone(neighbor)) {\n minEnd = Math.min(minEnd, neighbor.stable.start + neighbor.length)\n maxStart = Math.max(maxStart, neighbor.stable.start)\n } else {\n next.push(neighborId)\n }\n }\n }\n }\n }\n frontier = next\n }\n\n return minEnd === Infinity\n ? undefined\n : {\n start: Math.min(minEnd, maxStart),\n end: Math.max(minEnd, maxStart),\n }\n}\n\n// The reference interval to highlight for a node, in the coordinates of the\n// stable sequence the subgraph was cut from.\n//\n// A rank-0 node states its own, so the interval is exact. A rank>0 allele has\n// none: its SO is an offset on a *different* stable sequence and is not\n// comparable to the reference axis (see the axis note in\n// layout/anchoredLayout.ts), so it is located by the backbone it branches from\n// instead.\nexport function nodeReferenceSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}): ReferenceSpan | undefined {\n const node = nodeById.get(nodeId)\n return node === undefined\n ? undefined\n : isBackbone(node)\n ? { start: node.stable.start, end: node.stable.start + node.length }\n : flankingSpan({ nodeId, nodeById, neighbors })\n}\n\n// Every node's midpoint on the reference, keyed by id: what a position-based\n// colour scheme ramps over. Alleles come through the same flanking walk hover\n// sync uses, so a bubble takes the colour of the reference interval it branches\n// from instead of dropping out of the ramp \u2014 which is the whole point, since an\n// allele is exactly what a linear view cannot draw at a position of its own.\nexport function referenceMidpoints(graph: Graph) {\n const nodeById = new Map(graph.nodes.map(node => [node.id, node]))\n const neighbors = buildNeighbors(graph)\n const midpoints = new Map<string, number>()\n for (const node of graph.nodes) {\n const span = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors })\n if (span) {\n midpoints.set(node.id, (span.start + span.end) / 2)\n }\n }\n return midpoints\n}\n", "import { ROW_HEIGHT_PX } from '../layout/rowSpacing'\n\nimport type { 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\nexport function computeEdgeCurves(\n fromSegments: NodeSegment[],\n toSegments: NodeSegment[],\n isSelfLoop: boolean,\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 isSelfLoop,\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 const sides = isSelfLoop\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 (isSelfLoop) {\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 = Math.min(50, 50 * 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 { bypassedPoints } from '../deletionEdges'\nimport { computeEdgeCurves } 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 edge.from === edge.to,\n 0,\n 0,\n axis,\n bypassed ? bypassedPoints(nodePositions, bypassed) : [],\n ),\n )\n }\n }\n cache.set(nodePositions, {\n graph,\n scaleX,\n scaleY,\n deletions,\n version,\n curves,\n })\n return curves\n}\n", "import { isAnchored } from '../GraphGenomeView/anchoredNodes'\nimport { panSNContig, panSNHaplotype, panSNSample } from '../pansn'\n\nimport type { AnchoredNode } from '../GraphGenomeView/anchoredNodes'\nimport type { Graph, GraphNode } from '../GraphGenomeView/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 '../../MinigraphBubbleAdapter/bubbleLine'\n\nimport type { MinigraphBubble } from '../../MinigraphBubbleAdapter/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.\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\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(b: MinigraphBubble): BubbleClass {\n const c = classifyShape(b)\n return b.partial ? { ...c, label: `${c.label}, partial` } : c\n}\n\nfunction classifyShape(b: MinigraphBubble): 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 if (pathCount >= 8 && longest > 5 * Math.max(shortest, 1)) {\n return {\n kind: 'repeat',\n label: `${range} repeat array, ${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 { isBackbone } from '../anchoredNodes'\n\nimport type { Graph, 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 that node's only in-edge is the run's\n// only out-edge. A self loop counts on both sides and so 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\nexport function mergeRuns(graph: Graph): MergedRuns {\n const outOf = new Map<string, string[]>()\n const inOf = new Map<string, string[]>()\n const push = (map: Map<string, string[]>, key: string, value: string) => {\n const list = map.get(key)\n if (list) {\n list.push(value)\n } else {\n map.set(key, [value])\n }\n }\n for (const edge of graph.edges) {\n push(outOf, edge.from, edge.to)\n push(inOf, edge.to, edge.from)\n }\n const joins = (a: string, b: string) =>\n a !== b && outOf.get(a)?.length === 1 && inOf.get(b)?.length === 1\n const runOf = new Map<string, string>()\n const runs = new Map<string, string[]>()\n for (const node of graph.nodes) {\n const pred = inOf.get(node.id)\n if (\n runOf.has(node.id) ||\n (pred?.length === 1 && joins(pred[0]!, node.id))\n ) {\n continue\n }\n const run = [node.id]\n for (;;) {\n const next = outOf.get(run.at(-1)!)?.[0]\n if (next === undefined || runOf.has(next) || !joins(run.at(-1)!, next)) {\n break\n }\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 const internal = new Set<string>()\n for (const run of runs.values()) {\n for (let i = 1; i < run.length; i++) {\n internal.add(`${run[i - 1]}>${run[i]}`)\n }\n }\n const seen = new Set<string>()\n const edges = []\n for (const edge of graph.edges) {\n const key = `${runOf.get(edge.from)}>${runOf.get(edge.to)}`\n if (internal.has(`${edge.from}>${edge.to}`) || seen.has(key)) {\n continue\n }\n seen.add(key)\n edges.push({ from: runOf.get(edge.from)!, to: runOf.get(edge.to)! })\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", "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 purple 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}\n\nexport interface WalkRow {\n name: string\n label: string\n sample: string\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 onReference = new Set(reference.nodeIds)\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 for (const id of ids) {\n const len = lengthOf.get(id) ?? 0\n const shared = onReference.has(id)\n const last = runs.at(-1)\n if (last?.onReference === shared) {\n last.bp += len\n } else {\n runs.push({ start: bp, bp: len, onReference: shared })\n }\n bp += len\n if (!shared) {\n offReferenceBp += len\n }\n }\n return {\n name: path.name,\n label: labelOf(path),\n sample: sampleOf(path),\n bp,\n offReferenceBp,\n complete,\n runs,\n }\n }\n\n const origin = cut ? cut.start : referenceStart\n return {\n origin,\n unit,\n reference: rowOf(reference),\n rows: paths\n .filter(p => p !== reference)\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 { REFERENCE_RANK, isAnchored } from '../GraphGenomeView/anchoredNodes'\nimport { buildNeighbors } from '../GraphGenomeView/referenceSpan'\n\nimport type { AnchoredNode } from '../GraphGenomeView/anchoredNodes'\nimport type { Graph } from '../GraphGenomeView/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 (docs/layout-experiments.md, experiment 3).\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", "import { backboneNodes, backbonePositions } from '../anchoredNodes'\n\nimport type { Graph, LayoutResult } from '../types'\n\n// The reference alone, at its bp, as one line. Everything off the reference is\n// left unplaced, so the renderer draws no allele nodes: the variation is drawn\n// instead by the bubble overlay as one typed glyph per bubble, from the bubble\n// index. Links between placed backbone nodes still draw, which is how a\n// deletion keeps its arc.\nexport function variantMapLayout(graph: Graph): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n if (backbone.length === 0) {\n return undefined\n }\n return {\n nodePositions: backbonePositions(backbone),\n referenceAxis: true,\n pixelRows: true,\n }\n}\n", "import { isBackbone, isOffReference } from './anchoredNodes'\nimport { anchoredLayout } from './layout/anchoredLayout'\nimport { orderedLayout } from './layout/orderedLayout'\nimport { sampleRowLayout } from './layout/sampleRowLayout'\nimport { variantMapLayout } from './layout/variantMapLayout'\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 the drawing puts reference bp on x, so a host places it and a cut\n // carries margins to pan over. A mode without one draws its own picture of\n // the window, so its cut is the window alone.\n referenceAxis: 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 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 referenceAxis: 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 referenceAxis: 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 referenceAxis: true,\n available: hasWalks,\n drawsLocally: hasWalks,\n },\n {\n value: 'ordered',\n label: 'Ordered',\n description:\n 'x is reference order, not bp: every node gets room, bubbles read as lenses. Needs rGFA tags or a reference path.',\n run: orderedLayout,\n referenceAxis: false,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n },\n {\n value: 'variants',\n label: 'Variant map',\n description:\n 'The reference as a line with one typed glyph per bubble, from the bubble index or from the graph itself: SNP, indel, deletion, inversion, repeat array. Click a glyph to open the graph inside it.',\n run: variantMapLayout,\n referenceAxis: true,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n },\n {\n value: 'force',\n label: FORCE_LAYOUT_LABEL,\n description: 'OGDF FMMM, via the external Bandage engine.',\n run: () => undefined,\n referenceAxis: 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 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\n// The walks that span the array against the alleles reads spanned, then\n// everything else against what those two left. It decides the ambiguous cases\n// on merit: which walk carries the one allele a lone spanning read supports,\n// and which allele a walk that stops inside the array is drawn against.\nfunction pairSample<R extends { bp: number; complete: boolean }>(\n rows: R[],\n calls: RepeatCall[],\n) {\n const out = new Map<number, WalkCall>()\n const taken = new Set<RepeatCall>()\n const ranked = byLength(rows, r => r.bp)\n const stages = [\n [ranked.filter(r => r.item.complete), calls.filter(backed)],\n [ranked, calls],\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, {\n ...call,\n agrees:\n row.item.complete && backed(call)\n ? agrees(row.item.bp, call.bp)\n : undefined,\n })\n }\n })\n }\n return out\n}\n\nexport function withCalls<\n R extends { sample: string; bp: number; complete: boolean },\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
6
  "names": ["require_SvgIcon", "__commonJSMin", "exports", "module", "require_colorBits", "__commonJSMin", "exports", "module", "require_canvas2dUtils", "__commonJSMin", "exports", "module", "import_SvgIcon", "import_colorBits", "import_colorBits", "fadeAbgr", "c", "alpha", "brightenAbgr", "factor", "MAX_BACKBONE_HOPS", "buildNeighbors", "graph", "neighbors", "add", "from", "to", "list", "edge", "flankingSpan", "nodeId", "nodeById", "seen", "frontier", "minEnd", "maxStart", "hop", "next", "id", "adjacent", "neighborId", "neighbor", "isBackbone", "nodeReferenceSpan", "node", "referenceMidpoints", "midpoints", "span", "svgPath", "points", "round", "v", "p", "i", "projectLine", "x1", "y1", "x2", "y2", "distance", "d", "vx", "vy", "curvePointAt", "c", "u", "a", "b", "e", "curveMidpoint", "curves", "mid", "curveBounds", "minX", "minY", "maxX", "maxY", "subCurve", "t0", "t1", "p0", "p1", "dt", "d0", "curveTangentAt", "d1", "dashCurves", "dashWorld", "out", "polyLen", "spans", "translateCurves", "dx", "dy", "attachment", "segments", "side", "last", "TANGENT_SPAN_FACTOR", "MAX_TANGENT", "spanOf", "min", "max", "s", "distanceToSpan", "x", "span", "facingSide", "other", "tieBreak", "startGap", "endGap", "facingSides", "fromSegments", "toSegments", "APEX_FRACTION", "BOW_CLEARANCE", "ALONG_FRACTION", "MAX_ROW_BOW_PX", "bowAround", "p1x", "p1y", "p2x", "p2y", "toward", "maxBow", "chord", "ux", "uy", "reach", "minAlong", "maxAlong", "perp", "along", "uncapped", "size", "yToXOf", "scaleX", "scaleY", "PATH_RIBBON_SPACING", "drawnDirection", "yToX", "len", "pathRibbonOffsets", "count", "axis", "spacing", "dir", "offsets", "scaleYOf", "unscaleYOf", "computeEdgeCurves", "isSelfLoop", "offsetX", "offsetY", "bypassed", "maxBowIn", "scale", "sides", "fromAttach", "toAttach", "fromEnd", "toStart", "segDirX", "segDirY", "ext", "perpX", "perpY", "midX", "midY", "cp1x", "cp1y", "cp2x", "cp2y", "fromPrev", "toNext", "dist", "tangentProj", "prev", "at", "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", "cache", "baseEdgeCurves", "nodePositions", "graph", "axis", "deletions", "version", "scaleX", "scaleY", "cached", "curves", "ei", "edge", "from", "to", "bypassed", "computeEdgeCurves", "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", "ARROWHEAD_SIZE", "MIN_ARROW_SCALE", "pointNormalsOf", "rawPoints", "yToX", "points", "p", "pointNormals", "i", "nx", "ny", "dx", "dy", "len", "dx1", "dy1", "len1", "dx2", "dy2", "len2", "nx1", "ny1", "nx2", "ny2", "dot", "offsetPolyline", "normals", "distance", "packNorm", "r", "g", "b", "a", "hslToRgb", "h", "s", "l", "c", "x", "m", "DEPTH_GRADIENT", "STABLE_RANK_GRADIENT", "NODE_LENGTH_GRADIENT", "sampleGradient", "stops", "pos", "f", "hashColor", "str", "alpha", "nameHue", "PATH_SATURATION", "PATH_LIGHTNESS", "REFERENCE_RAMP_MAX_HUE", "REFERENCE_RAMP_SATURATION", "REFERENCE_RAMP_LIGHTNESS", "REFERENCE_RAMP_ALT_COLOR", "computeReferenceRamp", "graph", "domain", "midpoints", "referenceMidpoints", "min", "max", "mid", "computeColorSchemeRange", "minDepth", "maxDepth", "minLength", "maxLength", "maxRank", "n", "getNodeColor", "node", "nodeIndex", "colorScheme", "range", "ramp", "frac", "hue", "endTangent", "isPolylineInBounds", "segments", "bounds", "hit", "isBezierInBounds", "curves", "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", "scale", "yToXOf", "sharedCurves", "baseEdgeCurves", "nodeStrokes", "nodeStrokeRuns", "arrows", "arrowRuns", "edgeCurves", "edgeCurveRuns", "colorRange", "nodeIndexMap", "pathCount", "pathColorByName", "name", "nodePathSlots", "showArrows", "ei", "edge", "fromSegments", "toSegments", "baseCurves", "numPaths", "isDeletion", "onWalk", "edgeThickness", "ribbons", "plainEdgeColor", "edgeColor", "fadeAbgr", "buildSingleEdge", "offsetX", "offsetY", "color", "arrowColor", "translateCurves", "dash", "dashCurves", "last", "edgeCurveStart", "arrowStart", "offsets", "pathRibbonOffsets", "arrowRibbon", "offset", "ribbonColor", "edgeCurveCount", "arrowCount", "own", "faded", "width", "nodeWidthPx", "nodeThickness", "start", "slotWidth", "drawable", "worldPerScreenPx", "slot", "stripe", "count", "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", "SUPERBUBBLE_SEGMENTS", "formatBp", "bp", "formatBpRange", "shortest", "longest", "low", "high", "unit", "routes", "count", "SATURATED_PATH_COUNT", "classifyBubble", "b", "c", "classifyShape", "refSpan", "pathCount", "segmentCount", "inversion", "range", "alleles", "BUBBLE_KIND_COLORS", "BUBBLE_KIND_NAMES", "bubbleSegmentIds", "mergeRuns", "graph", "outOf", "inOf", "push", "map", "key", "value", "list", "edge", "joins", "a", "b", "runOf", "runs", "node", "pred", "run", "next", "id", "byId", "n", "nodes", "members", "backbone", "isBackbone", "first", "sum", "internal", "i", "seen", "edges", "splitRuns", "positions", "weightOf", "out", "line", "arc", "arcPrefix", "total", "weights", "m", "weightSum", "from", "member", "k", "share", "to", "slice", "polylineSlice", "polylineMidpoint", "pointAt", "s", "span", "t", "points", "sampleOf", "path", "panSNSample", "labelOf", "sliceBetween", "nodeIds", "span", "region", "flanked", "i0", "i1", "bestEnd", "bestStart", "id", "i", "s", "ids", "walkRows", "graph", "unit", "paths", "reference", "pathOrigin", "lengthOf", "n", "onReference", "referenceStart", "cut", "pos", "len", "rowOf", "complete", "runs", "bp", "offReferenceBp", "shared", "last", "a", "b", "READOUT_ROOM", "walkRowsExtent", "walks", "longest", "row", "walkRowLayout", "graph", "region", "backbone", "backboneNodes", "walkRows", "nodePositions", "backbonePositions", "rowLabels", "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", "variantMapLayout", "graph", "backbone", "backboneNodes", "backbonePositions", "FORCE_LAYOUT_LABEL", "hasBackbone", "graph", "isBackbone", "hasWalks", "hasAlleles", "isOffReference", "LAYOUT_MODES", "anchoredLayout", "sampleRowLayout", "walkRowLayout", "orderedLayout", "variantMapLayout", "LAYOUT_MODE_VALUES", "m", "modeUsesLayoutEngine", "value", "layoutModeByValue", "agrees", "walkBp", "callBp", "nearest", "walks", "calls", "W", "C", "cost", "paired", "j", "together", "dropWalk", "dropCall", "out", "byLength", "items", "bp", "item", "i", "a", "b", "backed", "call", "pairSample", "rows", "taken", "ranked", "r", "stages", "pool", "group", "free", "open", "c", "pick", "k", "row", "withCalls", "bySample", "sample", "called", "import_jsx_runtime", "Settings_default", "_jsx"]
7
7
  }