jbrowse-plugin-graphgenomeviewer 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["global-externals:@mui/material/SvgIcon", "global-externals:@jbrowse/core/util/colorBits", "global-externals:mobx", "global-externals:@jbrowse/render-core/canvas2dUtils", "global-externals:@jbrowse/render-core/renderingBackendBase", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/utils/createSvgIcon.mjs", "../../packages/core/src/labelLayout.ts", "../../packages/core/src/renderer/GeometryBuilder.ts", "../../packages/core/src/renderer/colorBits.ts", "../../packages/core/src/referenceSpan.ts", "../../packages/core/src/util/edgeCurves.ts", "../../packages/core/src/referenceRampCss.ts", "../../src/launchFromGraph/contributors.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/CompareArrows.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/HighlightAlt.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/LineStyle.mjs", "../../src/launchFromGraph/graphMenuItems.ts", "../../packages/core/src/tubeMap/reads.ts", "../../packages/core/src/alleleProjection/projectAlleles.ts", "../../packages/core/src/layout/placeOffReference.ts", "../../packages/core/src/layout/anchoredLayout.ts", "../../packages/core/src/layout/orderedLayout.ts", "../../packages/core/src/layout/sampleRowLayout.ts", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/types.js", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/layout.js", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/geometry.js", "../../packages/core/src/layout/tubeMapLayout.ts", "../../packages/core/src/layout/walkRows.ts", "../../packages/core/src/layout/walkRowLayout.ts", "../../packages/core/src/layoutModes.ts", "../../packages/core/src/bubbles/classifyBubble.ts", "../../packages/core/src/facetGrid.ts", "../../packages/core/src/el.ts", "../../packages/core/src/layout/walkRowDraw.ts", "../../packages/core/src/renderer/Canvas2DRenderer.ts", "../../packages/core/src/walkEncoding.ts", "../../packages/core/src/walkKey.ts", "../../packages/core/src/layout/walkStrip.ts", "../../packages/core/src/referenceStrip.ts", "../../packages/core/src/tubeMap/axis.ts", "../../packages/core/src/tubeMap/connectors.ts", "../../packages/core/src/tubeMap/deviations.ts", "../../packages/core/src/tubeMap/mismatches.ts", "../../packages/core/src/tubeMap/draw.ts", "../../packages/core/src/tubeMap/genes.ts", "../../src/GraphGenomeView/repeats/walkCalls.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/Settings.mjs"],
4
- "sourcesContent": ["module.exports = JBrowseExports[\"@mui/material/SvgIcon\"];", "module.exports = JBrowseExports[\"@jbrowse/core/util/colorBits\"];", "module.exports = JBrowseExports[\"mobx\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/canvas2dUtils\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/renderingBackendBase\"];", "'use client';\n\nexport { createSvgIcon as default } from '@mui/material/SvgIcon';", "import {\n nodeLabelBudget,\n placeSizeLabels,\n rowLabelBox,\n sizeLabelCandidates,\n} from './graphLabels'\nimport {\n LABEL_CHAR_PX,\n LABEL_PAD,\n LABEL_PX,\n occupancy,\n placeLabels,\n} from './overlayLabels'\n\nimport type { BubbleHalo, RouteLabel } from './bubbles/bubbleHalos'\nimport type { DeletionEdge } from './deletionEdges'\nimport type { GenePin } from './genes/genePins'\nimport type { GraphLabel } from './graphLabels'\nimport type { DeletionRoutes } from './layout/deletionRoutes'\nimport type { Box, PlacedLabel } from './overlayLabels'\nimport type { AlleleDeletion, NodeSegment } from './types'\nimport type { AxisScale } from './util/geometry'\n\nexport const HALO_FACTOR = 3.4\nexport const LEGEND_INSET_PX = 6\n// routes whose own stretches are drawn on top of each other stack their chips\nconst ROUTE_STACK = 8\nconst GENE_PIN_DROP_PX = 18\n// a gene's name drops a row or two rather than vanish under a bubble's\nconst GENE_STACK = 2\n// a chip's baseline this far down is the highest it sits whole in the pane\nconst TOPMOST_BASELINE = LABEL_PX + LABEL_PAD\n\nexport interface LabelLayoutSource {\n paneWidth: number\n canvasHeight: number\n axisScale: AxisScale\n translateX: number\n translateY: number\n contigThickness: number\n legendSize: { width: number; height: number }\n // the band along the top a reference strip draws in\n referenceStripZonePx?: number\n drawnRowLabels: { label: string; y: number }[]\n bubbleHalos: BubbleHalo[]\n genePins: GenePin[]\n poppedFrom?: { label: string }\n nodePositions?: Record<string, NodeSegment[]>\n // false where the drawing is not the canvas's nodes (the tube map), so a\n // length label would sit on a box's tubes rather than beside a node\n labelsNodeSizes: boolean\n nodeLengths: Map<string, number>\n showDeletionEdges: boolean\n deletions: DeletionEdge[]\n deletionRoutes?: DeletionRoutes\n alleleDeletions: AlleleDeletion[]\n positionsVersion: number\n}\n\nexport interface LabelLayout {\n bubbles: PlacedLabel<BubbleHalo>[]\n genes: PlacedLabel<GenePin>[]\n routes: PlacedLabel<{ halo: BubbleHalo; route: RouteLabel }>[]\n sizes: GraphLabel[]\n}\n\n// How much of a gene the cut's backbone carries, when that is not all of it,\n// in the unit of the gene's length: `35 of 132.8 kb`\nexport function geneCoverageNote(pin: GenePin) {\n const length = pin.gene.end - pin.gene.start\n if (pin.covered >= 0.98) {\n return undefined\n }\n const [per, unit] =\n length >= 1_000_000\n ? [1_000_000, 'Mb']\n : length >= 1000\n ? [1000, 'kb']\n : [1, 'bp']\n const amount = (bp: number) => +(bp / per).toFixed(per === 1 ? 0 : 1)\n return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`\n}\n\n// Each gene's name under its pin, the longest gene first, with how much of\n// it the cut carries where that is not all of it\nexport function geneLabelCandidates(\n pins: GenePin[],\n screen: (p: { x: number; y: number }) => { x: number; y: number },\n contigThickness: number,\n) {\n return byExtent(pins, pin => pin.gene.end - pin.gene.start).map(pin => {\n const { x, y } = screen(pin.at)\n const note = geneCoverageNote(pin)\n return {\n item: pin,\n x,\n y: y + contigThickness + GENE_PIN_DROP_PX,\n text: note ? `${pin.gene.name} \u00B7 ${note}` : pin.gene.name,\n fallback: note ? `${pin.gene.name} \u2026` : undefined,\n stack: GENE_STACK,\n }\n })\n}\n\nfunction byExtent<T>(items: T[], extent: (item: T) => number) {\n return [...items].sort((a, b) => extent(b) - extent(a))\n}\n\n// The order is what survives a crowd: the bubble's name, then the gene's, then\n// what a deletion skips, then who takes each route, and a node's length last.\nexport function layoutLabels(m: LabelLayoutSource): LabelLayout {\n const { paneWidth: width, canvasHeight: height, translateX, translateY } = m\n const { scaleX, scaleY } = m.axisScale\n const frame = { width, height }\n const screen = (p: { x: number; y: number }) => ({\n x: p.x * scaleX + translateX,\n y: p.y * scaleY + translateY,\n })\n const reserved: Box[] = m.drawnRowLabels.map(({ label, y }) =>\n rowLabelBox(label, y * scaleY + translateY),\n )\n const stripPx = m.referenceStripZonePx ?? 0\n if (stripPx > 0) {\n reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx })\n }\n if (m.legendSize.width > 0) {\n reserved.push({\n x0: width - LEGEND_INSET_PX - m.legendSize.width,\n x1: width,\n y0: 0,\n y1: stripPx + LEGEND_INSET_PX + m.legendSize.height,\n })\n }\n if (m.poppedFrom) {\n reserved.push({\n x0: 0,\n x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,\n y0: 0,\n y1: 44,\n })\n }\n const take = occupancy(frame, reserved)\n\n // On a row layout a bubble's name goes in the band above the top row, which\n // is the reference it varies, and stays at the pane's top edge once that row\n // scrolls out. Above its own nodes, a lower row's allele would put it on the\n // reference line.\n const onRows = m.drawnRowLabels.length > 0\n const rowsTop = onRows\n ? Math.min(...m.drawnRowLabels.map(r => r.y))\n : Infinity\n const halo = m.contigThickness * HALO_FACTOR\n const byBubble = byExtent(m.bubbleHalos, h => h.members)\n const bubbles = placeLabels(\n byBubble.map(h => {\n const { x, y } = screen({\n x: h.labelAt.x,\n y: Math.min(h.labelAt.y, rowsTop),\n })\n const baseline = y - halo / 2 - 6\n return {\n item: h,\n x,\n y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,\n text: h.label,\n }\n }),\n frame,\n take,\n )\n\n const genes = placeLabels(\n geneLabelCandidates(m.genePins, screen, m.contigThickness),\n frame,\n take,\n )\n const sizeCandidates =\n m.nodePositions && m.labelsNodeSizes\n ? sizeLabelCandidates({\n nodePositions: m.nodePositions,\n nodeLengths: m.nodeLengths,\n deletions: m.showDeletionEdges ? m.deletions : [],\n deletionRoutes: m.deletionRoutes,\n alleleDeletions: m.alleleDeletions,\n axis: m.axisScale,\n translateX,\n translateY,\n width,\n height,\n version: m.positionsVersion,\n })\n : { deletions: [], nodes: [] }\n const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take)\n const routes = placeLabels(\n byBubble.flatMap(h =>\n h.routes.map(route => {\n const { x, y } = screen(route.at)\n return {\n item: { halo: h, route },\n x,\n y: y + 4,\n text: route.text,\n stack: ROUTE_STACK,\n }\n }),\n ),\n frame,\n take,\n )\n const nodeLabels = placeSizeLabels(\n sizeCandidates.nodes,\n take,\n nodeLabelBudget(width, height),\n )\n return {\n bubbles,\n genes,\n routes,\n sizes: [...deletionLabels, ...nodeLabels],\n }\n}\n", "import { abgrToCssRgba, 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 { DeletionRoutes } from '../layout/deletionRoutes'\nimport type { NodeWidth } from '../nodeWidths'\nimport type { Graph, GraphNode, NodeSegment } from '../types'\nimport type { WalkLift } from '../walkHighlight'\nimport type {\n Arrowhead,\n EdgeCurveBatch,\n NodeStroke,\n RenderBatch,\n Run,\n} from './types'\nimport type { AxisScale, BezierCurve } from '../util/geometry'\n\n// Colors flow through the geometry builder as ABGR-in-u32 (see colorBits.ts),\n// so a brighten or fade is integer arithmetic and a batch of a hundred thousand\n// strokes carries its colours in one number each.\nconst EDGE_DEFAULT_COLOR = packAbgr(119, 119, 119, 217) // rgb(119,119,119) ~ 0.467, alpha 0.85\nconst EDGE_PATH_FALLBACK_COLOR = packAbgr(136, 136, 136, 217) // ~0.533, alpha 0.85\n// An edge that skips reference sequence: the graph's own statement that some\n// haplotype does not carry what the backbone does. Drawn thicker than a plain\n// link, and unconditionally rather than under a colour scheme, because a\n// deletion has no node to colour \u2014 nothing else in the drawing can carry it, so\n// there is no scheme it could disagree with. See deletionEdges.ts.\n//\n// NEAR-BLACK, not red. The reference-position ramp runs hue 0 to 300 at 70%/50%,\n// and its hue-0 end is rgb(217,38,38) \u2014 the old rgb(214,39,40) was that colour\n// to within three points, so at the start of any region the deletion arc and the\n// backbone under it were the same red (review: \"the 'red' coloring is too close\n// to the rainbow\"). Nothing else in the drawing is near-black: nodes are ramp\n// hues or the off-reference charcoal, plain links are mid grey. Weight and\n// darkness carry it instead of a hue, which is what keeps hue meaning reference\n// position and only that.\nconst EDGE_DELETION_COLOR = packAbgr(24, 24, 28, 240)\nconst DELETION_THICKNESS_FACTOR = 2.2\n// A lifted walk's links keep their plain ink, since the walk's lanes in the\n// nodes carry it, and the rest fades. The alpha they fade to keeps every colour\n// scheme's hues, only dimmer, on any background.\nexport const LIFT_BACKDROP_COLOR = packAbgr(160, 160, 160, 255)\nexport const FADED_ALPHA = 0.18\n// The same fade as css, for what paints outside the batch: the strip, the\n// keys, a tube map's tubes\nexport const LIFT_BACKDROP_CSS = abgrToCssRgba(\n fadeAbgr(LIFT_BACKDROP_COLOR, FADED_ALPHA),\n)\n// Dash period in screen px, so a dashed arc looks the same at any zoom. Dashes\n// are geometry rather than a stroke style, because only one of the two backends\n// has one; see dashCurves.\nconst DELETION_DASH_PX = 11\n\n// A node drawn under `drawPaths` is split into one lengthwise stripe per path in\n// the graph, in the legend's own order, and a path that does not visit the node\n// leaves its slot empty. The slot is what makes carriage readable: an absence\n// lands in the same place on every node, so \"which sample skips this segment\"\n// is a gap at a fixed height rather than something to be counted.\n//\n// Only the edges used to carry this, and how many of a graph's paths resolved\n// there was a function of how long the edge happened to be DRAWN: a kilobase\n// deletion is one long edge and separates all five strokes, while a 1 bp SNP\n// bubble is an edge a few px long and shows a speck (review: \"only deletion\n// really shows paths clearly, the edges between nodes are too small to see the\n// paths ... even coloring the length of the nodes using the per-sample colors\").\n// A node is drawn at its own length, which is the one thing in the drawing that\n// does not shrink to a joint.\n//\n// How many paths is too many to colour at all is not decided here \u2014 it is one\n// rule over the stripes, the ribbons and the key, so the model resolves it and\n// hands down the answer as `drawPaths`. See MAX_PATH_COLORS.\n//\n// A stripe below this many screen px is not a color, it is aliasing between\n// neighbouring stripes. Under it the node keeps its own scheme colour.\n// `contigThickness` is itself in screen px, so this bites on the path COUNT\n// rather than on the zoom.\nconst MIN_PATH_STRIPE_PX = 1.2\n// A lifted walk's lane is never thinner than this, so a node carrying three\n// lifted walks widens rather than splitting into hairlines\nconst MIN_WALK_LANE_PX = 4\n// Guards the screen-px-to-world division below against a degenerate transform.\nconst MIN_SCALE_FOR_OFFSET = 1e-6\n\n// An arrowhead's size in SCREEN px, grown from the half-width of the edge it\n// ends: the renderer expands it after the transform (see TransformUniform), so\n// a head is the same size at every zoom. A fixed 12 px head was twice the width\n// of a default 6 px node and hid the 1 bp nodes it pointed at; this one is about\n// a node wide on a default edge and grows with a deletion's or a walk's weight.\nconst ARROW_LENGTH_BASE_PX = 7\nconst ARROW_LENGTH_PER_EDGE_PX = 3\nconst ARROW_HALF_WIDTH_BASE_PX = 3\nconst ARROW_HALF_WIDTH_PER_EDGE_PX = 1.5\n// A head on an edge shorter than itself would overhang the node it leaves, so\n// it shrinks to fit, and below this fraction of its size it is dropped until a\n// zoom lengthens the edge. In a tight bubble the old heads, laid over every\n// 1 bp allele, were most of the ink.\nconst MIN_ARROW_FIT = 0.5\nconst MIN_ARROW_SCALE = 0.45\n\n// Steps per cubic when walking a curve back from its end, spaced by the square\n// of the step so they bunch where the head sits: the first is 1/144 of the\n// cubic, a few px on even a long edge.\nconst ARROW_CURVE_STEPS = 12\n// Below this an edge has no length, only the rounding of sampling one.\nconst ABUTTING_PX = 1e-6\n\n// Scratch for backFromEnd, reused across edges: this runs once per head per\n// rebuild, and objects per sample doubled the cost of a 16k-edge build.\nlet walkX = new Float64Array(64)\nlet walkY = new Float64Array(64)\nlet walkPx = new Float64Array(64)\n\n// A run of curves as points walked back from its end into the walk arrays,\n// each with its distance from the end in screen px, as far as `untilPx`.\n// Returns the point count.\nfunction backFromEnd(\n curves: BezierCurve[],\n scale: number,\n yToX: number,\n untilPx: number,\n) {\n const most = curves.length * ARROW_CURVE_STEPS + 1\n if (walkX.length < most) {\n walkX = new Float64Array(most)\n walkY = new Float64Array(most)\n walkPx = new Float64Array(most)\n }\n const last = curves[curves.length - 1]!\n walkX[0] = last.x1\n walkY[0] = last.y1\n walkPx[0] = 0\n let n = 1\n let px = 0\n for (let ci = curves.length - 1; ci >= 0 && px < untilPx; ci--) {\n const c = curves[ci]!\n for (let k = 1; k <= ARROW_CURVE_STEPS && px < untilPx; k++) {\n const t = 1 - (k / ARROW_CURVE_STEPS) ** 2\n const u = 1 - t\n const a = u * u * u\n const b = 3 * u * u * t\n const d = 3 * u * t * t\n const e = t * t * t\n const x = a * c.x0 + b * c.cx0 + d * c.cx1 + e * c.x1\n const y = a * c.y0 + b * c.cy0 + d * c.cy1 + e * c.y1\n px += Math.hypot(x - walkX[n - 1]!, (y - walkY[n - 1]!) * yToX) * scale\n walkX[n] = x\n walkY[n] = y\n walkPx[n] = px\n n++\n }\n }\n return n\n}\n\nfunction walkPointAt(n: number, px: number) {\n let i = 1\n while (i < n - 1 && walkPx[i]! < px) {\n i++\n }\n const from = walkPx[i - 1]!\n const to = walkPx[i]!\n const f = to > from ? Math.min(1, Math.max(0, (px - from) / (to - from))) : 0\n return {\n x: walkX[i - 1]! + (walkX[i]! - walkX[i - 1]!) * f,\n y: walkY[i - 1]! + (walkY[i]! - walkY[i - 1]!) * f,\n }\n}\n\n// Bandage's rule, from GraphicsItemNode::shape: a node's arrow is sized by the\n// node's own width. Here the head belongs to the edge, so the edge sets its\n// size, and the node it enters sets where the tip stops. The tip lands on that\n// node's outline, which a round cap puts `nodeHalfWidth` from the endpoint in\n// every direction; on the centreline it sank half a node deep and covered the\n// node's first few px. Only as far as the edge is long, so an abutting joint\n// keeps its head on the joint.\n//\n// A joint's head lies over the node the edge leaves, so that node's drawn\n// length is the room it fits to. A run of 1 bp nodes on a walk row otherwise\n// drew a head at every joint, a serration along the row.\n//\n// The head lies along the curve it ends, from the curve point a head-length\n// back to the tip. The end tangent alone runs along the entered node, and an\n// edge that turns in its last few px then drew a head pointing across its own\n// stroke.\nfunction arrowheadFor({\n curves,\n edgeHalfWidth,\n nodeHalfWidth,\n scale,\n yToX,\n color,\n fromSegments,\n toSegments,\n}: {\n curves: BezierCurve[]\n edgeHalfWidth: number\n nodeHalfWidth: number\n scale: number\n yToX: number\n color: number\n fromSegments: NodeSegment[]\n toSegments: NodeSegment[]\n}): Arrowhead | undefined {\n const length = ARROW_LENGTH_BASE_PX + ARROW_LENGTH_PER_EDGE_PX * edgeHalfWidth\n const n = backFromEnd(curves, scale, yToX, nodeHalfWidth + length)\n const lengthPx = walkPx[n - 1]!\n const inset = Math.min(nodeHalfWidth, lengthPx)\n const room =\n lengthPx < ABUTTING_PX\n ? polylinePx(fromSegments, scale, yToX)\n : lengthPx - inset\n const fit = Math.min(1, room / length)\n if (fit < MIN_ARROW_FIT) {\n return undefined\n }\n const tip = walkPointAt(n, inset)\n const base = walkPointAt(n, inset + length * fit)\n const dx = tip.x - base.x\n const dy = (tip.y - base.y) * yToX\n const last = curves[curves.length - 1]!\n return {\n x: tip.x,\n y: tip.y,\n angle:\n Math.hypot(dx, dy) > 0\n ? Math.atan2(dy, dx)\n : endTangent(last, yToX, intoDirection(last, toSegments)),\n length: length * fit,\n halfWidth:\n (ARROW_HALF_WIDTH_BASE_PX +\n ARROW_HALF_WIDTH_PER_EDGE_PX * edgeHalfWidth) *\n fit,\n color,\n }\n}\n\nfunction polylinePx(segments: NodeSegment[], scale: number, yToX: number) {\n let length = 0\n for (let i = 1; i < segments.length; i++) {\n length += Math.hypot(\n segments[i]!.x - segments[i - 1]!.x,\n (segments[i]!.y - segments[i - 1]!.y) * yToX,\n )\n }\n return length * scale\n}\n\n// The way into a node from the end an edge reaches it at: the direction an\n// abutting edge, which has no length of its own, points.\nfunction intoDirection(last: BezierCurve, toSegments: NodeSegment[]) {\n if (toSegments.length < 2) {\n return undefined\n }\n const first = toSegments[0]!\n const final = toSegments[toSegments.length - 1]!\n const atStart =\n Math.hypot(last.x1 - first.x, last.y1 - first.y) <=\n Math.hypot(last.x1 - final.x, last.y1 - final.y)\n return atStart\n ? { from: first, to: toSegments[1]! }\n : { from: final, to: toSegments[toSegments.length - 2]! }\n}\n\n// Per-point unit normals of a polyline, mitred at the interior joints so a\n// stripe slid along them keeps a constant distance from the node's own outline\n// where it bends.\n//\n// `yToX` puts the two axes in comparable units first (see computeEdgeCurves), so\n// the normal is perpendicular to the polyline AS DRAWN rather than to its\n// coordinates. It is 1 on an isotropic layout, where the two are the same thing.\nfunction pointNormalsOf(rawPoints: { x: number; y: number }[], yToX = 1) {\n const points =\n yToX === 1 ? rawPoints : rawPoints.map(p => ({ x: p.x, y: p.y * yToX }))\n const pointNormals: { nx: number; ny: number }[] = []\n for (let i = 0; i < points.length; i++) {\n let nx = 0\n let ny = 0\n\n if (i === 0) {\n const dx = points[1]!.x - points[0]!.x\n const dy = points[1]!.y - points[0]!.y\n const len = Math.hypot(dx, dy)\n if (len > 0) {\n nx = -dy / len\n ny = dx / len\n }\n } else if (i === points.length - 1) {\n const dx = points[i]!.x - points[i - 1]!.x\n const dy = points[i]!.y - points[i - 1]!.y\n const len = Math.hypot(dx, dy)\n if (len > 0) {\n nx = -dy / len\n ny = dx / len\n }\n } else {\n const dx1 = points[i]!.x - points[i - 1]!.x\n const dy1 = points[i]!.y - points[i - 1]!.y\n const len1 = Math.hypot(dx1, dy1)\n const dx2 = points[i + 1]!.x - points[i]!.x\n const dy2 = points[i + 1]!.y - points[i]!.y\n const len2 = Math.hypot(dx2, dy2)\n\n if (len1 > 0 && len2 > 0) {\n const nx1 = -dy1 / len1\n const ny1 = dx1 / len1\n const nx2 = -dy2 / len2\n const ny2 = dx2 / len2\n nx = (nx1 + nx2) / 2\n ny = (ny1 + ny2) / 2\n const dot = nx1 * nx + ny1 * ny\n if (dot > 0.1) {\n nx /= dot\n ny /= dot\n }\n } else if (len1 > 0) {\n nx = -dy1 / len1\n ny = dx1 / len1\n } else if (len2 > 0) {\n nx = -dy2 / len2\n ny = dx2 / len2\n }\n }\n pointNormals.push({ nx, ny })\n }\n return pointNormals\n}\n\n// The polyline shifted `distance` world units along its own normals, which is\n// the node equivalent of the constant perpendicular offset the edge path\n// strokes already take. `distance` is in x units and the normal is the drawn\n// one (pointNormalsOf), so the y component comes back out of x units here.\nfunction offsetPolyline(\n points: { x: number; y: number }[],\n normals: { nx: number; ny: number }[],\n distance: number,\n yToX = 1,\n) {\n return points.map((p, i) => ({\n x: p.x + normals[i]!.nx * distance,\n y: p.y + (normals[i]!.ny * distance) / yToX,\n }))\n}\n\nfunction packNorm(r: number, g: number, b: number, a: number) {\n return packAbgr(\n Math.round(r * 255),\n Math.round(g * 255),\n Math.round(b * 255),\n Math.round(a * 255),\n )\n}\n\nexport interface BuildOptions {\n nodePositions: Record<string, NodeSegment[]>\n graph: Graph\n nodeById: Map<string, GraphNode>\n colorScheme: ResolvedColorScheme\n contigThickness: number\n connectorThickness: number\n // Whether to paint per path \u2014 the RESOLVED answer, not the raw view prop.\n // Past MAX_PATH_COLORS a graph's paths are not drawable as colours at all and\n // the model hands over `false`, which is the one place that decides it for\n // the stripes, the ribbons and the key together. Same contract as\n // `colorScheme` above, which arrives with 'auto' already resolved.\n drawPaths: boolean\n // thicker by depth, or every node at `contigThickness`; see nodeWidths.ts\n nodeWidth?: NodeWidth\n // walks lifted out: each draws a lane of its own along the nodes it visits,\n // their links draw dark and heavy, and everything else fades to a fraction\n // of its alpha\n highlight?: WalkLift\n // Both scales together, and required. Every screen-metric constant here (dash\n // period, stripe width, arrowhead angle) divides by scaleX, and everything\n // that mixes the axes needs their ratio; taking them as one value is what\n // keeps a caller from supplying an x scale and defaulting y, which draws a\n // wrong picture and reports nothing. See AxisScale.\n axis: AxisScale\n linearLayout?: boolean\n viewportBounds?: { minX: number; minY: number; maxX: number; maxY: number }\n // What the 'reference-position' scheme paints from: each node's midpoint on\n // the reference, and the interval the hue spans. Unused by every other\n // scheme, and passed in rather than derived here for the reason `deletions`\n // below is \u2014 it costs a neighbour walk per node, it is a fact about the\n // graph, and this function runs on every pan. `computeReferenceRamp` is what\n // builds one.\n referenceRamp?: ReferenceRamp\n // links that skip reference sequence (deletionEdges()), keyed by their index\n // into graph.edges and carrying the backbone node ids they bypass. Passed in\n // rather than derived here because the model can hold it against the graph,\n // and because the same set names the hover text.\n deletions?: Map<number, string[]>\n // LayoutResult.deletionRoutes\n deletionRoutes?: DeletionRoutes\n hiddenEdges?: ReadonlySet<number>\n // Bumped when a drag moves the positions in place, which their identity\n // cannot report. Only the shared curve cache reads it; see baseEdgeCurves.\n version?: number\n}\n\nexport function hslToRgb(\n h: number,\n s: number,\n l: number,\n): [number, number, number] {\n const c = (1 - Math.abs(2 * l - 1)) * s\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = l - c / 2\n let r = 0\n let g = 0\n let b = 0\n if (h < 60) {\n r = c\n g = x\n } else if (h < 120) {\n r = x\n g = c\n } else if (h < 180) {\n g = c\n b = x\n } else if (h < 240) {\n g = x\n b = c\n } else if (h < 300) {\n r = x\n b = c\n } else {\n r = c\n b = x\n }\n return [r + m, g + m, b + m]\n}\n\n// Evenly-spaced RGB gradient stops (0-255 per channel).\nconst DEPTH_GRADIENT = [\n [68, 1, 84],\n [59, 82, 139],\n [33, 145, 140],\n [94, 201, 98],\n [253, 231, 37],\n] as const\n\n// ranks >= 1, i.e. everything off the reference backbone\nconst STABLE_RANK_GRADIENT = [\n [237, 137, 44],\n [158, 42, 122],\n] as const\n\nconst NODE_LENGTH_GRADIENT = [\n [220, 50, 50],\n [50, 120, 220],\n] as const\n\n// Sample a piecewise-linear RGB gradient; t is clamped to [0,1].\nfunction sampleGradient(\n stops: readonly (readonly [number, number, number])[],\n t: number,\n) {\n const pos = Math.max(0, Math.min(1, t)) * (stops.length - 1)\n const i = Math.min(stops.length - 2, Math.floor(pos))\n const f = pos - i\n const a = stops[i]!\n const b = stops[i + 1]!\n return packAbgr(\n Math.round(a[0] + (b[0] - a[0]) * f),\n Math.round(a[1] + (b[1] - a[1]) * f),\n Math.round(a[2] + (b[2] - a[2]) * f),\n 255,\n )\n}\n\n// Deterministic color from a string (djb2-style hash \u2192 HSL hue), for the\n// `random` node scheme. The path ribbons do NOT use this \u2014 see pathHueAt.\nfunction hashColor(str: string, alpha: number) {\n const [r, g, b] = hslToRgb(nameHue(str), PATH_SATURATION, PATH_LIGHTNESS)\n return packNorm(r, g, b, alpha)\n}\n\n// A hue ramp over reference coordinates, which is the one colouring a linear\n// track can reproduce exactly: it is a function of two stated numbers and a\n// node's reference midpoint, so `hsl(min(300,max(0,(mid-start)/span*300)),70%,50%)`\n// in a track's `color` slot paints a segment the colour the graph paints its\n// node. Stops at 300 rather than wrapping to 360, so the two ends of the window\n// are red and magenta rather than both red.\nexport const REFERENCE_RAMP_MAX_HUE = 300\nconst REFERENCE_RAMP_SATURATION = 0.7\nconst REFERENCE_RAMP_LIGHTNESS = 0.5\n// Off-reference segments come off the ramp entirely and paint one flat charcoal.\n//\n// They used to keep the hue of the reference they replace, paler and softer, on\n// the argument that the correspondence is the whole scheme. Two rounds of docs\n// review say otherwise: a pale hue is still a hue on the ramp, so an alternative\n// allele reads as reference sequence at that position rather than as sequence\n// the reference does not have \u2014 \"non-backbone parts of the bandage graph could\n// be coloured in a non-spectrum colouring\". Nothing is lost by dropping the hue,\n// because in both anchored layouts an allele's x already states which reference\n// position it attaches to, and in the force layout its position states nothing\n// to begin with.\n//\n// Distinct from the light grey a node with no reference coordinates at all gets:\n// this one is anchored and off-reference, that one is unplaceable.\nconst REFERENCE_RAMP_ALT_RGB = [60, 65, 72] as const\nexport const REFERENCE_RAMP_ALT_COLOR = packAbgr(...REFERENCE_RAMP_ALT_RGB, 255)\nexport const REFERENCE_RAMP_ALT_CSS = `rgb(${REFERENCE_RAMP_ALT_RGB.join(', ')})`\n\nexport interface ReferenceRamp {\n start: number\n span: number\n midpoints: Map<string, number>\n}\n\nexport interface ColorSchemeRange {\n minDepth: number\n maxDepth: number\n minLength: number\n maxLength: number\n maxRank: number\n nodeCount: number\n referenceRamp?: ReferenceRamp\n}\n\n// The domain is stated by the caller \u2014 the region the subgraph was cut from \u2014\n// rather than measured off the nodes, because a linear view has no way to know\n// what the cut reached: segments overrun the region at both edges, so a\n// measured domain would shift the hue of every node by an amount the other\n// panel cannot compute. With no region (a whole-file import) the drawn extent\n// is all there is, and the ramp is then only comparable to itself.\nexport function computeReferenceRamp(\n graph: Graph,\n domain: { start: number; end: number } | undefined,\n): ReferenceRamp {\n const midpoints = referenceMidpoints(graph)\n if (domain && domain.end > domain.start) {\n return {\n start: domain.start,\n span: domain.end - domain.start,\n midpoints,\n }\n }\n let min = Infinity\n let max = -Infinity\n for (const mid of midpoints.values()) {\n min = Math.min(min, mid)\n max = Math.max(max, mid)\n }\n return {\n start: min === Infinity ? 0 : min,\n span: max > min ? max - min : 1,\n midpoints,\n }\n}\n\nexport function computeColorSchemeRange(graph: Graph) {\n let minDepth = Infinity\n let maxDepth = -Infinity\n let minLength = Infinity\n let maxLength = -Infinity\n let maxRank = 0\n for (const n of graph.nodes) {\n if (n.stable !== undefined && n.stable.rank > maxRank) {\n maxRank = n.stable.rank\n }\n if (n.depth < minDepth) {\n minDepth = n.depth\n }\n if (n.depth > maxDepth) {\n maxDepth = n.depth\n }\n if (n.length < minLength) {\n minLength = n.length\n }\n if (n.length > maxLength) {\n maxLength = n.length\n }\n }\n return {\n minDepth,\n maxDepth,\n minLength,\n maxLength,\n maxRank,\n nodeCount: graph.nodes.length,\n } satisfies ColorSchemeRange\n}\n\nexport function getNodeColor(\n node: GraphNode,\n nodeIndex: number,\n colorScheme: ResolvedColorScheme,\n range: ColorSchemeRange,\n) {\n switch (colorScheme) {\n case 'random':\n return hashColor(node.id, 1)\n\n case 'depth':\n return sampleGradient(\n DEPTH_GRADIENT,\n range.maxDepth > range.minDepth\n ? (node.depth - range.minDepth) / (range.maxDepth - range.minDepth)\n : 0.5,\n )\n\n case 'node-length':\n return sampleGradient(\n NODE_LENGTH_GRADIENT,\n range.maxLength > range.minLength\n ? (node.length - range.minLength) /\n (range.maxLength - range.minLength)\n : 0.5,\n )\n\n // rGFA states the backbone rather than leaving it to be inferred: rank 0 is\n // the reference, higher ranks are the sequence that diverges from it. A\n // graph with no SR tags draws every node as unranked.\n case 'stable-rank':\n return node.stable === undefined\n ? packAbgr(160, 160, 160, 255)\n : node.stable.rank === 0\n ? packAbgr(52, 152, 219, 255)\n : sampleGradient(\n STABLE_RANK_GRADIENT,\n range.maxRank > 1\n ? (node.stable.rank - 1) / (range.maxRank - 1)\n : 0,\n )\n\n // Position on the reference, the one quantity both panels of a\n // graph-over-linear figure can state. A node with no reference position at\n // all \u2014 nothing anchored, or an allele whose flanks fell outside the cut \u2014\n // is grey rather than a hue it has not earned.\n case 'reference-position': {\n const ramp = range.referenceRamp\n const mid = ramp?.midpoints.get(node.id)\n if (ramp === undefined || mid === undefined) {\n return packAbgr(160, 160, 160, 255)\n }\n if (node.stable !== undefined && node.stable.rank > 0) {\n return REFERENCE_RAMP_ALT_COLOR\n }\n const frac = Math.max(0, Math.min(1, (mid - ramp.start) / ramp.span))\n const [r, g, b] = hslToRgb(\n frac * REFERENCE_RAMP_MAX_HUE,\n REFERENCE_RAMP_SATURATION,\n REFERENCE_RAMP_LIGHTNESS,\n )\n return packNorm(r, g, b, 1)\n }\n\n case 'grey':\n return packAbgr(160, 160, 160, 255)\n\n case 'rainbow': {\n const hue = range.nodeCount > 1 ? (nodeIndex / range.nodeCount) * 360 : 0\n const [r, g, b] = hslToRgb(hue, 0.75, 0.5)\n return packNorm(r, g, b, 1)\n }\n\n default:\n return packAbgr(52, 152, 219, 255)\n }\n}\n\n// The SCREEN angle an arrowhead points: a cubic's tangent at t=1, which runs\n// from its last control point to its endpoint, so no tessellation is needed.\n// The head is expanded from a screen-px size after the transform, so its\n// direction has to be the drawn one too, and `yToX` puts the y difference in x\n// units first.\n//\n// A control point on the endpoint states no direction there. The tangent's\n// limit as t -> 1 then runs from the FIRST control point, and failing that from\n// the start. An edge with no length at all, the abutting joint of an anchored\n// layout, has none of those, and `into` (the way into the node it enters) is\n// what is left; without it such a head pointed along +x whatever the node did.\nexport function endTangent(\n c: BezierCurve,\n yToX = 1,\n into?: { from: NodeSegment; to: NodeSegment },\n) {\n const candidates: [number, number][] = [\n [c.cx1, c.cy1],\n [c.cx0, c.cy0],\n [c.x0, c.y0],\n ]\n for (const [x, y] of candidates) {\n const dx = c.x1 - x\n const dy = (c.y1 - y) * yToX\n if (Math.hypot(dx, dy) > 0) {\n return Math.atan2(dy, dx)\n }\n }\n return into\n ? Math.atan2((into.to.y - into.from.y) * yToX, into.to.x - into.from.x)\n : 0\n}\n\n// Whether any part of a node's polyline falls inside the viewport. Testing the\n// *segments* rather than their endpoints is what makes a node wider than the\n// window visible: in the reference-anchored layouts x is in bp, so a backbone\n// segment routinely spans the whole viewport with both ends outside it, and\n// point containment culled exactly the segment the view exists to show.\n//\n// Segment bounding boxes, not exact line-rect intersection: culling only has to\n// avoid dropping something visible, and a diagonal kept by its bbox costs one\n// polyline.\nfunction isPolylineInBounds(\n segments: NodeSegment[],\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n) {\n let hit = false\n for (let i = 0; i < segments.length - 1 && !hit; i++) {\n const a = segments[i]!\n const b = segments[i + 1]!\n hit =\n Math.max(a.x, b.x) >= bounds.minX &&\n Math.min(a.x, b.x) <= bounds.maxX &&\n Math.max(a.y, b.y) >= bounds.minY &&\n Math.min(a.y, b.y) <= bounds.maxY\n }\n return hit\n}\n\nfunction isBezierInBounds(\n curves: BezierCurve[],\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n) {\n for (const c of curves) {\n const cMinX = Math.min(c.x0, c.cx0, c.cx1, c.x1)\n const cMaxX = Math.max(c.x0, c.cx0, c.cx1, c.x1)\n const cMinY = Math.min(c.y0, c.cy0, c.cy1, c.y1)\n const cMaxY = Math.max(c.y0, c.cy0, c.cy1, c.y1)\n if (\n cMaxX >= bounds.minX &&\n cMinX <= bounds.maxX &&\n cMaxY >= bounds.minY &&\n cMinY <= bounds.maxY\n ) {\n return true\n }\n }\n return false\n}\n\n// Per-graph derivations, cached on the graph: this function runs on every\n// debounced pan and zoom, and a walk over every path's every step is a fact\n// about the graph rather than about the frame.\n\n// By position in the path list, matching pathLegend's swatches exactly, so the\n// key beside the drawing names the strokes in it.\n//\n// One array plus a name lookup, rather than an array and a Map of the same\n// colours: two paths that share a name then draw the same colour on their\n// edges and on their nodes, instead of the Map keeping the last one written\n// while the array kept the first. `gfaConverter` no longer produces such a\n// pair, and this is what makes the drawing merely wrong rather than\n// inconsistent if anything ever does again.\nconst paletteCache = new WeakMap<\n Graph,\n { pathColorByIndex: number[]; pathIndexByName: Map<string, number> }\n>()\n\nfunction pathPaletteOf(graph: Graph) {\n let palette = paletteCache.get(graph)\n if (!palette) {\n const pathColorByIndex: number[] = []\n const pathIndexByName = new Map<string, number>()\n const paths = graph.paths ?? []\n for (let i = 0; i < paths.length; i++) {\n const [r, g, b] = hslToRgb(\n pathHueAt(i, paths.length),\n PATH_SATURATION,\n PATH_LIGHTNESS,\n )\n pathColorByIndex.push(packNorm(r, g, b, 0.85))\n if (!pathIndexByName.has(paths[i]!.name)) {\n pathIndexByName.set(paths[i]!.name, i)\n }\n }\n palette = { pathColorByIndex, pathIndexByName }\n paletteCache.set(graph, palette)\n }\n return palette\n}\n\n// nodeId -> the indices of the paths that visit it, i.e. which stripe slots\n// this node fills. Built from the paths themselves rather than read off the\n// node, because `GraphNode.samples` collapses a sample's haplotypes together\n// and the legend does not.\nconst slotsCache = new WeakMap<Graph, Map<string, number[]>>()\nconst NO_SLOTS: ReadonlyMap<string, number[]> = new Map()\n\nfunction pathSlotsOf(graph: Graph): ReadonlyMap<string, number[]> {\n let slots = slotsCache.get(graph)\n if (!slots) {\n slots = new Map()\n for (const [pathIdx, path] of (graph.paths ?? []).entries()) {\n for (const nodeId of new Set(path.nodeIds)) {\n const visits = slots.get(nodeId)\n if (visits) {\n visits.push(pathIdx)\n } else {\n slots.set(nodeId, [pathIdx])\n }\n }\n }\n slotsCache.set(graph, slots)\n }\n return slots\n}\n\nexport function buildGeometry(options: BuildOptions): RenderBatch {\n const {\n nodePositions,\n graph,\n nodeById,\n colorScheme,\n contigThickness,\n connectorThickness,\n drawPaths,\n nodeWidth = 'uniform',\n highlight,\n axis,\n linearLayout,\n viewportBounds,\n referenceRamp,\n deletions,\n deletionRoutes,\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 deletionRoutes,\n )\n\n const nodeStrokes: NodeStroke[] = []\n const nodeStrokeRuns = new Map<string, Run>()\n const arrows: Arrowhead[] = []\n const arrowRuns = new Map<number, Run>()\n const edgeCurves: EdgeCurveBatch[] = []\n const edgeCurveRuns = new Map<number, Run>()\n\n const colorRange = { ...computeColorSchemeRange(graph), referenceRamp }\n\n const nodeIndexMap = new Map<string, number>()\n if (colorScheme === 'rainbow') {\n for (let i = 0; i < graph.nodes.length; i++) {\n nodeIndexMap.set(graph.nodes[i]!.id, i)\n }\n }\n\n const pathCount = graph.paths?.length ?? 0\n const { pathColorByIndex, pathIndexByName } = pathPaletteOf(graph)\n const pathColorByName = (name: string) =>\n pathColorByIndex[pathIndexByName.get(name) ?? -1] ??\n EDGE_PATH_FALLBACK_COLOR\n const nodePathSlots =\n drawPaths && pathCount > 1 ? pathSlotsOf(graph) : NO_SLOTS\n\n // An arrowhead at every joint of a few hundred short nodes is a serration\n // along the whole drawing, so heads wait for a zoom where a node is longer\n // than its head.\n const showArrows = scale > (linearLayout ? 1 : MIN_ARROW_SCALE)\n\n for (let ei = 0; ei < graph.edges.length; ei++) {\n const edge = graph.edges[ei]!\n const fromSegments = nodePositions[edge.from]\n const toSegments = nodePositions[edge.to]\n const baseCurves = sharedCurves.get(ei)\n if (\n !fromSegments?.length ||\n !toSegments?.length ||\n !baseCurves ||\n hiddenEdges?.has(ei)\n ) {\n continue\n }\n\n const numPaths = edge.pathIds?.length ?? 0\n const bypassed = deletions?.get(ei)\n const isDeletion = bypassed !== undefined\n const onWalk = highlight?.edgeIndexes.has(ei) ?? false\n const edgeThickness =\n (connectorThickness / 2) * (isDeletion ? DELETION_THICKNESS_FACTOR : 1)\n // One stroke per path crossing the edge, fanned off it: the shared curve\n // slid sideways, which is also what the hit index tests against. Rebuilding\n // the curve at each offset was 40% of a striped build and differed from a\n // translation only in a deletion's bow, by the offset's few px.\n const ribbons = drawPaths && numPaths > 0\n\n // An arrowhead states which way a LINK is read, which is a property of the\n // edge and not of each path crossing it. Drawn per ribbon it says one thing\n // once per haplotype: five stacked heads at a joint, in five colours, for a\n // single fact about a single link. That is the \"coloured confetti at each\n // joint\" that made `drawPaths` unusable on a force layout, and it reached\n // the anchored layouts too the moment their abutting edges started drawing\n // again. Measured on `ecoli_pggb_subgraph.gfa`: 72 arrowheads plain, 155\n // with ribbons, over 72 links.\n //\n // So one head per edge, in the edge's own colour rather than a ribbon's,\n // since choosing a ribbon's would privilege one haplotype for a fact that\n // belongs to none of them. `undefined` means this call draws no head.\n const plainEdgeColor = isDeletion ? EDGE_DELETION_COLOR : EDGE_DEFAULT_COLOR\n const edgeColor =\n !highlight || onWalk\n ? plainEdgeColor\n : fadeAbgr(plainEdgeColor, FADED_ALPHA)\n const toNode = nodeById.get(edge.to)\n const intoHalfWidth =\n (toNode\n ? nodeWidthPx(toNode, contigThickness, nodeWidth, depthNorm)\n : contigThickness) / 2\n const buildSingleEdge = (\n offsetX: number,\n offsetY: number,\n color: number,\n arrowColor: number | undefined,\n ) => {\n const curves =\n offsetX === 0 && offsetY === 0\n ? baseCurves\n : translateCurves(baseCurves, offsetX, offsetY)\n\n if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {\n return\n }\n\n // A deletion is drawn dashed, and that is the only thing in the drawing\n // that is. Weight and darkness were carrying it alone, and they could not:\n // an off-reference node is charcoal rgb(60,65,72) and the arc is near-black\n // rgb(24,24,28), which is ~25 points a channel apart, so in a figure the\n // two dark strokes read as the same ink and the arc is the rarer of them\n // (measured on `pangenome/hprc_lpa_kiv2`: 7,411 px of charcoal node against\n // ~5,000 px of arc). Hue cannot separate them either, since hue means\n // reference position and charcoal means having none. A broken line is\n // available, unused, and says what it draws: sequence that is not there.\n if (isDeletion) {\n for (const dash of dashCurves(curves, DELETION_DASH_PX / scale)) {\n edgeCurves.push({ curves: dash, thickness: edgeThickness, color })\n }\n } else {\n edgeCurves.push({ curves, thickness: edgeThickness, color })\n }\n\n const head =\n showArrows && arrowColor !== undefined\n ? arrowheadFor({\n curves,\n edgeHalfWidth: edgeThickness,\n nodeHalfWidth: intoHalfWidth,\n scale,\n yToX,\n color: arrowColor,\n fromSegments,\n toSegments,\n })\n : undefined\n if (head) {\n arrows.push(head)\n }\n }\n\n const edgeCurveStart = edgeCurves.length\n const arrowStart = arrows.length\n\n // A lifted walk's link carries on its lane from the node it leaves, so a\n // lane runs unbroken through the joints rather than beside a grey link\n const laneWalks =\n highlight && onWalk && !ribbons ? highlight.walks : undefined\n if (laneWalks) {\n const fromNode = nodeById.get(edge.from)\n const width = fromNode\n ? nodeWidthPx(fromNode, contigThickness, nodeWidth, depthNorm)\n : contigThickness\n const laneWidth = Math.max(width / laneWalks.length, MIN_WALK_LANE_PX)\n // the lanes' sideways direction where the link leaves its node, which\n // is the end a '-' link leaves from the start of\n const normals = pointNormalsOf(fromSegments, yToX)\n const { nx, ny } = (edge.fromStrand === '-'\n ? normals[0]\n : normals.at(-1)) ?? {\n nx: 0,\n ny: 0,\n }\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n laneWalks.forEach((walk, lane) => {\n if (!walk.edgeIndexes.has(ei)) {\n return\n }\n const offset =\n (lane - (laneWalks.length - 1) / 2) * laneWidth * worldPerScreenPx\n const curves =\n offset === 0\n ? baseCurves\n : translateCurves(baseCurves, nx * offset, (ny * offset) / yToX)\n if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {\n return\n }\n edgeCurves.push({\n curves,\n thickness: laneWidth / 2,\n color:\n walk.colors.get(edge.from) ??\n walk.colors.get(edge.to) ??\n plainEdgeColor,\n })\n })\n } else if (!ribbons) {\n buildSingleEdge(0, 0, edgeColor, edgeColor)\n } else {\n const offsets = pathRibbonOffsets(\n fromSegments,\n toSegments,\n numPaths,\n axis,\n )\n // The middle ribbon carries the head, which at an odd path count is the\n // one drawn at offset 0, i.e. on the edge's own line. At an even count it\n // is half a spacing off, which beats a sixth `computeEdgeCurves` call to\n // rebuild a curve the fan is already centred on.\n const arrowRibbon = Math.floor((numPaths - 1) / 2)\n for (let pathIdx = 0; pathIdx < numPaths; pathIdx++) {\n const offset = offsets[pathIdx]!\n const ribbonColor = pathColorByName(edge.pathIds![pathIdx]!)\n buildSingleEdge(\n offset.x,\n offset.y,\n highlight && !highlight.names.has(edge.pathIds![pathIdx]!)\n ? fadeAbgr(ribbonColor, FADED_ALPHA)\n : ribbonColor,\n pathIdx === arrowRibbon ? edgeColor : undefined,\n )\n }\n }\n\n const edgeCurveCount = edgeCurves.length - edgeCurveStart\n if (edgeCurveCount > 0) {\n edgeCurveRuns.set(ei, { start: edgeCurveStart, count: edgeCurveCount })\n }\n const arrowCount = arrows.length - arrowStart\n if (arrowCount > 0) {\n arrowRuns.set(ei, { start: arrowStart, count: arrowCount })\n }\n }\n\n for (const [nodeId, segments] of Object.entries(nodePositions)) {\n const node = nodeById.get(nodeId)\n if (!node) {\n continue\n }\n\n if (viewportBounds && !isPolylineInBounds(segments, viewportBounds)) {\n continue\n }\n\n const faded = highlight !== undefined && !highlight.nodeIds.has(nodeId)\n // Under lifted walks the lanes carry every colour, so the rest fades in\n // grey rather than in a scheme no lane is drawn in\n const own = faded\n ? LIFT_BACKDROP_COLOR\n : getNodeColor(\n node,\n nodeIndexMap.get(nodeId) ?? 0,\n colorScheme,\n colorRange,\n )\n const color = faded ? fadeAbgr(own, FADED_ALPHA) : own\n const width = nodeWidthPx(node, contigThickness, nodeWidth, depthNorm)\n const nodeThickness = width / 2\n\n const start = nodeStrokes.length\n // The node's drawn width is the same either way: the stripes divide it\n // rather than inflate it, so turning paths on does not re-weight the\n // drawing against the edges and the layout underneath it.\n //\n // A thickness is SCREEN px and a point is world units. So the slot width is\n // screen px and the sideways shift that places a stripe has to be taken\n // back into world units, or the stripes fan apart as you zoom in and\n // collapse into one as you zoom out.\n const slots = nodePathSlots.get(nodeId)\n const slotWidth = width / pathCount\n const drawable = segments.length >= 2\n // Each lifted walk keeps one lane across the whole drawing, so a lane\n // missing from a node is a walk that does not visit it\n const lanes =\n highlight && !faded && !slots?.length ? highlight.walks : undefined\n if (drawable && lanes) {\n const laneWidth = Math.max(width / lanes.length, MIN_WALK_LANE_PX)\n const normals = pointNormalsOf(segments, yToX)\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n lanes.forEach((walk, lane) => {\n const laneColor = walk.colors.get(nodeId)\n if (laneColor === undefined) {\n return\n }\n const offset =\n (lane - (lanes.length - 1) / 2) * laneWidth * worldPerScreenPx\n nodeStrokes.push({\n points:\n lanes.length === 1\n ? segments\n : offsetPolyline(segments, normals, offset, yToX),\n thickness: laneWidth / 2,\n color: laneColor,\n })\n })\n } else if (drawable && slots?.length && slotWidth >= MIN_PATH_STRIPE_PX) {\n const normals = pointNormalsOf(segments, yToX)\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n for (const slot of slots) {\n const offset =\n (slot - (pathCount - 1) / 2) * slotWidth * worldPerScreenPx\n const stripe = pathColorByIndex[slot] ?? EDGE_PATH_FALLBACK_COLOR\n nodeStrokes.push({\n points: offsetPolyline(segments, normals, offset, yToX),\n thickness: slotWidth / 2,\n // a node off the lifted walk fades whichever way it is painted\n color: faded ? fadeAbgr(stripe, FADED_ALPHA) : stripe,\n })\n }\n } else if (drawable) {\n nodeStrokes.push({ points: segments, thickness: nodeThickness, color })\n }\n const count = nodeStrokes.length - start\n if (count > 0) {\n nodeStrokeRuns.set(nodeId, { start, count })\n }\n }\n\n return {\n nodeStrokes,\n nodeStrokeRuns,\n arrows,\n arrowRuns,\n edgeCurves,\n edgeCurveRuns,\n }\n}\n", "import {\n abgrAlpha,\n abgrBlue,\n abgrGreen,\n abgrRed,\n packAbgr,\n} from '@jbrowse/core/util/colorBits'\n\n// The one ABGR helper core does not have. Everything else the renderer needs \u2014\n// the packing, the channel accessors, both css formatters \u2014 comes straight from\n// `@jbrowse/core/util/colorBits`, which ReExports/list.ts now names, so the\n// plugin bundle binds it to the host's copy. The plugin targets the latest\n// JBrowse beta; 5.0.0-beta.4 serves that module without `packAbgr`, and the\n// plugin fails to install there.\n\n// Scale a packed color's channels, clamped at full brightness and leaving alpha\n// alone. factor === 1 returns the color unchanged.\n// The same colour at a fraction of its alpha, so a faded node reads as the\n// same ink through it on any background.\nexport function fadeAbgr(c: number, alpha: number) {\n return packAbgr(\n abgrRed(c),\n abgrGreen(c),\n abgrBlue(c),\n Math.round(abgrAlpha(c) * alpha),\n )\n}\n\nexport function brightenAbgr(c: number, factor: number) {\n return packAbgr(\n Math.min(255, Math.round(abgrRed(c) * factor)),\n Math.min(255, Math.round(abgrGreen(c) * factor)),\n Math.min(255, Math.round(abgrBlue(c) * factor)),\n abgrAlpha(c),\n )\n}\n", "import { isBackbone } from './anchoredNodes'\n\nimport type { Graph, GraphNode } from './types'\n\n// How long an allele chain can be and still be located from both ends. An\n// allele is a run of off-reference segments between two backbone flanks, so the\n// two flanks sit at *different* hop depths from any segment in the middle and\n// the walk has to keep going after it finds the first one. Minigraph alleles are\n// short runs, so this reaches both flanks throughout the fixtures; a longer run\n// degrades to the flanks it did reach rather than to nothing.\nconst MAX_BACKBONE_HOPS = 4\n\nexport interface ReferenceSpan {\n start: number\n end: number\n}\n\n// Undirected adjacency over node ids. Edges carry the topology in both\n// directions for this purpose: finding the backbone a node hangs off doesn't\n// care which way the L line was written.\nexport function buildNeighbors(graph: Graph) {\n const neighbors = new Map<string, string[]>()\n function add(from: string, to: string) {\n const list = neighbors.get(from)\n if (list) {\n list.push(to)\n } else {\n neighbors.set(from, [to])\n }\n }\n for (const edge of graph.edges) {\n add(edge.from, edge.to)\n add(edge.to, edge.from)\n }\n return neighbors\n}\n\n// The reference interval an allele branches into: between the end of the\n// backbone segment upstream of it and the start of the one downstream.\n// `[min(ends), max(starts)]` picks those two out of the reached flanks without\n// needing to know which is which, and it degrades usefully in both directions \u2014\n// a pure insertion, whose flanks abut, collapses to the single reference point\n// it is inserted at, and a bubble with only one flank inside the window yields\n// that flank's own span.\nfunction flankingSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}) {\n const seen = new Set([nodeId])\n let frontier = [nodeId]\n let minEnd = Infinity\n let maxStart = -Infinity\n\n // Backbone nodes are terminal \u2014 they are collected, never queued \u2014 so the walk\n // cannot cross the reference into a neighbouring bubble. It stays inside the\n // allele it started in and stops on its own at the flanks.\n for (let hop = 0; hop < MAX_BACKBONE_HOPS && frontier.length > 0; hop++) {\n const next: string[] = []\n for (const id of frontier) {\n const adjacent = neighbors.get(id)\n if (adjacent) {\n for (const neighborId of adjacent) {\n if (!seen.has(neighborId)) {\n seen.add(neighborId)\n const neighbor = nodeById.get(neighborId)\n if (neighbor && isBackbone(neighbor)) {\n minEnd = Math.min(minEnd, neighbor.stable.start + neighbor.length)\n maxStart = Math.max(maxStart, neighbor.stable.start)\n } else {\n next.push(neighborId)\n }\n }\n }\n }\n }\n frontier = next\n }\n\n return minEnd === Infinity\n ? undefined\n : {\n start: Math.min(minEnd, maxStart),\n end: Math.max(minEnd, maxStart),\n }\n}\n\n// The reference interval to highlight for a node, in the coordinates of the\n// stable sequence the subgraph was cut from.\n//\n// A rank-0 node states its own, so the interval is exact. A rank>0 allele has\n// none: its SO is an offset on a *different* stable sequence and is not\n// comparable to the reference axis (see the axis note in\n// layout/anchoredLayout.ts), so it is located by the backbone it branches from\n// instead.\nexport function nodeReferenceSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}): ReferenceSpan | undefined {\n const node = nodeById.get(nodeId)\n return node === undefined\n ? undefined\n : isBackbone(node)\n ? { start: node.stable.start, end: node.stable.start + node.length }\n : flankingSpan({ nodeId, nodeById, neighbors })\n}\n\n// Every node's midpoint on the reference, keyed by id: what a position-based\n// colour scheme ramps over. Alleles come through the same flanking walk hover\n// sync uses, so a bubble takes the colour of the reference interval it branches\n// from instead of dropping out of the ramp \u2014 which is the whole point, since an\n// allele is exactly what a linear view cannot draw at a position of its own.\nexport function referenceMidpoints(graph: Graph) {\n const nodeById = new Map(graph.nodes.map(node => [node.id, node]))\n const neighbors = buildNeighbors(graph)\n const midpoints = new Map<string, number>()\n for (const node of graph.nodes) {\n const span = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors })\n if (span) {\n midpoints.set(node.id, (span.start + span.end) / 2)\n }\n }\n return midpoints\n}\n", "import { bypassedPoints } from '../deletionEdges'\nimport { computeEdgeCurves, routedEdgeCurves, selfLinkOf } from './geometry'\n\nimport type { DeletionRoutes } from '../layout/deletionRoutes'\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 routes: DeletionRoutes | 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 // where a force layout ran each deletion; such an edge follows its route\n routes?: DeletionRoutes,\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.routes === routes &&\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 const route = bypassed && routes?.[ei]\n curves.set(\n ei,\n route\n ? routedEdgeCurves(from, to, route)\n : computeEdgeCurves(\n from,\n to,\n selfLinkOf(edge),\n 0,\n 0,\n axis,\n bypassed ? bypassedPoints(nodePositions, bypassed) : [],\n ),\n )\n }\n }\n cache.set(nodePositions, {\n graph,\n scaleX,\n scaleY,\n deletions,\n routes,\n version,\n curves,\n })\n return curves\n}\n", "import { REFERENCE_RAMP_MAX_HUE } from './renderer/GeometryBuilder'\n\n// The reference-position ramp in CSS, from the same three numbers\n// `getNodeColor` paints with (hue 0 to REFERENCE_RAMP_MAX_HUE at 70%/50%), so\n// a key or an overlay cannot come to describe a ramp the canvas stopped using.\n\nexport function rampHueCss(hue: number) {\n return `hsl(${hue}, 70%, 50%)`\n}\n\nexport function rampHue(bp: number, domain: { start: number; end: number }) {\n const frac = (bp - domain.start) / (domain.end - domain.start)\n return Math.max(0, Math.min(1, frac)) * REFERENCE_RAMP_MAX_HUE\n}\n\n// hue degrees one gradient stop spans, since SVG interpolates stops in RGB\nconst HUE_STOP_DEG = 30\n\n// The hues a gradient across `bp` of reference paints, evenly spaced along it\n// and walked from `to`'s end when reversed. One hue means the span reads flat.\nexport function rampStops(\n { start, bp, reversed }: { start: number; bp: number; reversed?: boolean },\n domain: { start: number; end: number },\n) {\n const low = rampHue(start, domain)\n const high = rampHue(start + bp, domain)\n if (high - low < 1) {\n return [(low + high) / 2]\n }\n const n = Math.ceil((high - low) / HUE_STOP_DEG)\n return Array.from({ length: n + 1 }, (_, i) =>\n rampHue(start + (bp * (reversed ? n - i : i)) / n, domain),\n )\n}\n\nexport const RAMP_GRADIENT_CSS = `linear-gradient(to right, ${Array.from(\n { length: 7 },\n (_, i) => `${rampHueCss((i / 6) * REFERENCE_RAMP_MAX_HUE)} ${(i / 6) * 100}%`,\n).join(', ')})`\n", "import { isAnchored } from '@jbrowse/bandage-core/anchoredNodes'\nimport {\n panSNContig,\n panSNHaplotype,\n panSNSample,\n} from '@jbrowse/bandage-core/pansn'\n\nimport type { AnchoredNode } from '@jbrowse/bandage-core/anchoredNodes'\nimport type { Graph, GraphNode } from '@jbrowse/bandage-core/types'\n\nexport interface GraphLocation {\n // PanSN sample name off the node's own stable sequence. The assembly that\n // contributed the sequence, which is a *name*, not a resolved assembly \u2014 see\n // resolveContributors.\n sample: string\n // `sample#hap` where the stable name states a haplotype (HPRC release 2.1\n // names every node `NA20809#2#CM094351.1`), so a sample loaded as two\n // assemblies opens the right one; undefined where it states none\n haplotype: string | undefined\n refName: string\n start: number\n end: number\n}\n\n// Where a node's own sequence lives, in the coordinates of the assembly that\n// contributed it. rGFA states this on every segment: SN names the stable\n// sequence and SO the offset on it, so this is exact at every rank.\n//\n// It is the other question from nodeReferenceSpan, which asks where a node sits\n// on the *reference* axis and can only approximate that off the backbone. A\n// rank>0 node has an exact coordinate of its own; what it has no coordinate for\n// is the reference.\nexport function nodeOwnLocation(node: GraphNode): GraphLocation | undefined {\n return isAnchored(node) ? anchoredNodeLocation(node) : undefined\n}\n\nfunction anchoredNodeLocation(node: AnchoredNode): GraphLocation {\n return {\n sample: panSNSample(node.stable.refName),\n haplotype: panSNHaplotype(node.stable.refName),\n refName: panSNContig(node.stable.refName),\n start: node.stable.start,\n end: node.stable.start + node.length,\n }\n}\n\nexport interface Contributor extends GraphLocation {\n // lowest rank this assembly holds here: 0 means it is the reference backbone\n rank: number\n nodeCount: number\n}\n\ninterface Span {\n start: number\n end: number\n}\n\n// The widest run of spans with no gap larger than `maxGap`, which is the locus\n// this assembly contributes *here*. A bare union of every span would be wrong\n// on a graph where a sample also contributes sequence from a distant\n// duplication: the union then reads as a multi-megabase locus, and a linear view\n// launched at it shows neither end.\nfunction widestCluster(spans: Span[], maxGap: number) {\n const sorted = [...spans].sort((a, b) => a.start - b.start)\n let best: Span | undefined\n let current: Span | undefined\n for (const span of sorted) {\n current =\n current && span.start - current.end <= maxGap\n ? { start: current.start, end: Math.max(current.end, span.end) }\n : { start: span.start, end: span.end }\n if (!best || current.end - current.start > best.end - best.start) {\n best = current\n }\n }\n return best\n}\n\ninterface Group {\n sample: string\n haplotype: string | undefined\n refName: string\n rank: number\n spans: Span[]\n}\n\nfunction groupKey(loc: GraphLocation) {\n return `${loc.haplotype ?? loc.sample}\u0000${loc.refName}`\n}\n\n// Every assembly with a segment in this graph, and the locus each one covers on\n// its own coordinates.\n//\n// This is what makes a launch out of the graph possible at all: the graph names\n// its contributors in the data (rGFA's SN tag), so a node, a row and the graph\n// as a whole can each say which assembly they belong to without consulting an\n// alignment.\n//\n// Ordered reference-first, then by name, so the row order matches\n// sampleRowLayout's and neither reshuffles as the window moves.\nexport function contributingAssemblies(\n graph: Graph,\n { maxGap = Infinity }: { maxGap?: number } = {},\n): Contributor[] {\n const groups = new Map<string, Group>()\n for (const node of graph.nodes) {\n if (isAnchored(node)) {\n const loc = anchoredNodeLocation(node)\n const key = groupKey(loc)\n const group = groups.get(key)\n if (group) {\n group.spans.push({ start: loc.start, end: loc.end })\n group.rank = Math.min(group.rank, node.stable.rank)\n } else {\n groups.set(key, {\n sample: loc.sample,\n haplotype: loc.haplotype,\n refName: loc.refName,\n rank: node.stable.rank,\n spans: [{ start: loc.start, end: loc.end }],\n })\n }\n }\n }\n\n // One entry per assembly (a haplotype where the names state one, else the\n // sample): an assembly contributing on several contigs is launched at the\n // contig it contributes most of, since a linear view opens on one region.\n const bySample = new Map<string, Contributor>()\n for (const group of groups.values()) {\n const span = widestCluster(group.spans, maxGap)\n if (span) {\n const candidate = {\n sample: group.sample,\n haplotype: group.haplotype,\n refName: group.refName,\n start: span.start,\n end: span.end,\n rank: group.rank,\n nodeCount: group.spans.length,\n }\n const key = group.haplotype ?? group.sample\n const existing = bySample.get(key)\n if (!existing || candidate.nodeCount > existing.nodeCount) {\n bySample.set(key, candidate)\n }\n }\n }\n\n // Reference first, then by name: the same order sampleRowLayout gives its\n // rows, so the menu lists the assemblies in the order they are stacked on\n // screen. Sorting by rank instead would order them by minigraph's build\n // order, which is not what the labels beside the drawing say.\n return [...bySample.values()].sort(\n (a, b) =>\n Number(a.rank !== 0) - Number(b.rank !== 0) ||\n a.sample.localeCompare(b.sample) ||\n (a.haplotype ?? '').localeCompare(b.haplotype ?? ''),\n )\n}\n\n// The assembly a location opens on: its haplotype's where the name states one\n// and the session holds it, else its sample's. A session holding both\n// haplotypes of a sample opens the right one; one holding a haplotype under\n// the bare sample name still opens it.\nexport function resolveLocationAssembly(\n resolve: (sample: string) => string | undefined,\n loc: Pick<GraphLocation, 'sample' | 'haplotype'>,\n) {\n const byHaplotype =\n loc.haplotype === undefined ? undefined : resolve(loc.haplotype)\n return byHaplotype ?? resolve(loc.sample)\n}\n\n// Contributors that name an assembly this session has loaded, which are the\n// only ones a linear or synteny view can open on.\n//\n// The two cases this filters apart are both normal. An E. coli pangenome demo\n// loads every strain as its own assembly, so every contributor resolves and the\n// whole graph is navigable. An HPRC graph names hundreds of haplotypes that no\n// session would ever load as assemblies, so only the reference resolves \u2014\n// there the reference projection (nodeReferenceSpan) is the way back to a\n// coordinate, not this.\n//\n// `resolve` is a name lookup rather than a list of names, because a PanSN sample\n// is the *graph's* spelling and need not be the assembly's. HPRC's graph writes\n// `CHM13`, and the assembly holding that sequence is UCSC's `hs1`, with CHM13\n// among its aliases; matching on names alone leaves that donor unopenable unless\n// the reader renames their assembly after the graph. The resolved (canonical)\n// name replaces the sample, so a launch opens the assembly the session knows.\nexport function resolveContributors(\n contributors: Contributor[],\n resolve: (sample: string) => string | undefined,\n) {\n return contributors.flatMap(c => {\n const sample = resolveLocationAssembly(resolve, c)\n return sample === undefined ? [] : [{ ...c, sample }]\n })\n}\n\n// 1-based inclusive, which is what a locstring means. Unformatted digits: this\n// is parsed, not read. locLabel is the one people read.\nexport function locString(loc: GraphLocation) {\n return `${loc.refName}:${loc.start + 1}-${loc.end}`\n}\n\nexport function locLabel(loc: { refName: string; start: number; end: number }) {\n return `${loc.refName}:${(loc.start + 1).toLocaleString()}-${loc.end.toLocaleString()}`\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M9.01 14H2v2h7.01v3L13 15l-3.99-4zm5.98-1v-3H22V8h-7.01V5L11 9z\"\n}), 'CompareArrows');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M17 5h-2V3h2zm-2 16h2v-2.59L19.59 21 21 19.59 18.41 17H21v-2h-6zm4-12h2V7h-2zm0 4h2v-2h-2zm-8 8h2v-2h-2zM7 5h2V3H7zM3 17h2v-2H3zm2 4v-2H3c0 1.1.9 2 2 2M19 3v2h2c0-1.1-.9-2-2-2m-8 2h2V3h-2zM3 9h2V7H3zm4 12h2v-2H7zm-4-8h2v-2H3zm0-8h2V3c-1.1 0-2 .9-2 2\"\n}), 'HighlightAlt');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M3 16h5v-2H3zm6.5 0h5v-2h-5zm6.5 0h5v-2h-5zM3 20h2v-2H3zm4 0h2v-2H7zm4 0h2v-2h-2zm4 0h2v-2h-2zm4 0h2v-2h-2zM3 12h8v-2H3zm10 0h8v-2h-8zM3 4v4h18V4z\"\n}), 'LineStyle');", "import CompareArrowsIcon from '@mui/icons-material/CompareArrows'\nimport HighlightAltIcon from '@mui/icons-material/HighlightAlt'\nimport LineStyleIcon from '@mui/icons-material/LineStyle'\n\nimport { locLabel } from './contributors'\n\nimport type { Contributor, GraphLocation } from './contributors'\nimport type { LaunchableSyntenyTrack } from './syntenyTracks'\nimport type { MenuItem } from '@jbrowse/core/ui'\n\nconst LINEAR_LABEL = 'Linear genome view'\nconst SYNTENY_LABEL = 'Linear synteny view'\nconst NO_SYNTENY_TRACK =\n 'No synteny track in this session aligns these assemblies'\n\nexport interface LinearTarget {\n location: GraphLocation\n assembly: string\n}\n\n// One flat item when there is one thing to open, a submenu when there is a\n// choice. Same shape as subgraphMenuItems uses in the other direction, so the\n// two ends of the graph <-> linear trip read the same way.\nfunction oneOrMany<T>({\n label,\n icon,\n entries,\n entryLabel,\n onSelect,\n disabledHelpText,\n nameTheOnlyEntry = true,\n}: {\n label: string\n icon: React.ElementType\n entries: T[]\n entryLabel: (entry: T) => string\n onSelect: (entry: T) => () => void\n disabledHelpText?: string\n // Whether a lone entry's name is worth appending to the item. It is when the\n // entry is where you land (`GRCh38 chr6:31,980,000-32,050,000`) and not when\n // it is the ingredient used to get there \u2014 a synteny track's name says\n // nothing the item does not already say, and reading it costs the width of\n // `pggb graph: all-vs-all synteny (wfmash)`.\n nameTheOnlyEntry?: boolean\n}): MenuItem[] {\n const [first] = entries\n // Nothing to open: a *disabled* item with the reason in its tooltip, when\n // there is a reason worth reading. An item that vanishes teaches nobody that\n // the session is one synteny track short of a multi-genome view.\n const empty: MenuItem[] = disabledHelpText\n ? [{ label, icon, disabled: true, disabledHelpText, onClick: () => {} }]\n : []\n return entries.length === 0\n ? empty\n : entries.length === 1 && first\n ? [\n {\n label: nameTheOnlyEntry ? `${label} \u2014 ${entryLabel(first)}` : label,\n icon,\n onClick: onSelect(first),\n },\n ]\n : [\n {\n label,\n icon,\n type: 'subMenu',\n subMenu: entries.map(entry => ({\n label: entryLabel(entry),\n onClick: onSelect(entry),\n })),\n },\n ]\n}\n\n// The graph's way out: a linear view of any assembly the graph names here, and a\n// synteny view of all of them at once. Until this existed the triangle had two\n// edges \u2014 a linear view could open a graph or a synteny view of a locus, and the\n// graph could open nothing at all.\n//\n// `contributors` are already resolved against the session's assemblies. On an\n// E. coli pangenome every strain is its own assembly, so all of them appear and\n// the synteny item is the interesting one; on an HPRC graph the 400-odd\n// contributing haplotypes are not loaded assemblies and only the reference\n// appears, which is the honest answer rather than a missing feature.\nexport function graphLaunchMenuItems({\n contributors,\n syntenyTracks,\n onShowLinear,\n onShowSynteny,\n}: {\n contributors: Contributor[]\n syntenyTracks: LaunchableSyntenyTrack[]\n onShowLinear: (target: LinearTarget) => void\n onShowSynteny: (trackId: string) => void\n}): MenuItem[] {\n // sorted widest first\n const widest = syntenyTracks[0]?.coverage ?? contributors.length\n return [\n ...oneOrMany({\n label: LINEAR_LABEL,\n icon: LineStyleIcon,\n entries: contributors,\n entryLabel: c => `${c.sample} ${locLabel(c)}`,\n onSelect: c => () => {\n onShowLinear({ location: c, assembly: c.sample })\n },\n }),\n ...(contributors.length >= 2\n ? oneOrMany({\n // the count the launch opens, which is what the widest track aligns\n // and not always every contributor; see launchSyntenyView\n label: `${SYNTENY_LABEL} (${widest} assemblies)`,\n icon: CompareArrowsIcon,\n entries: syntenyTracks,\n entryLabel: track =>\n track.coverage < widest\n ? `${track.name} (${track.coverage} of ${widest})`\n : track.name,\n nameTheOnlyEntry: false,\n disabledHelpText: NO_SYNTENY_TRACK,\n onSelect: track => () => {\n onShowSynteny(track.trackId)\n },\n })\n : []),\n ]\n}\n\n// A single node's way out, for the node context menu. Two different questions,\n// both worth offering:\n//\n// - the node's own coordinates, on the assembly that contributed it. Exact,\n// from the node's SN/SO, and available at every rank \u2014 but only openable\n// when that assembly is loaded, which for an HPRC haplotype it is not.\n// - where the node sits on the reference the graph was cut against. Always\n// openable, and off the backbone it is the flanking projection rather than\n// an exact span (see nodeReferenceSpan), so it is labelled as the region\n// around the node rather than as the node.\n//\n// Labels are verbs. Review: \"the lineargenomeview menu item should say launch\n// if that is what it does\" \u2014 a bare `Linear genome view \u2014 hg38 chr6:\u2026` names a\n// place and leaves the reader to guess whether clicking scrolls the view beside\n// the graph or opens a new one. It does the first when there is one to scroll\n// (see showInLinearView), so \"Open in\" covers both without claiming a launch.\n//\n// Verb, assembly, and where \u2014 nothing else. Every word here is read inside a\n// menu opened on a node, so \"this node in the hg38 view\" and \"in linear view\"\n// restate the context they are already in; what a coordinate opens in is not in\n// question, and the assembly name is.\n//\n// Flat, not under a \"Launch view\" submenu: this menu is a handful of items and\n// every one is contextual to the node just clicked.\nexport function nodeLaunchMenuItems({\n own,\n reference,\n highlight,\n onShowLinear,\n onHighlight,\n}: {\n own: LinearTarget | undefined\n reference: LinearTarget | undefined\n // the node's exact reference span, where `reference` is padded to open on\n highlight: LinearTarget | undefined\n onShowLinear: (target: LinearTarget) => void\n // absent when no linear view in the session is this graph's to mark\n onHighlight?: (target: LinearTarget) => void\n}): MenuItem[] {\n const entries = [\n own\n ? {\n label: `Open in ${own.assembly} \u2014 ${locLabel(own.location)}`,\n target: own,\n }\n : undefined,\n // Only when it says something the item above doesn't. A backbone segment on\n // a loaded reference answers both questions with the same assembly and\n // nearly the same span, and offering that twice reads as two different\n // places to go.\n reference && reference.assembly !== own?.assembly\n ? {\n label: `Open in ${reference.assembly} \u2014 around this node`,\n target: reference,\n }\n : undefined,\n ].filter(entry => entry !== undefined)\n\n // The reference projection, not the node's own coordinates: a highlight is\n // drawn in the linear view already on screen, and that view is on the\n // reference the subgraph was cut from.\n return [\n ...(onHighlight && highlight\n ? [\n {\n label: `Highlight in ${highlight.assembly}`,\n icon: HighlightAltIcon,\n onClick: () => {\n onHighlight(highlight)\n },\n },\n ]\n : []),\n ...entries.map(({ label, target }) => ({\n label,\n icon: LineStyleIcon,\n onClick: () => {\n onShowLinear(target)\n },\n })),\n ]\n}\n", "import { parseCs } from '../gaf/parseGaf'\n\nimport type { GafRecord } from '../gaf/parseGaf'\nimport type { Graph, GraphNode } from '../types'\nimport type { InputTrack, Mismatch } from '@gmod/tubemap-core'\n\n// GAF reads as tube map reads over a cut. A read keeps the steps whose\n// segments the cut holds, and its cs edits land on the node they fall in, at\n// a position in the direction the read walks it, as vg's GAM mappings state\n// theirs.\n\nexport function canonicalStrand(node: GraphNode) {\n return node.id.endsWith('-') ? '-' : '+'\n}\n\n// A visit to a node as tubemap-core names it: `-` where the walk runs against\n// the strand the node is drawn in\nexport function stepName(node: GraphNode, strand: '+' | '-') {\n return strand === canonicalStrand(node) ? node.id : `-${node.id}`\n}\n\n// Where each step starts on the walk, where that can be known: counted from\n// the walk's start up to the first segment outside the cut, and back from its\n// end down to the last. A step between two such segments has no start.\nfunction stepStarts(lengths: readonly (number | undefined)[], total: number) {\n const starts: (number | undefined)[] = lengths.map(() => undefined)\n let head = 0\n let i = 0\n for (; i < lengths.length && lengths[i] !== undefined; i++) {\n starts[i] = head\n head += lengths[i]!\n }\n let tail = total\n for (let j = lengths.length - 1; j > i && lengths[j] !== undefined; j--) {\n tail -= lengths[j]!\n starts[j] = tail\n }\n return starts\n}\n\nfunction mismatchesByStep(\n record: GafRecord,\n lengths: readonly (number | undefined)[],\n) {\n const byStep: Mismatch[][] = record.path.map(() => [])\n if (record.cs === undefined) {\n return byStep\n }\n const starts = stepStarts(lengths, record.pathLength)\n // the step holding walk position `pos`, or for `atEnd` the one ending there\n const stepAt = (pos: number, atEnd = false) => {\n for (let s = 0; s < starts.length; s++) {\n const start = starts[s]\n const length = lengths[s]\n if (\n start !== undefined &&\n length !== undefined &&\n start <= pos &&\n (pos < start + length || (atEnd && pos === start + length))\n ) {\n return { s, local: pos - start, room: start + length - pos }\n }\n }\n return undefined\n }\n const add = (s: number, mismatch: Mismatch) => {\n const list = byStep[s]!\n const last = list.at(-1)\n if (\n mismatch.type === 'substitution' &&\n last?.type === 'substitution' &&\n last.pos + last.seq!.length === mismatch.pos\n ) {\n last.seq += mismatch.seq!\n } else {\n list.push(mismatch)\n }\n }\n let pos = record.pathStart\n for (const op of parseCs(record.cs)) {\n if (op.op === 'match') {\n pos += op.length\n } else if (op.op === 'substitution') {\n const at = stepAt(pos)\n if (at) {\n add(at.s, { type: 'substitution', pos: at.local, seq: op.seq })\n }\n pos += 1\n } else if (op.op === 'insertion') {\n const at = stepAt(pos, pos === record.pathEnd)\n if (at) {\n add(at.s, { type: 'insertion', pos: at.local, seq: op.seq })\n }\n } else {\n let left = op.length\n while (left > 0) {\n const at = stepAt(pos)\n const take = at ? Math.min(left, at.room) : left\n if (at) {\n add(at.s, { type: 'deletion', pos: at.local, length: take })\n }\n pos += take\n left -= take\n }\n }\n }\n return byStep\n}\n\n// `firstId` follows the haplotype tracks' ids, which tubemap-core shares\n// between tracks and reads.\nexport function tubeMapReads(\n graph: Graph,\n records: readonly GafRecord[],\n firstId: number,\n): InputTrack[] {\n const nodeByName = new Map(graph.nodes.map(n => [n.name, n]))\n const reads: InputTrack[] = []\n for (const record of records) {\n const nodes = record.path.map(step => nodeByName.get(step.name))\n const kept = nodes.flatMap((node, i) => (node ? [i] : []))\n const first = kept[0]\n const last = kept.at(-1)\n if (first !== undefined && last !== undefined) {\n const mismatches = mismatchesByStep(\n record,\n nodes.map(n => n?.length),\n )\n const names = kept.map(i => stepName(nodes[i]!, record.path[i]!.strand))\n const lastLength = nodes[last]!.length\n reads.push({\n id: firstId + reads.length,\n name: record.name,\n type: 'read',\n sourceTrackID: 1,\n sequence: names,\n sequenceNew: kept.map((i, k) => ({\n nodeName: names[k]!,\n mismatches: mismatches[i]!,\n })),\n firstNodeOffset: first === 0 ? record.pathStart : 0,\n finalNodeCoverLength:\n last === record.path.length - 1\n ? lastLength - (record.pathLength - record.pathEnd)\n : lastLength,\n mapping_quality:\n record.mappingQuality === 255 ? 0 : record.mappingQuality,\n is_secondary: record.secondary,\n is_reverse: record.strand === '-',\n })\n }\n }\n return reads\n}\n\n// sequenceTubeMap's read palettes: blues forward, reds reverse, staggered by\n// read so neighbours differ\nexport const FORWARD_READ_COLORS = ['#6baed6', '#4292c6', '#2171b5', '#08519c']\nexport const REVERSE_READ_COLORS = ['#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15']\n\nexport function readColor(read: { id: number; is_reverse?: boolean }) {\n const colors = read.is_reverse ? REVERSE_READ_COLORS : FORWARD_READ_COLORS\n return colors[read.id % colors.length]!\n}\n", "import { REFERENCE_RANK, isAnchored } from '../anchoredNodes'\nimport { buildNeighbors } from '../referenceSpan'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph } from '../types'\n\n// An rGFA subgraph's alleles on the reference axis, from the stable\n// coordinates and the L lines alone: an allele is a run of off-reference\n// segments, and the rank-0 segments it detaches from and reattaches to give it\n// a reference span. rGFA carries no W or P lines, so nothing here reads a walk.\n\nexport interface ProjectedAllele {\n // reference bp the allele replaces, from the flanking rank-0 segments\n start: number\n refSpan: number\n // the run's strand-suffixed node ids, ordered by `nodeOffsets`\n nodeIds: string[]\n // bp from the allele's entry to the start of each node, along the run's own\n // edges, so a layout puts a node where it sits within the allele\n nodeOffsets: number[]\n // bp of the longest path through the run, the denominator for nodeOffsets:\n // a bubble's branches are alternatives, so their sum is no assembly's length\n pathLength: number\n}\n\n// PanSN is `sample#haplotype#contig`, but minigraph accepts a bare contig name\n// and the E. coli fixture is `K12#1#chr`, so this parses what is there.\nexport function parsePanSN(refName: string) {\n const parts = refName.split('#')\n const haplotype = parts.length >= 2 ? Number(parts[1]) : Number.NaN\n return {\n sample: parts[0] ?? refName,\n haplotype: Number.isFinite(haplotype) ? haplotype : undefined,\n }\n}\n\n// One connected run of off-reference segments and the rank-0 segments on its\n// boundary. Walked through anchored nodes only: everything reported about a\n// run is read off a stable coordinate.\nfunction collectRun(\n seed: string,\n byId: Map<string, AnchoredNode>,\n adj: Map<string, string[]>,\n claimed: Set<string>,\n) {\n const members: AnchoredNode[] = []\n const anchors: AnchoredNode[] = []\n const queue = [seed]\n claimed.add(seed)\n\n while (queue.length > 0) {\n const id = queue.pop()!\n const node = byId.get(id)\n if (node) {\n members.push(node)\n for (const neighborId of adj.get(id) ?? []) {\n const neighbor = byId.get(neighborId)\n if (neighbor) {\n if (neighbor.stable.rank === REFERENCE_RANK) {\n anchors.push(neighbor)\n } else if (!claimed.has(neighborId)) {\n claimed.add(neighborId)\n queue.push(neighborId)\n }\n }\n }\n }\n }\n return { members, anchors }\n}\n\n// The reference interval an allele replaces: from the end of the last backbone\n// segment before it to the start of the first one after it. Anchors on two\n// stable sequences, or in an order that runs backwards (an inverted or\n// translocated attachment), state no span rather than a negative one.\nfunction anchorSpan(anchors: AnchoredNode[]) {\n const refNames = new Set(anchors.map(a => a.stable.refName))\n if (refNames.size !== 1 || anchors.length < 2) {\n return undefined\n }\n const sorted = [...anchors].sort((a, b) => a.stable.start - b.stable.start)\n const first = sorted[0]!\n const last = sorted.at(-1)!\n const start = first.stable.start + first.length\n const end = last.stable.start\n return end >= start ? { start, end, entry: first } : undefined\n}\n\n// Shortest-path bp from the upstream anchor to each member over the run's own\n// edges, which only orders the run. The edges are undirected (a GFA states a\n// reverse-complement link backwards and single-node mode collapses both\n// orientations), so there is no topological order and a longest-path\n// relaxation would not converge.\nfunction runDistances(\n members: AnchoredNode[],\n entry: AnchoredNode,\n adj: Map<string, string[]>,\n) {\n const byId = new Map(members.map(m => [m.id, m]))\n const dist = new Map<string, number>()\n const queue: string[] = []\n for (const neighborId of adj.get(entry.id) ?? []) {\n if (byId.has(neighborId)) {\n dist.set(neighborId, 0)\n queue.push(neighborId)\n }\n }\n if (queue.length === 0) {\n for (const member of members) {\n dist.set(member.id, 0)\n queue.push(member.id)\n }\n }\n for (const id of queue) {\n const reach = dist.get(id)! + byId.get(id)!.length\n for (const neighborId of adj.get(id) ?? []) {\n if (byId.has(neighborId)) {\n const known = dist.get(neighborId)\n if (known === undefined || reach < known) {\n dist.set(neighborId, reach)\n queue.push(neighborId)\n }\n }\n }\n }\n return dist\n}\n\n// Where each member sits within the allele: ordered by distance from the entry\n// and swept forward, so a node starts where the last of its already-placed\n// neighbours ends. Offsets are then monotone along every edge in the run, which\n// is what keeps a bubble's entry and exit curves from crossing; where the two\n// branches differ in length the short one gets slack rather than the long one a\n// backwards edge.\nfunction runOffsets(\n members: AnchoredNode[],\n entry: AnchoredNode,\n adj: Map<string, string[]>,\n) {\n const dist = runDistances(members, entry, adj)\n const ordered = [...members].sort((a, b) => dist.get(a.id)! - dist.get(b.id)!)\n const rank = new Map(ordered.map((m, i) => [m.id, i]))\n const byId = new Map(members.map(m => [m.id, m]))\n const offsets = new Map<string, number>()\n for (const [i, member] of ordered.entries()) {\n let offset = 0\n for (const neighborId of adj.get(member.id) ?? []) {\n const neighbor = byId.get(neighborId)\n if (neighbor && rank.get(neighborId)! < i) {\n offset = Math.max(offset, offsets.get(neighborId)! + neighbor.length)\n }\n }\n offsets.set(member.id, offset)\n }\n return { offsets, ordered }\n}\n\n// Every allele with a reference span, by start. A run that reaches the edge of\n// the cut has one anchor and no span, which is the ordinary case at a window\n// boundary; placeOffReference chains those off wherever they branch.\nexport function projectAlleles(graph: Graph): ProjectedAllele[] {\n const byId = new Map(\n graph.nodes.filter(isAnchored).map(n => [n.id, n] as const),\n )\n const adj = buildNeighbors(graph)\n const claimed = new Set<string>()\n const alleles: ProjectedAllele[] = []\n\n for (const node of byId.values()) {\n if (node.stable.rank !== REFERENCE_RANK && !claimed.has(node.id)) {\n const { members, anchors } = collectRun(node.id, byId, adj, claimed)\n const span = anchorSpan(anchors)\n if (span) {\n const { offsets, ordered } = runOffsets(members, span.entry, adj)\n alleles.push({\n start: span.start,\n refSpan: span.end - span.start,\n nodeIds: ordered.map(m => m.id),\n nodeOffsets: ordered.map(m => offsets.get(m.id)!),\n pathLength: ordered.reduce(\n (max, m) => Math.max(max, offsets.get(m.id)! + m.length),\n 0,\n ),\n })\n }\n }\n }\n\n return alleles.sort((a, b) => a.start - b.start)\n}\n", "import { projectAlleles } from '../alleleProjection/projectAlleles'\nimport { buildNeighbors } from '../referenceSpan'\n\nimport type { AlleleDeletion, Graph, NodeSegment } from '../types'\n\n// Where off-reference segments go on a reference x axis. Shared by the two\n// row-structured layouts, which differ only in what a row means (stable rank\n// vs contributing assembly); how far along the reference a segment reaches is\n// the same question in both, and answering it twice is how they drifted.\n//\n// The rule: a segment occupies the reference its allele replaces, never its own\n// sequence length. x is reference bp \u2014 the axis the backbone declares and the\n// linear view beside it is drawn on \u2014 so an insertion, which consumes no\n// reference, must not advance along it. Both layouts used to advance by\n// sequence length, which made every off-reference x a false statement about\n// reference position and, on a 1 kb window holding a 100 kb allele, drew 101x\n// the window so that zoom-to-fit rendered the reference at 1% of the frame.\n//\n// Magnitude is the cost. An axis of reference coordinates has no way to state\n// how much sequence an insertion adds, so it is not stated here: it is in the\n// node tooltip, and drawn to scale in the allele-inventory track, where a CIGAR\n// gives the alignments display a channel for length that is not the x axis.\n\n// Reference bp an allele has to skip before the drawing calls it a deletion:\n// the 1 bp floor `deletionEdges` uses, a guard against abutting anchors' zero\n// gap rather than a biological threshold. Whether a small one is drawn is\n// decided by its label's fit in graphLabels.\nconst MIN_DELETION_BP = 1\n\n// Floor on an off-reference node's drawn span, as a fraction of the window.\n// Node thickness is a constant number of screen pixels, so a segment on a\n// sub-percent slice of the window draws wider than it is long and reads as a\n// dot on a stalk, and a pure insertion occupies no reference at all. Rank-0\n// nodes keep their exact offsets, so the reference axis is untouched.\nconst MIN_ALLELE_SPAN_FRACTION = 0.015\n\ninterface PlaceArgs {\n graph: Graph\n // the reference interval the drawing is scaled against (referenceSpan)\n span: number\n // y for a segment, which is the whole of what the two layouts disagree about\n rowY: (node: Graph['nodes'][number]) => number\n // the backbone, already placed at its declared offsets. Mutated in place, and\n // read for the branch points the unanchored walk below starts from.\n positions: Record<string, NodeSegment[]>\n}\n\nexport function placeOffReference({\n graph,\n span,\n rowY,\n positions,\n}: PlaceArgs): AlleleDeletion[] {\n const minSpan = span * MIN_ALLELE_SPAN_FRACTION\n const alleles = projectAlleles(graph)\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n // Alleles standing in for more reference than they carry. Collected here\n // rather than derived by the label pass, because the reason they need saying\n // is the placement done in this function: a run drawn over the reference it\n // replaces has a width that is not its length, and nothing downstream can\n // tell that from a node drawn at its own scale. Same measure the deletion\n // arcs use \u2014 reference bp a haplotype does not carry \u2014 so the two kinds of\n // deletion in a drawing are stated in the same units and the same words.\n const alleleDeletions: AlleleDeletion[] = []\n\n for (const allele of alleles) {\n const skipped = allele.refSpan - allele.pathLength\n if (skipped >= MIN_DELETION_BP) {\n alleleDeletions.push({ nodeIds: allele.nodeIds, bp: skipped })\n }\n const drawn = Math.max(allele.refSpan, minSpan)\n // A node sits where it sits *within* the allele: its bp offset from the\n // entry, scaled so the run's longest path fills the reference the allele\n // replaces. So a bubble path occupies the reference exactly once however\n // many segments it is built from, and two parallel branches share the x\n // range rather than queue up after each other \u2014 they are alternatives over\n // the same interval, not consecutive sequence.\n //\n // Offsets are measured over the graph rather than over nodeIds' order (see\n // runOffsets). Concatenating in traversal order put graph neighbours 26 kb\n // apart on a 27 kb allele, and the edges spanning those gaps crossed as a\n // long X. A run of all zero-length segments has no distances to scale by;\n // splitting evenly beats 0/0, which wrote NaN into every position after it.\n //\n // Where the floor widens a run past the reference it replaces, the overhang\n // hangs off the right, so the run can end past the rank-0 segment it\n // rejoins and its exit edge runs backwards. That is handled where it shows,\n // in computeEdgeCurves, which sizes each control point by how far the other\n // endpoint lies along its own tangent \u2014 a backwards edge gets a straight\n // leader rather than the loop that read as a crossed bowtie.\n const scale = allele.pathLength > 0 ? drawn / allele.pathLength : 0\n allele.nodeIds.forEach((nodeId, i) => {\n const node = byId.get(nodeId)\n if (node) {\n const offset = allele.nodeOffsets[i]!\n const even = drawn / allele.nodeIds.length\n // both ends from the offset rather than start-plus-width, so two nodes\n // the sweep placed end to end land on exactly the same x instead of on\n // two roundings of it\n const at = (bp: number) => allele.start + bp * scale\n const y = rowY(node)\n positions[nodeId] = scale\n ? [\n { x: at(offset), y },\n { x: at(offset + node.length), y },\n ]\n : [\n { x: allele.start + i * even, y },\n { x: allele.start + (i + 1) * even, y },\n ]\n }\n })\n }\n\n // A run that reaches the edge of the fetched subgraph has only one backbone\n // anchor, so no reference span can be stated for it. That is the normal case\n // at a window boundary, not an error, and dropping it is indistinguishable\n // from the layout being broken \u2014 the sample-rows layout did drop them.\n //\n // They get the floor each, chained from wherever they branch: the only honest\n // claim available is \"attaches here and continues past the edge\", and a floor\n // keeps a chain of them bounded by the window instead of by their sequence.\n // The same undirected adjacency the reference walk builds, from the one\n // function that builds it. The copy here rebuilt each list with a spread per\n // edge, which is quadratic in a node's degree and is the shape a collapsed\n // repeat's hub node is worst at.\n const neighbors = buildNeighbors(graph)\n // Read forward while appending, so this is a BFS with an O(1) dequeue rather\n // than a quadratic shift() over what ends up being every node in the run.\n const queue = Object.keys(positions)\n for (const fromId of queue) {\n const fromEnd = positions[fromId]!.at(-1)!.x\n for (const nextId of neighbors.get(fromId) ?? []) {\n const next = byId.get(nextId)\n if (next && !(nextId in positions)) {\n const y = rowY(next)\n positions[nextId] = [\n { x: fromEnd, y },\n { x: fromEnd + minSpan, y },\n ]\n queue.push(nextId)\n }\n }\n }\n\n return alleleDeletions\n}\n", "import { placeOffReference } from './placeOffReference'\nimport { ROW_HEIGHT_PX } from './rowSpacing'\nimport {\n backboneNodes,\n backbonePositions,\n referenceSpan,\n} from '../anchoredNodes'\n\nimport type { Graph, LayoutResult, RowLabel } from '../types'\n\n// Layout for rGFA, where the graph states its own backbone instead of leaving a\n// force simulation to find one. Every segment carries SN/SO/SR (gfatools\n// doc/rGFA.md), so both axes come from the file:\n//\n// x reference bp, for every segment. Rank-0 segments sit at the offset they\n// declare; an off-reference segment cannot use its own SO, which is an\n// offset on a different stable sequence, so it takes the slice of the\n// reference its allele replaces (placeOffReference).\n// y one row per stable rank *present in this subgraph*, the way lh3's own\n// rGFA viewer (VRPG) does it: rank 0 is the reference line, higher ranks\n// below it in order. In SCREEN PIXELS, not bp \u2014 see rowSpacing.ts, shared\n// with the sample-row layout.\n\n// Rank is a property of the whole graph, not of the window being drawn: HPRC's\n// minigraph graph ranks up to 89, but an MHC window holds only ranks\n// 0/1/3/6/14/23. Indexing rows by raw rank would leave 17 of 24 rows empty and\n// zoom-to-fit would shrink the drawing to fit that void (measured: 0.3% scale).\n// So rows are the ranks actually present, in rank order \u2014 identical to the raw\n// rank whenever the window happens to hold a contiguous run from 0.\nfunction rankRows(graph: Graph) {\n const present = new Set<number>()\n for (const node of graph.nodes) {\n if (node.stable) {\n present.add(node.stable.rank)\n }\n }\n return new Map([...present].sort((a, b) => a - b).map((r, i) => [r, i]))\n}\n\nexport function anchoredLayout(\n graph: Graph,\n region?: { start: number; end: number },\n): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n if (backbone.length === 0) {\n return undefined\n }\n\n const span = referenceSpan(backbone, region)\n const rows = rankRows(graph)\n\n // Row 0 by construction: a backbone exists, so rank 0 is present, and\n // `rankRows` numbers the present ranks in ascending order.\n const nodePositions = backbonePositions(backbone)\n\n const alleleDeletions = placeOffReference({\n graph,\n span,\n rowY: node =>\n (node.stable ? (rows.get(node.stable.rank) ?? rows.size) : rows.size) *\n ROW_HEIGHT_PX,\n positions: nodePositions,\n })\n\n // Named from the same `rows` map that placed every node, so the axis and the\n // drawing cannot disagree. Rank 0 is spelled out: \"the reference\" is what a\n // reader needs, and \"Rank 0\" alone reads as an ordinal with no meaning.\n const rowLabels: RowLabel[] = [...rows].map(([rank, row]) => ({\n label: rank === 0 ? 'Reference (rank 0)' : `Rank ${rank}`,\n y: row * ROW_HEIGHT_PX,\n }))\n\n return {\n nodePositions,\n rowLabels,\n referenceAxis: true,\n pixelRows: true,\n alleleDeletions,\n }\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { isBackbone } from '../anchoredNodes'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph, GraphNode, LayoutResult, NodeSegment } from '../types'\n\n// Reference-ordered layered layout: x is reference ORDER, not bp. Backbone\n// segments take increasing layers by stable offset, every other node the layer\n// between the anchors it hangs off, and a node is as wide as the log of its bp,\n// so a SNP allele gets the same room as a 10 kb segment and a bubble reads as a\n// lens rather than a bar under a line. y is a lane in screen px with the\n// reference pinned at lane 0 (ADR-042).\n\nconst GUTTER = 12\nconst LANE_PX = ROW_HEIGHT_PX\n\nfunction nodeWidth(node: GraphNode) {\n return 10 + 8 * Math.log2(1 + node.length)\n}\n\nfunction compare<T extends string | number>(a: T, b: T) {\n return a < b ? -1 : a > b ? 1 : 0\n}\n\n// Where a node sorts: `at` is the rank of a placed node, the backbone and what\n// the walks visit, and a node the search claims sorts before or after the one\n// that claimed it, by its steps from it. A tuple rather than an offset nudged\n// per step: on a base-level graph the backbone nodes are a bp apart, and a\n// chain a few nodes long nudged its keys past the ones that follow.\ninterface OrderKey {\n at: number\n side: -1 | 0 | 1\n hops: number\n}\n\nfunction compareKeys(a: OrderKey, b: OrderKey) {\n return (\n compare(a.at, b.at) || compare(a.side, b.side) || compare(a.hops, b.hops)\n )\n}\n\nconst UNREACHED: OrderKey = { at: Infinity, side: 0, hops: 0 }\n\n// Every node id in reference order: backbone by stable offset, every other node\n// beside a backbone node (orderKeys), nodes nothing reaches last. Name breaks\n// ties, so the order is a strict total order and the same graph always gets\n// the same one.\nexport function referenceOrder(graph: Graph) {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const key = orderKeys(graph)\n return graph.nodes\n .map(n => n.id)\n .sort(\n (a, b) =>\n compareKeys(key.get(a) ?? UNREACHED, key.get(b) ?? UNREACHED) ||\n compare(byId.get(a)!.name, byId.get(b)!.name) ||\n compare(a, b),\n )\n}\n\n// A walk states where its nodes lie, which the links alone do not. Starting\n// from the backbone in offset order, each node a walk visits for the first time\n// goes into the order immediately after the node that walk came from, and the\n// nodes a walk visits before it meets a placed one go immediately before it.\n//\n// That keeps every true boundary. A walk's step joins two nodes on the same\n// side of a node every haplotype crosses once, unless one of them is that\n// node, so by induction the nodes between two such boundaries stay one\n// unbroken stretch of the order and no link among them reaches across. On the\n// eight-haplotype KIV-2 cut (15,808 nodes) the walks imply 28 bubbles and this\n// derives those 28, with 5 of 65,776 walk steps against the order. Placing\n// every node by the links alone derived 21, with 23,308 steps against it.\n//\n// A walk on the reverse strand is read end-first.\nfunction placeByWalks(graph: Graph, backbone: AnchoredNode[]) {\n const isNode = new Set(graph.nodes.map(n => n.id))\n const startOf = new Map(backbone.map(n => [n.id, n.stable.start]))\n const next = new Map<string, string | undefined>()\n const prev = new Map<string, string | undefined>()\n backbone.forEach((n, i) => {\n prev.set(n.id, backbone[i - 1]?.id)\n next.set(n.id, backbone[i + 1]?.id)\n })\n let head = backbone[0]?.id\n const insertAfter = (at: string, id: string) => {\n const after = next.get(at)\n next.set(at, id)\n prev.set(id, at)\n next.set(id, after)\n if (after !== undefined) {\n prev.set(after, id)\n }\n }\n const insertBefore = (at: string, id: string) => {\n const before = prev.get(at)\n if (before === undefined) {\n head = id\n prev.set(id, undefined)\n next.set(id, at)\n prev.set(at, id)\n } else {\n insertAfter(before, id)\n }\n }\n\n for (const path of graph.paths ?? []) {\n let rising = 0\n let falling = 0\n let last: number | undefined\n for (const id of path.nodeIds) {\n const start = startOf.get(id)\n if (start !== undefined) {\n if (last !== undefined && start !== last) {\n if (start > last) {\n rising++\n } else {\n falling++\n }\n }\n last = start\n }\n }\n const ids = falling > rising ? [...path.nodeIds].reverse() : path.nodeIds\n let from: string | undefined\n const leading = new Set<string>()\n for (const id of ids) {\n if (next.has(id)) {\n if (from === undefined) {\n for (const lead of leading) {\n insertBefore(id, lead)\n }\n }\n from = id\n } else if (isNode.has(id)) {\n if (from === undefined) {\n leading.add(id)\n } else {\n insertAfter(from, id)\n from = id\n }\n }\n }\n }\n\n const placed: string[] = []\n for (let id = head; id !== undefined; id = next.get(id)) {\n placed.push(id)\n }\n return placed\n}\n\n// The nodes no walk visits, which is every off-reference node of a graph with\n// no walks: each goes beside the nearest placed node that reaches it (BFS over\n// the links as undirected, seeded in order so a tie goes left).\n//\n// That search meets itself in the middle of an allele, so the far half of a\n// chain A>X1>X2>B is claimed from B, its RIGHT anchor. Placed after B, as every\n// claimed node once was, X2 sorted past the node it leads into, its link was\n// turned round, and the insertion stopped being a bubble. So a run of nodes\n// claimed from one placed node lies BEFORE it, farthest node first, when it is\n// such a far half: it touches nothing right of that node, and every dead end of\n// the search inside it is a place the search from the left arrived. Steps are\n// a position only along a chain. In a tangle the farthest node is a dead end\n// with nothing before it, and sorted first its links to its real neighbours\n// sweep across every boundary between, so any other run stays after.\nfunction placeBySearch(\n adjacent: Map<string, string[]>,\n key: Map<string, OrderKey>,\n) {\n const queue = [...key.entries()]\n .sort(([a, ka], [b, kb]) => compareKeys(ka, kb) || compare(a, b))\n .map(([id]) => id)\n const placed = queue.length\n // the queue grows while the loop walks it, which for-of follows\n for (const id of queue) {\n const from = key.get(id)!\n for (const nb of adjacent.get(id) ?? []) {\n if (!key.has(nb)) {\n key.set(nb, { at: from.at, side: 1, hops: from.hops + 1 })\n queue.push(nb)\n }\n }\n }\n\n const searched = new Set(queue.slice(placed))\n const sided = new Set<string>()\n for (const seed of searched) {\n if (sided.has(seed)) {\n continue\n }\n const { at } = key.get(seed)!\n const run = [seed]\n sided.add(seed)\n let farHalf = true\n for (const id of run) {\n const { hops } = key.get(id)!\n let deadEnd = true\n let touchesLeft = false\n for (const nb of adjacent.get(id) ?? []) {\n const other = key.get(nb)!\n if (other.at === at && searched.has(nb)) {\n deadEnd &&= other.hops <= hops\n if (!sided.has(nb)) {\n sided.add(nb)\n run.push(nb)\n }\n } else if (other.at !== at) {\n touchesLeft ||= other.at < at\n farHalf &&= other.at < at\n }\n }\n farHalf &&= !deadEnd || touchesLeft\n }\n if (farHalf) {\n for (const id of run) {\n const k = key.get(id)!\n key.set(id, { ...k, side: -1, hops: -k.hops })\n }\n }\n }\n}\n\nfunction orderKeys(graph: Graph) {\n const ids = new Set(graph.nodes.map(n => n.id))\n const adjacent = new Map<string, string[]>()\n for (const e of graph.edges) {\n if (!ids.has(e.from) || !ids.has(e.to)) {\n continue\n }\n ;(adjacent.get(e.from) ?? adjacent.set(e.from, []).get(e.from)!).push(e.to)\n ;(adjacent.get(e.to) ?? adjacent.set(e.to, []).get(e.to)!).push(e.from)\n }\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort(\n (a, b) =>\n compare(a.stable.start, b.stable.start) || compare(a.name, b.name),\n )\n const key = new Map<string, OrderKey>()\n placeByWalks(graph, backbone).forEach((id, rank) => {\n key.set(id, { at: rank, side: 0, hops: 0 })\n })\n placeBySearch(adjacent, key)\n return key\n}\n\nexport interface LayeredGraph {\n layers: string[][]\n layerOf: Map<string, number>\n // predecessors in layer order, for the y sweep\n preds: Map<string, string[]>\n}\n\n// The layered DAG the ordered layout draws and the bubble decomposition reads\n// (bubbles/bubblesFromGraph.ts), from one function so the two cannot disagree\n// about which nodes share a layer.\nexport function layerGraph(graph: Graph): LayeredGraph {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const order = referenceOrder(graph)\n const key = new Map(order.map((id, i) => [id, i]))\n const isRef = (id: string) => isBackbone(byId.get(id)!)\n\n // Every edge directed from lower reference key to higher, so the graph is\n // acyclic by construction and no cycle-removal heuristic can misplace the\n // reference.\n const preds = new Map<string, string[]>(order.map(id => [id, []]))\n const succs = new Map<string, string[]>(order.map(id => [id, []]))\n const link = (a: string, b: string) => {\n preds.get(b)!.push(a)\n succs.get(a)!.push(b)\n }\n for (const e of graph.edges) {\n if (e.from === e.to || !key.has(e.from) || !key.has(e.to)) {\n continue\n }\n if (key.get(e.from)! < key.get(e.to)!) {\n link(e.from, e.to)\n } else {\n link(e.to, e.from)\n }\n }\n // The backbone chained by virtual edges, so the reference stays monotone even\n // where adjacent segments are not linked inside the cut.\n const backbone = order.filter(isRef)\n for (let i = 1; i < backbone.length; i++) {\n link(backbone[i - 1]!, backbone[i]!)\n }\n\n // Longest-path layering; the key order is a topological order of this DAG.\n const layerOf = new Map<string, number>()\n for (const id of order) {\n let l = 0\n for (const p of preds.get(id)!) {\n l = Math.max(l, layerOf.get(p)! + 1)\n }\n layerOf.set(id, l)\n }\n // Then each allele slides right to centre between the layer its predecessors\n // force and the one its successors allow, so a short allele sits in the\n // middle of its bubble rather than hugging the left anchor.\n for (let i = order.length - 1; i >= 0; i--) {\n const id = order[i]!\n const s = succs.get(id)!\n if (isRef(id) || s.length === 0) {\n continue\n }\n let minSucc = Infinity\n for (const t of s) {\n minSucc = Math.min(minSucc, layerOf.get(t)!)\n }\n const lo = layerOf.get(id)!\n const hi = Math.max(lo, minSucc - 1)\n layerOf.set(id, Math.floor((lo + hi) / 2))\n }\n\n const layers: string[][] = []\n for (const id of order) {\n ;(layers[layerOf.get(id)!] ??= []).push(id)\n }\n return { layers, layerOf, preds }\n}\n\nexport function orderedLayout(graph: Graph): LayoutResult | undefined {\n if (!graph.nodes.some(isBackbone)) {\n return undefined\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const isRef = (id: string) => isBackbone(byId.get(id)!)\n const { layers, layerOf: layer, preds } = layerGraph(graph)\n\n const layerWidth = layers.map(ids => {\n let w = 0\n for (const id of ids) {\n w = Math.max(w, nodeWidth(byId.get(id)!))\n }\n return w\n })\n const layerX: number[] = []\n let x = 0\n for (let l = 0; l < layers.length; l++) {\n layerX[l] = x\n x += layerWidth[l]! + GUTTER\n }\n\n // y: sweep the layers left to right, each allele at the barycenter of its\n // placed predecessors, then at the nearest FREE lane to it. Lane 0 is the\n // reference line and stays reserved even in a layer with no backbone node,\n // or an allele between two backbone segments reads as reference.\n const y = new Map<string, number>()\n for (const ids of layers) {\n const free: { id: string; ideal: number }[] = []\n for (const id of ids) {\n if (isRef(id)) {\n y.set(id, 0)\n continue\n }\n const placed = preds.get(id)!.filter(p => y.has(p))\n let ideal = 0\n for (const p of placed) {\n ideal += y.get(p)! / placed.length\n }\n // a node straight off the backbone wants to leave it, not sit on it\n free.push({ id, ideal: ideal === 0 ? LANE_PX : ideal })\n }\n free.sort((a, b) => a.ideal - b.ideal)\n const taken = new Set([0])\n for (const { id, ideal } of free) {\n let lane = Math.round(ideal / LANE_PX)\n if (lane === 0) {\n lane = ideal >= 0 ? 1 : -1\n }\n for (let d = 1; taken.has(lane); d++) {\n if (!taken.has(lane + d)) {\n lane += d\n } else if (!taken.has(lane - d)) {\n lane -= d\n }\n }\n taken.add(lane)\n y.set(id, lane * LANE_PX)\n }\n }\n\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const n of graph.nodes) {\n const l = layer.get(n.id)!\n const w = nodeWidth(n)\n const cx = layerX[l]! + layerWidth[l]! / 2\n const ny = y.get(n.id)!\n nodePositions[n.id] = [\n { x: cx - w / 2, y: ny },\n { x: cx + w / 2, y: ny },\n ]\n }\n return { nodePositions, referenceAxis: false, pixelRows: true }\n}\n", "import { placeOffReference } from './placeOffReference'\nimport { ROW_HEIGHT_PX } from './rowSpacing'\nimport { parsePanSN } from '../alleleProjection/projectAlleles'\nimport {\n backboneNodes,\n backbonePositions,\n isOffReference,\n referenceSpan,\n} from '../anchoredNodes'\n\nimport type { Graph, LayoutResult, RowLabel } from '../types'\n\n// One row per contributing assembly, x on the reference.\n//\n// The anchored layout rows by *rank*, which is a property of the graph's\n// construction order rather than of any sample: at an HPRC locus rank 1 holds\n// alleles from dozens of different haplotypes, so a row there means nothing\n// biological. This layout rows by the assembly each allele came from, so a row\n// is one sample and reading across it says what that sample does to the\n// reference: a deletion leaves the row empty across the span it removes, and an\n// insertion is a mark at the point it attaches. Insertion *size* is deliberately\n// not on this axis \u2014 see placeOffReference.\n//\n// Positions come from `projectAlleles`, which derives them from the stable\n// coordinates and the L-lines alone. rGFA has no W/P records to consult, so\n// anything requiring walks could not run on the format the graph tracks read.\n//\n// What a row means differs with where the coordinates came from, and the\n// difference is not cosmetic. On rGFA a segment's SN names the assembly that\n// *first contributed* it (SR is build order), so a row is first-seen\n// attribution. On a path-anchored graph it names an assembly that actually\n// carries the segment. Carriage is a set, though, and a row here is one node's\n// one position: where several assemblies carry a segment, it draws on the row\n// of the first path in the file that visits it, and `GraphNode.samples` holds\n// the rest. Drawing a segment once per carrier needs the layout to emit more\n// nodes than the graph has, which the renderer keys by node id and cannot do.\n\n// Every assembly with a segment in this subgraph, ordered by how much\n// off-reference sequence it contributes here, most first. Taken from the nodes\n// rather than from the projection's per-allele attribution: a bubble path can\n// mix assemblies, and a segment belongs on the row of the assembly its own\n// stable name gives it, not on the row of whichever contributor happens to\n// dominate the run it sits in.\n//\n// Read off the same field `rowY` places by, so every row that exists has nodes\n// on it and every node lands on a row that exists.\n//\n// **Sorted by content rather than by name**, which is the change and the\n// tradeoff. Alphabetical put `HG00099` above `HG00280` for no reason a reader\n// could see, so a row's neighbours said nothing and the stack had to be read one\n// row at a time. By contributed sequence it reads the way a sorted pileup does:\n// the haplotypes doing the most to the reference are together at the top, and\n// how far down the stack the marks stop IS how many samples carry anything.\n//\n// What that costs is comparing one window against another \u2014 the order is a fact\n// about the window, so a sample is not in the same place in two of them. That is\n// the case \u00A78's \"a row set cannot be requested\" is for, and an explicit sample\n// list is the right answer to it rather than an order nobody can read. A\n// follow's re-cut is the exception it can answer already: `keep` is the rows on\n// screen, those samples hold their order, and a sample new to the window goes\n// below them by content.\n//\n// Name breaks ties, so the order is still deterministic and a pan that changes\n// nothing about the content reshuffles nothing.\nfunction contributingSamples(graph: Graph, keep: readonly string[]) {\n const carried = new Map<string, number>()\n for (const node of graph.nodes) {\n if (isOffReference(node)) {\n const { sample } = parsePanSN(node.stable.refName)\n carried.set(sample, (carried.get(sample) ?? 0) + node.length)\n }\n }\n const held = new Map(keep.map((sample, row) => [sample, row]))\n return [...carried.keys()].sort(\n (a, b) =>\n (held.get(a) ?? Infinity) - (held.get(b) ?? Infinity) ||\n carried.get(b)! - carried.get(a)! ||\n a.localeCompare(b),\n )\n}\n\nexport function sampleRowLayout(\n graph: Graph,\n region?: { start: number; end: number },\n keep: readonly string[] = [],\n): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n const samples = contributingSamples(graph, keep)\n if (backbone.length === 0 || samples.length === 0) {\n return undefined\n }\n const span = referenceSpan(backbone, region)\n\n // The backbone keeps row 0 and every sample gets a row below it, in the\n // order contributingSamples gives.\n const rowOf = new Map(samples.map((s, i) => [s, i + 1]))\n const nodePositions = backbonePositions(backbone)\n\n // `placeOffReference` also reaches nodes with no coordinate at all: it walks\n // out from what it placed to close a run that leaves the fetched window, and\n // on a path-anchored graph a segment no P or W line visits never got a\n // `stable` to begin with. That is legal GFA rather than a broken file, so\n // asserting the coordinate here threw a bare \"cannot read properties of\n // undefined\" out of the layout and left the view showing it instead of a\n // graph. Such a node belongs on no sample's row, so it takes the spare row\n // below the named ones, which is what the sibling layout does with an\n // unrecognised rank.\n const spareRow = samples.length + 1\n const alleleDeletions = placeOffReference({\n graph,\n span,\n rowY: node => {\n const stable = node.stable\n const row = stable\n ? rowOf.get(parsePanSN(stable.refName).sample)\n : undefined\n return (row ?? spareRow) * ROW_HEIGHT_PX\n },\n positions: nodePositions,\n })\n\n // Built from the same `rowOf` map that placed the alleles, so a label cannot\n // name a row the layout put somewhere else. Row 0 is the backbone, named for\n // the assembly it comes from rather than \"reference\", because on this layout\n // that name is one of the samples and reads as a peer of the rows below it.\n const referenceSample = parsePanSN(backbone[0]!.stable.refName).sample\n const rowLabels: RowLabel[] = [\n { label: referenceSample, y: 0 },\n ...samples.map(sample => ({\n label: sample,\n y: rowOf.get(sample)! * ROW_HEIGHT_PX,\n })),\n ]\n\n return {\n nodePositions,\n rowLabels,\n sampleRows: samples,\n referenceAxis: true,\n pixelRows: true,\n alleleDeletions,\n }\n}\n", "export function emptyTrackShapes() {\n return {\n rectangles: [],\n curves: [],\n corners: [],\n verticalRectangles: [],\n featureRectangles: [],\n };\n}\n", "// The tube map layout, from input nodes and tracks to drawable shapes. Ported\n// from the original sequenceTubeMap JS, where it shared one module with the d3\n// drawing; the passes below still share module state, but layoutTubeMap resets\n// all of it on entry and runs synchronously, so no two calls see each other's.\nimport { emptyTrackShapes } from './types.js';\nconst DEBUG = false;\nconst DEFAULT_TRACK_COLORS = [\n '#1f77b4',\n '#ff7f0e',\n '#2ca02c',\n '#d62728',\n '#9467bd',\n '#8c564b',\n '#e377c2',\n '#7f7f7f',\n];\nfunction configFrom(options) {\n return {\n mergeNodesFlag: options.mergeNodes ?? true,\n showReads: options.showReads ?? true,\n coarsenedReadView: options.coarsenedReadView ?? false,\n ignoreStrand: options.ignoreStrand ?? false,\n nodeWidthOption: options.nodeWidthOption ?? 'normal',\n charWidth: options.charWidth ?? 8.401,\n trackWidth: options.trackWidth ?? 15,\n mappingQualityCutoff: options.mappingQualityCutoff ?? 0,\n focusReadNames: options.focusReadNames ?? null,\n showExonsFlag: options.showExons ?? false,\n trackColor: options.trackColor ??\n (track => DEFAULT_TRACK_COLORS[track.id % DEFAULT_TRACK_COLORS.length]),\n trackAlpha: options.trackAlpha ?? (() => 1),\n };\n}\nlet config = configFrom({});\nlet bed = null;\nlet nodes = [];\n// Each track has a `path`, which is an array of Segment objects describing pieces of the path that need to be drawn, in order along the path.\nlet tracks = [];\n// Each read also has a `path` list of Segments, but reads are organized vertically using a different system than non-read tracks.\nlet reads = [];\nlet nodeMap = new Map();\nlet nodesPerOrder = [];\n// Scratch array used only during generateNodeOrder. Indexed by node index;\n// undefined = \"not yet assigned.\" Copied into node.order at end of layout.\nlet nodeOrders = [];\n// Lane assignment info for tracks, in one list per horizontal \"order\" slot.\n// Duplicates info in tracks' `path` lists but is organized by order. Reads do not use this.\nlet assignments = [];\nlet extraLeft = [];\nlet extraRight = [];\nlet maxOrder = -1; // horizontal order of the rightmost node\nlet shapes = emptyTrackShapes();\nlet imageBounds = { minX: 0, maxX: 0, minY: 0, maxY: 0 };\nlet trackForRuler;\nlet coarsenedEdgeMeta = new Map();\n// Lay out `inputNodes` and `inputTracks` (and `inputReads`, when\n// `showReads`), or return undefined when nothing visible is left to draw.\n// Neither input is modified. Track 0 is the reference the layout straightens.\nexport function layoutTubeMap(inputNodes, inputTracks, inputReads = [], options = {}) {\n config = configFrom(options);\n bed = options.bed ?? null;\n shapes = emptyTrackShapes();\n assignments = [];\n extraLeft = [];\n extraRight = [];\n nodesPerOrder = [];\n imageBounds = { minX: 0, maxX: 0, minY: 0, maxY: 0 };\n trackForRuler = undefined;\n coarsenedEdgeMeta = new Map();\n if (inputNodes.length === 0 || inputTracks.length === 0) {\n return undefined;\n }\n // Boundary promotion: layout passes below populate the Node/Track fields\n // (width, order, x/y, path, indexSequence, etc.) before any reader runs.\n const copies = deepCopy(inputNodes);\n // Nodes are referenced in inputs by internal `name` attribute and not by\n // index. Internally in e.g. a path's indexSequence we need to reference\n // nodes by *signed* index, so index 0 can never be used: budge everything\n // down and leave a hole (rather than an `undefined` entry) at 0, which we\n // won't iterate over. Made after the copy, because not every structuredClone\n // keeps a hole.\n nodes = [];\n copies.forEach((node, i) => {\n nodes[i + 1] = node;\n });\n tracks = deepCopy(inputTracks);\n // Whether any reads were loaded at all, distinct from `reads.length` below:\n // a mapping-quality cutoff or focus-name filter can filter every read out,\n // and that should not make the coarsened view fall back to bunching\n // haplotypes instead \u2014 the graph does have reads, they're just all hidden.\n const hadInputReads = inputReads.length > 0;\n // Drop the reads we will never draw before cloning them \u2014 the deep copy of a\n // large GAM is the single most expensive step in a redraw.\n reads = deepCopy(filterReads(inputReads));\n for (let i = tracks.length - 1; i >= 0; i -= 1) {\n const t = tracks[i];\n if (t.type === undefined) {\n t.type = 'haplotype';\n }\n if (t.hidden === true) {\n tracks.splice(i, 1);\n continue;\n }\n if (t.indexOfFirstBase !== undefined) {\n trackForRuler = t.name;\n }\n }\n if (tracks.length === 0) {\n return undefined;\n }\n // Run against the visible tracks only, so hiding the reference doesn't leave\n // the layout straightened around a track that is no longer drawn.\n straightenTrack(0);\n nodeMap = generateNodeMap();\n generateTrackIndexSequences(tracks);\n if (config.showReads && reads.length > 0)\n generateTrackIndexSequences(reads);\n generateNodeWidth();\n if (config.mergeNodesFlag) {\n generateNodeSuccessors();\n generateNodeOrder();\n if (config.showReads && reads.length > 0)\n reverseReversedReads();\n mergeNodes();\n nodeMap = generateNodeMap();\n generateNodeWidth();\n generateTrackIndexSequences(tracks);\n if (config.showReads && reads.length > 0)\n generateTrackIndexSequences(reads);\n }\n generateNodeSuccessors();\n generateNodeDegree();\n if (DEBUG)\n console.log(`${nodes.length} nodes.`);\n generateNodeOrder();\n maxOrder = getMaxOrder();\n // can cause problems when there is a reversed single track node\n // OTOH, can solve problems with complex inversion patterns\n switchNodeOrientation();\n generateNodeOrder();\n maxOrder = getMaxOrder();\n // Coarsened (Sankey) mode normally collapses the *read* list into synthetic\n // per-edge bands (below). A haplotype-only graph has no reads to coarsen,\n // so a coarsened request there instead coarsens every haplotype but the\n // reference: that one keeps its normal per-track lane (so it still carries\n // the ruler) while the rest are pulled out of `tracks` here, before\n // calculateTrackWidth/generateLaneAssignment ever see them, and rejoin the\n // layout below through the same reads-style overlay used for coarsened\n // reads.\n const coarsenHaplotypes = config.showReads && config.coarsenedReadView && !hadInputReads;\n if (coarsenHaplotypes) {\n const rulerIndex = trackForRuler === undefined\n ? -1\n : tracks.findIndex(t => t.name === trackForRuler);\n const refIndex = rulerIndex === -1 ? 0 : rulerIndex;\n const altHaplotypes = tracks.filter((_, i) => i !== refIndex);\n if (altHaplotypes.length > 0) {\n const bands = buildCoarsenedSyntheticBands(altHaplotypes, 'haplotype');\n // A haplotype that only ever visits one node produces no edge, so an\n // all-single-node set of alts would otherwise leave `reads` empty and\n // fall through to the no-overlay branch below, which never gives their\n // nodes a y/contentHeight. Keep them off to the side instead of\n // dropping them silently.\n if (bands.length > 0) {\n tracks = [tracks[refIndex]];\n reads = bands;\n }\n }\n }\n calculateTrackWidth();\n generateLaneAssignment();\n if (config.showExonsFlag && bed !== null)\n addTrackFeatures();\n // Coarsened (Sankey) mode: collapse the read list (or, when coarsening\n // haplotypes, the alt haplotypes pulled out above) to one synthetic \"read\"\n // per (srcSigned \u2192 dstSigned) edge BEFORE the normal read placement runs.\n // Each synthetic read traverses exactly two nodes, so the rest of the\n // pipeline (placeReads, generateSVGShapesFromPath, curve drawing) handles\n // it like any normal read: it gets a lane, picks up the \"right-down-left-\n // up-right\" topology for loops automatically, and uses the same elegant\n // bezier. Node heights end up proportional to *edge* count (typically\n // tens) rather than *read* or *haplotype* count (potentially thousands).\n const drawCoarsenedReads = config.showReads &&\n config.coarsenedReadView &&\n reads.length > 0 &&\n !coarsenHaplotypes;\n if (config.showReads && reads.length > 0) {\n generateReadOnlyNodeAttributes();\n reverseReversedReads();\n generateTrackIndexSequences(reads);\n if (drawCoarsenedReads) {\n reads = buildCoarsenedSyntheticBands(reads, 'read');\n reverseReversedReads();\n generateTrackIndexSequences(reads);\n }\n placeReads();\n tracks = tracks.concat(reads);\n }\n else {\n nodes.forEach(node => {\n if (node) {\n node.incomingReads = [];\n node.outgoingReads = [];\n node.internalReads = [];\n }\n });\n }\n generateNodeXCoords();\n generateSVGShapesFromPath();\n if (DEBUG) {\n console.log('Tracks:');\n console.log(tracks);\n console.log('Nodes:');\n console.log(nodes);\n console.log('Lane assignment:');\n console.log(assignments);\n }\n getImageDimensions();\n return {\n nodes,\n tracks,\n reads,\n nodeMap,\n shapes,\n bounds: imageBounds,\n maxOrder,\n trackForRuler,\n coarsenedEdgeMeta,\n };\n}\nfunction generateTrackColor(track, highlight = 'plain') {\n return config.trackColor(track, highlight);\n}\nfunction generateTrackAlpha(track) {\n return config.trackAlpha(track);\n}\n// structuredClone preserves sparse-array holes; JSON round-trip would fill them with null.\nfunction deepCopy(val) {\n return structuredClone(val);\n}\n// Return true if the given name names a reverse strand node, and false otherwise.\nexport function isReverse(nodeName) {\n const s = String(nodeName);\n return s.length >= 1 && s.startsWith('-');\n}\n// Get the forward version of a node name, which may be either forward or backward (negative)\nexport function forward(nodeName) {\n return isReverse(nodeName) ? String(nodeName).substring(1) : nodeName;\n}\n// Get the reverse version of a node name, which may be either forward or backward (negative)\nexport function reverse(nodeName) {\n return isReverse(nodeName) ? nodeName : `-${nodeName}`;\n}\n// Get the opposite orientation node name for the given node.\nexport function flip(nodeName) {\n return isReverse(nodeName) ? forward(nodeName) : reverse(nodeName);\n}\n// Signed-index orientation test. Uses 1/n to distinguish -0 from +0 since\n// indexSequence entries can be negative-zero for reverse-strand visits of node 0.\nexport function isForwardIndex(n) {\n return (n || 1 / n) >= 0;\n}\n// straighten track given by index by inverting inverted nodes\n// only keep them inverted if this single track runs through them in both directions\n// Operates on the per-render `nodes`/`tracks` copies; inputs are left untouched.\nfunction straightenTrack(index) {\n const nodesToInvert = new Set();\n // find out which nodes should be inverted\n let currentSequence = tracks[index].sequence;\n for (let i = 0; i < currentSequence.length; i += 1) {\n const cur = currentSequence[i];\n if (isReverse(cur)) {\n const nodeName = forward(cur);\n const firstForwardIndex = currentSequence.indexOf(nodeName);\n if (firstForwardIndex === -1 || firstForwardIndex > i) {\n // only if this inverted node is no repeat\n nodesToInvert.add(nodeName);\n }\n }\n }\n // invert nodes in the tracks' sequence\n for (const track of tracks) {\n currentSequence = track.sequence;\n for (let j = 0; j < currentSequence.length; j += 1) {\n const cur = currentSequence[j];\n if (!isReverse(cur)) {\n if (nodesToInvert.has(cur)) {\n currentSequence[j] = reverse(cur);\n }\n }\n else if (nodesToInvert.has(forward(cur))) {\n currentSequence[j] = forward(cur);\n }\n }\n }\n // invert the sequence within the nodes\n nodes.forEach(node => {\n if (node && nodesToInvert.has(node.name)) {\n node.seq = node.seq.split('').reverse().join('');\n }\n });\n}\n// generates attributes (node.y, node.contentHeight) for nodes without tracks, only reads\nfunction generateReadOnlyNodeAttributes() {\n nodesPerOrder = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n nodesPerOrder[i] = [];\n }\n const orderY = new Map();\n nodes.forEach(node => {\n if (node.y !== undefined) {\n setMapToMax(orderY, node.order, node.y + node.contentHeight);\n }\n });\n // for order values where there is no node with haplotypes, orderY is calculated via tracks\n tracks.forEach(track => {\n if (track.type === 'haplotype') {\n track.path.forEach(step => {\n setMapToMax(orderY, step.order, (step.y ?? 0) + track.width);\n });\n }\n });\n nodes.forEach((node, i) => {\n if (node.order >= 0 && node.y === undefined) {\n node.y = (orderY.get(node.order) ?? 0) + 25;\n node.contentHeight = 0;\n nodesPerOrder[node.order].push(i);\n }\n });\n}\nfunction setMapToMax(map, key, value) {\n const existing = map.get(key);\n if (existing === undefined) {\n map.set(key, value);\n }\n else {\n map.set(key, Math.max(existing, value));\n }\n}\nexport const READ_WIDTH = 7;\n// add info about reads to nodes (incoming, outgoing and internal reads)\nfunction assignReadsToNodes() {\n nodes.forEach(node => {\n if (node) {\n node.incomingReads = [];\n node.outgoingReads = [];\n node.internalReads = [];\n }\n });\n reads.forEach((read, idx) => {\n // Honor a pre-set width (e.g. coarsened Sankey bands pre-size themselves\n // by traversing-read count) \u2014 only fall back to the default for normal\n // reads which leave width unset.\n if (read.width === undefined || read.width === 0) {\n read.width = READ_WIDTH;\n }\n if (read.path.length === 1) {\n const firstNode = read.path[0].node;\n if (firstNode !== null) {\n nodes[firstNode].internalReads.push(idx);\n }\n }\n else {\n read.path.forEach((element, pathIdx) => {\n if (pathIdx === 0) {\n const firstNode = read.path[0].node;\n if (firstNode !== null) {\n nodes[firstNode].outgoingReads.push([idx, pathIdx]);\n }\n }\n else {\n const elemNode = element.node;\n if (elemNode !== null) {\n nodes[elemNode].incomingReads.push([idx, pathIdx]);\n }\n }\n });\n }\n });\n}\n// calculate paths (incl. correct y coordinate) for all reads\nfunction placeReads() {\n generateBasicPathsForReads();\n assignReadsToNodes();\n // sort nodes by order, then by y-coordinate\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n // Organize read IDs by source track\n const readsBySource = new Map();\n for (let i = 0; i < reads.length; i++) {\n const source = reads[i].sourceTrackID;\n const bucket = readsBySource.get(source);\n if (bucket === undefined) {\n readsBySource.set(source, [i]);\n }\n else {\n bucket.push(i);\n }\n }\n const allSources = Array.from(readsBySource.keys()).sort((a, b) => a - b);\n // Space out read tracks if multiple exist\n const topMargin = allSources.length > 1 ? READ_WIDTH : 0;\n sortedNodes.forEach(node => {\n if (!node)\n return;\n // For each node\n for (const source of allSources) {\n // Go through all source tracks in order\n // Place the reads from this source in this node.\n // Use a margin to separate multiple read tracks if we have them.\n placeReadSet(readsBySource.get(source), node, topMargin);\n }\n });\n // place read segments which are without node\n const bottomY = calculateBottomY();\n const elementsWithoutNode = [];\n reads.forEach((read, idx) => {\n const len = read.path.length;\n // For each path index, precompute the nearest preceding/following segment\n // whose node is non-null and truthy. Falls back to the boundary segment when\n // none qualifies, matching the original walking-loop semantics.\n const prevSegment = new Array(len);\n let lastValidPrev;\n for (let i = 0; i < len; i++) {\n if (i === 0) {\n prevSegment[i] = undefined;\n }\n else {\n prevSegment[i] = lastValidPrev ?? read.path[0];\n }\n const seg = read.path[i];\n if (seg.node !== null && seg.node)\n lastValidPrev = seg;\n }\n const nextSegment = new Array(len);\n let firstValidNext;\n for (let i = len - 1; i >= 0; i--) {\n if (i === len - 1) {\n nextSegment[i] = undefined;\n }\n else {\n nextSegment[i] = firstValidNext ?? read.path[len - 1];\n }\n const seg = read.path[i];\n if (seg.node !== null && seg.node)\n firstValidNext = seg;\n }\n read.path.forEach((element, pathIdx) => {\n if (element.y === undefined) {\n const previousVisitToNode = prevSegment[pathIdx];\n const nextVisitToNode = nextSegment[pathIdx];\n // Specifically referring to segments between a cycle that's traversing from right to left\n const betweenCycleReverseTraversal = Boolean(\n // A segment can be between a cycle if it there are nodes on both sides\n nextVisitToNode &&\n previousVisitToNode &&\n nextVisitToNode.isForward !== undefined &&\n previousVisitToNode.isForward !== undefined &&\n // A segment is between a cycle if the next node it visits is behind the previous node it visited\n (previousVisitToNode.order > nextVisitToNode.order ||\n // A segment can also be between a cycle if it's visiting the same node it just visited in the same direction\n (nextVisitToNode.order === previousVisitToNode.order &&\n nextVisitToNode.isForward === previousVisitToNode.isForward)));\n element.betweenCycleReverseTraversal = betweenCycleReverseTraversal;\n elementsWithoutNode.push({\n readIndex: idx,\n pathIndex: pathIdx,\n previousY: previousVisitToNode?.y,\n previousNode: previousVisitToNode?.node,\n });\n }\n });\n });\n elementsWithoutNode.sort(compareNoNodeReads);\n elementsWithoutNode.forEach(element => {\n const read = reads[element.readIndex];\n const segment = read.path[element.pathIndex];\n segment.y = bottomY[segment.order];\n bottomY[segment.order] += read.width;\n });\n if (DEBUG) {\n console.log('Reads:');\n console.log(reads);\n }\n}\n// Place a particular collection of reads, identified by a list of read\n// numbers, into the given node at the right Y coordinates. All reads in all\n// nodes above it, and no reads in any nodes below it, are already placed.\n// Makes the given node bigger if needed and moves other nodes down if needed.\n// If topMargin is set, applies that amount of spacing down from whatever is above the reads.\nfunction placeReadSet(readIDs, node, topMargin) {\n // Turn the read IDs into a set\n const toPlace = new Set(readIDs);\n // Get arrays of the read entry/exit/internal-ness records we want to work on\n let incomingReads = node.incomingReads.filter(([readID]) => toPlace.has(readID));\n const outgoingReads = node.outgoingReads.filter(([readID]) => toPlace.has(readID));\n const internalReads = node.internalReads.filter(readID => toPlace.has(readID));\n // Only actually use the top margin if we have any reads on the node.\n if (incomingReads.length === 0 &&\n outgoingReads.length === 0 &&\n internalReads.length === 0) {\n topMargin = 0;\n }\n // Determine where we start vertically in the node.\n // TODO: Why do we have to double this to keep reads out of adjacent lanes???\n const startY = node.y + node.contentHeight + topMargin * 2;\n // sort incoming reads \u2014 decorate with a precomputed key so each compare is O(1).\n // The original comparator walks pathIdx backwards in lockstep on both sides, picking\n // the first step where either has a defined y (or reaches index 0). The walk's outcome\n // for a single entry is fully captured by:\n // decisionStep: the smallest k>=1 where path[pathIdx-k].y is defined, or pathIdx+1\n // if no preceding segment has a defined y (the \"reached 0\" case).\n // y: the defined y at decisionStep, or undefined for \"reached 0\".\n // Smaller decisionStep wins; ties put the \"reached 0\" entries first;\n // otherwise compare y's.\n const incomingKeys = incomingReads.map(entry => {\n const [readID, pathIdx] = entry;\n const path = reads[readID].path;\n let decisionStep = pathIdx + 1;\n let foundY;\n for (let k = 1; k <= pathIdx; k++) {\n const y = path[pathIdx - k]?.y;\n if (y !== undefined) {\n decisionStep = k;\n foundY = y;\n break;\n }\n }\n return { entry, decisionStep, y: foundY };\n });\n incomingKeys.sort(compareIncomingReadKeys);\n incomingReads = incomingKeys.map(k => k.entry);\n // place incoming reads\n let currentY = startY;\n const occupiedUntil = new Map();\n incomingReads.forEach(readElement => {\n const read = reads[readElement[0]];\n read.path[readElement[1]].y = currentY;\n setOccupiedUntil(occupiedUntil, read, readElement[1], currentY, node);\n currentY += read.width;\n });\n let maxY = currentY;\n // sort outgoing reads\n outgoingReads.sort(compareReadOutgoingSegmentsByGoingTo);\n // place outgoing reads\n const occupiedFrom = new Map();\n currentY = startY;\n outgoingReads.forEach(readElement => {\n const read = reads[readElement[0]];\n const firstNodeOffset = read.firstNodeOffset ?? 0;\n // place in next lane\n read.path[readElement[1]].y = currentY;\n occupiedFrom.set(currentY, firstNodeOffset);\n // if no conflicts. The original predicate was strict-less-than, which\n // treats two reads that exactly touch (incoming ends at base K, outgoing\n // starts at base K+1) as conflicting. They don't overlap, so they can\n // share a lane \u2014 relax to <=. This also matters for coarsened Sankey\n // bands at single-base nodes (e.g. a 1bp \"G\" node): with edge-only offsets\n // the math becomes 0+1 < 1 (conflict, wrong) vs. 0+1 <= 1 (no conflict).\n const occUntil = occupiedUntil.get(currentY);\n if (occUntil === undefined || occUntil + 1 <= firstNodeOffset) {\n currentY += read.width;\n maxY = Math.max(maxY, currentY);\n }\n else {\n // otherwise push down incoming reads to make place for outgoing Read\n occupiedUntil.set(currentY, 0);\n incomingReads.forEach(incReadElementIndices => {\n const incRead = reads[incReadElementIndices[0]];\n const incReadPathElement = incRead.path[incReadElementIndices[1]];\n if (incReadPathElement.y !== undefined &&\n incReadPathElement.y >= currentY) {\n incReadPathElement.y += read.width;\n setOccupiedUntil(occupiedUntil, incRead, incReadElementIndices[1], incReadPathElement.y, node);\n }\n });\n currentY += read.width;\n maxY += read.width;\n }\n });\n // sort internal reads\n internalReads.sort(compareInternalReads);\n // place internal reads\n internalReads.forEach(readIdx => {\n const currentRead = reads[readIdx];\n const firstNodeOffset = currentRead.firstNodeOffset ?? 0;\n const finalNodeCoverLength = currentRead.finalNodeCoverLength ?? 0;\n currentY = startY;\n while (firstNodeOffset < (occupiedUntil.get(currentY) ?? -Infinity) + 2 ||\n finalNodeCoverLength > (occupiedFrom.get(currentY) ?? Infinity) - 3) {\n currentY += currentRead.width || READ_WIDTH;\n }\n currentRead.path[0].y = currentY;\n occupiedUntil.set(currentY, finalNodeCoverLength);\n maxY = Math.max(maxY, currentY);\n });\n // adjust node height and move other nodes vertically down\n const heightIncrease = maxY - node.y - node.contentHeight;\n node.contentHeight += heightIncrease;\n adjustVertically3(node, heightIncrease);\n}\n// Smaller decisionStep wins. On a tie, entries that walked off the front of the\n// path (no defined y) sort first and tie with each other; otherwise compare y.\nexport function compareIncomingReadKeys(a, b) {\n if (a.decisionStep !== b.decisionStep) {\n return a.decisionStep - b.decisionStep;\n }\n if (a.y === undefined || b.y === undefined) {\n return (a.y === undefined ? 0 : 1) - (b.y === undefined ? 0 : 1);\n }\n return a.y - b.y;\n}\n// keeps track of where reads end within nodes\nfunction setOccupiedUntil(map, read, pathIndex, y, node) {\n if (pathIndex === read.path.length - 1) {\n // last node of current read\n map.set(y, read.finalNodeCoverLength ?? 0);\n }\n else {\n // read covers the whole node\n map.set(y, node.sequenceLength);\n }\n}\n// compare read segments which are outside of nodes to sort them in a good horizontal\n// and then vertical display order.\nfunction compareNoNodeReads(a, b) {\n const readA = reads[a.readIndex];\n const readB = reads[b.readIndex];\n const segmentA = readA.path[a.pathIndex];\n const segmentB = readB.path[b.pathIndex];\n // Sort by order by segments\n if (segmentA.order !== segmentB.order) {\n return segmentA.order - segmentB.order;\n }\n // Sort by reads' source track\n if (readA.sourceTrackID !== readB.sourceTrackID) {\n return readA.sourceTrackID - readB.sourceTrackID;\n }\n // Sort by order of previous node\n if (a.previousNode && b.previousNode) {\n const prevNodeA = nodes[a.previousNode];\n const prevNodeB = nodes[b.previousNode];\n if (prevNodeA && prevNodeB && prevNodeA.order !== prevNodeB.order) {\n return prevNodeA.order - prevNodeB.order;\n }\n }\n // We want to sort in reverse order when the segment is along the reverse-going part of a cycle.\n // This ensures a loop that starts on the outside, stays on the outside,\n // and rolls up in order with other loops.\n const aPrev = a.previousY ?? 0;\n const bPrev = b.previousY ?? 0;\n if (segmentA.betweenCycleReverseTraversal &&\n segmentB.betweenCycleReverseTraversal) {\n return bPrev - aPrev;\n }\n else {\n return aPrev - bPrev;\n }\n}\n// compare read segments by where they are going to\nfunction compareReadOutgoingSegmentsByGoingTo([readIndexA, pathIndexA], [readIndexB, pathIndexB]) {\n // Expect two arrays both containing 2 integers.\n // The first index of each array contains the read index\n // The second index of each array contains the path index\n // Segments are first sorted by the y value of their last node,\n // then by the node they end on,\n // then by length in final node\n const readA = reads[readIndexA];\n const readB = reads[readIndexB];\n let previousValidYA;\n let previousValidYB;\n let lastPathIndexA = readA.path.length - 1;\n let lastPathIndexB = readB.path.length - 1;\n while (!previousValidYA && lastPathIndexA >= 0) {\n previousValidYA = readA.path[lastPathIndexA]?.y;\n lastPathIndexA -= 1;\n }\n while (!previousValidYB && lastPathIndexB >= 0) {\n previousValidYB = readB.path[lastPathIndexB]?.y;\n lastPathIndexB -= 1;\n }\n if (previousValidYA && previousValidYB) {\n return previousValidYA - previousValidYB;\n }\n // Couldn't find a valid y value for at least one of the reads, sort by which node reads end on\n const initialNodeA = readA.path[pathIndexA]?.node;\n const initialNodeB = readB.path[pathIndexB]?.node;\n let nodeA = initialNodeA != null ? nodes[initialNodeA] : null;\n let nodeB = initialNodeB != null ? nodes[initialNodeB] : null;\n // Follow the reads' paths until we find the node they diverge at\n // Or, they go through all the same nodes and we do a tiebreaker at the end\n while (nodeA != null && nodeB != null && nodeA === nodeB) {\n if (pathIndexA < readA.path.length - 1) {\n pathIndexA += 1;\n while (readA.path[pathIndexA]?.node === null)\n pathIndexA += 1; // skip null nodes in path\n const nextNodeIdx = readA.path[pathIndexA]?.node;\n nodeA = nextNodeIdx != null ? nodes[nextNodeIdx] : null;\n }\n else {\n nodeA = null;\n }\n if (pathIndexB < readB.path.length - 1) {\n pathIndexB += 1;\n while (readB.path[pathIndexB]?.node === null)\n pathIndexB += 1; // skip null nodes in path\n const nextNodeIdx = readB.path[pathIndexB]?.node;\n nodeB = nextNodeIdx != null ? nodes[nextNodeIdx] : null;\n }\n else {\n nodeB = null;\n }\n }\n if (nodeA != null) {\n if (nodeB != null)\n return compareNodesByOrder(nodeA, nodeB);\n return 1; // nodeB is null, nodeA not null\n }\n if (nodeB != null)\n return -1; // nodeB not null, nodeA null\n // both nodes are null -> both end in the same node\n const beginDiff = (readA.firstNodeOffset ?? 0) - (readB.firstNodeOffset ?? 0);\n if (beginDiff !== 0)\n return beginDiff;\n // break tie: both reads cover the same nodes and begin at the same position\n // One or both reads didn't have a previously valid Y value, compare by the endPosition of the read\n return (readA.finalNodeCoverLength ?? 0) - (readB.finalNodeCoverLength ?? 0);\n}\n// Compare tracks based on ordering at their first convergence\nfunction compareTrackByInitialOrdering(trackA, trackB) {\n // Find the first node where the two tracks converge, sort by layer.\n // Tracks with no path sort first, and two of them tie.\n if (trackA.path === undefined || trackB.path === undefined) {\n return ((trackA.path === undefined ? 0 : 1) - (trackB.path === undefined ? 0 : 1));\n }\n const pathA = trackA.path;\n const pathB = trackB.path;\n let AIndex = 0;\n let BIndex = 0;\n while (AIndex < pathA.length && BIndex < pathB.length) {\n const a = pathA[AIndex];\n const b = pathB[BIndex];\n const trackAOrder = a.order;\n const trackBOrder = b.order;\n if (trackAOrder === trackBOrder && a.node && b.node) {\n return (b.y ?? 0) - (a.y ?? 0);\n }\n else if (trackAOrder < trackBOrder) {\n AIndex += 1;\n }\n else if (trackAOrder > trackBOrder) {\n BIndex += 1;\n }\n else {\n // Orders are equal but are traversing out of nodes\n AIndex += 1;\n BIndex += 1;\n }\n }\n // Tracks do not converge, keep the same ordering\n return 0;\n}\n// compare 2 reads which are completely within a single node\nfunction compareInternalReads(idxA, idxB) {\n const a = reads[idxA];\n const b = reads[idxB];\n // compare by first base within first node\n const aFirst = a.firstNodeOffset ?? 0;\n const bFirst = b.firstNodeOffset ?? 0;\n if (aFirst < bFirst)\n return -1;\n else if (aFirst > bFirst)\n return 1;\n // compare by last base within last node\n const aLast = a.finalNodeCoverLength ?? 0;\n const bLast = b.finalNodeCoverLength ?? 0;\n if (aLast < bLast)\n return -1;\n else if (aLast > bLast)\n return 1;\n return 0;\n}\n// determine biggest y-coordinate for each order-value\nfunction calculateBottomY() {\n const bottomY = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n bottomY.push(0);\n }\n nodes.forEach(node => {\n if (node.y !== undefined) {\n bottomY[node.order] = Math.max(bottomY[node.order], node.y + node.contentHeight + 20);\n }\n });\n tracks.forEach(track => {\n track.path.forEach(element => {\n bottomY[element.order] = Math.max(bottomY[element.order], (element.y ?? 0) + track.width);\n });\n });\n return bottomY;\n}\n// generate path-info for each read\n// containing order, node and orientation, but no concrete coordinates\n// TODO: Duplicates a lot of the same work as generateLaneAssignment() does for non-read tracks.\nfunction generateBasicPathsForReads() {\n reads.forEach(read => {\n // add info for start of track\n let currentNodeIndex = Math.abs(read.indexSequence[0]);\n let currentNodeIsForward = isForwardIndex(read.indexSequence[0]);\n let currentNode = nodes[currentNodeIndex];\n let previousNode;\n let previousNodeIsForward;\n read.path = [];\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n for (let i = 1; i < read.sequence.length; i += 1) {\n previousNode = currentNode;\n previousNodeIsForward = currentNodeIsForward;\n currentNodeIndex = Math.abs(read.indexSequence[i]);\n currentNodeIsForward = isForwardIndex(read.indexSequence[i]);\n currentNode = nodes[currentNodeIndex];\n if (currentNode.order > previousNode.order) {\n if (!previousNodeIsForward) {\n // backward to forward at previous node\n read.path.push({\n order: previousNode.order,\n isForward: true,\n node: null,\n });\n }\n for (let j = previousNode.order + 1; j < currentNode.order; j += 1) {\n // forward without nodes\n read.path.push({ order: j, isForward: true, node: null });\n }\n if (!currentNodeIsForward) {\n // forward to backward at current node\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: currentNodeIndex,\n });\n }\n else {\n // current Node forward\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: currentNodeIndex,\n });\n }\n }\n else if (currentNode.order < previousNode.order) {\n if (previousNodeIsForward) {\n // turnaround from fw to bw at previous node\n read.path.push({\n order: previousNode.order,\n isForward: false,\n node: null,\n });\n }\n for (let j = previousNode.order - 1; j > currentNode.order; j -= 1) {\n // bachward without nodes\n read.path.push({ order: j, isForward: false, node: null });\n }\n if (currentNodeIsForward) {\n // backward to forward at current node\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: currentNodeIndex,\n });\n }\n else {\n // backward at current node\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: currentNodeIndex,\n });\n }\n }\n else {\n if (currentNodeIsForward !== previousNodeIsForward) {\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n }\n else {\n read.path.push({\n order: currentNode.order,\n isForward: !currentNodeIsForward,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n }\n }\n }\n });\n}\n// reverse reads which are reversed\nfunction reverseReversedReads() {\n reads.forEach(read => {\n let pos = 0;\n while (pos < read.sequence.length && read.sequence[pos].startsWith('-')) {\n pos += 1;\n }\n if (pos === read.sequence.length) {\n // completely reversed read\n read.is_reverse = true;\n read.sequence = read.sequence.reverse(); // invert sequence\n for (let i = 0; i < read.sequence.length; i += 1) {\n read.sequence[i] = forward(read.sequence[i]); // visit nodes forward\n // TODO: Do we really want to visit all the nodes forward here? Are we\n // sure we aren't in mixed orientation?\n }\n const sequenceNew = read.sequenceNew ?? [];\n sequenceNew.reverse(); // invert sequence\n read.sequenceNew = sequenceNew;\n for (const entry of sequenceNew) {\n entry.nodeName = forward(entry.nodeName); // visit nodes forward\n reverseMismatches(entry.mismatches, nodeByName(entry.nodeName).sequenceLength);\n }\n // adjust firstNodeOffset and finalNodeCoverLength\n const temp = read.firstNodeOffset ?? 0;\n const firstLen = nodeByName(read.sequence[0]).sequenceLength;\n read.firstNodeOffset = firstLen - (read.finalNodeCoverLength ?? 0);\n const lastLen = nodeByName(read.sequence[read.sequence.length - 1]).sequenceLength;\n read.finalNodeCoverLength = lastLen - temp;\n }\n });\n}\n// Flip a node's mismatches onto the opposite strand: positions are measured\n// from the other end of the node, and sequences are complemented.\n// sequenceLength (not node.width, which is only equal to it in 'normal'\n// node-width mode) is the right pivot because mismatch positions are base\n// offsets. Exported for testing.\nexport function reverseMismatches(mismatches, sequenceLength) {\n mismatches.forEach(mm => {\n if (mm.type === 'insertion') {\n mm.pos = sequenceLength - mm.pos;\n }\n else if (mm.type === 'deletion') {\n mm.pos = sequenceLength - mm.pos - (mm.length ?? 0);\n }\n else if (mm.type === 'substitution') {\n mm.pos = sequenceLength - mm.pos - (mm.seq?.length ?? 0);\n }\n if (mm.seq !== undefined) {\n // NOTE: reverse-complement followed by reverse is a plain complement.\n // Preserved verbatim from the original code rather than \"fixed\" here.\n mm.seq = getReverseComplement(mm.seq).split('').reverse().join('');\n }\n });\n}\nconst COMPLEMENT = { A: 'T', T: 'A', C: 'G', G: 'C' };\nfunction getReverseComplement(s) {\n let result = '';\n for (let i = s.length - 1; i >= 0; i -= 1) {\n result += COMPLEMENT[s.charAt(i)] ?? 'N';\n }\n return result;\n}\n// for each track: generate sequence of node indices from seq. of node names\nfunction generateTrackIndexSequences(tracksOrReads) {\n tracksOrReads.forEach(track => {\n track.indexSequence = [];\n track.sequence.forEach(rawNodeName => {\n let nodeName = rawNodeName;\n // if node was switched, reverse it here\n // Q? Is flipping the index enough? It looks like yes. Or should we also flip the node name in 'sequence'?\n const fwdIdx = nodeMap.get(forward(nodeName));\n const switched = nodes[fwdIdx]?.switched ?? false;\n if (switched) {\n nodeName = flip(nodeName);\n }\n // Get the index to visit the node. If the node is switched, this means\n // visiting it reverse. Otherwise, this means visiting it forward.\n const nodeIndex = nodeMap.get(forward(nodeName));\n if (nodeIndex === 0) {\n // If a node index is ever 0, we can't visit it in reverse, so we don't allow that to happen.\n throw new Error('Node ' + forward(nodeName) + ' has prohibited index 0');\n }\n if (isReverse(nodeName) !== switched) {\n // If we visit the node in reverse XOR the node is switched, go through\n // it right to left as displayed.\n track.indexSequence.push(-nodeIndex);\n }\n else {\n // If either the node isn't switched and we go through it forward, or\n // the node is switched *and* we go through it backward, go through it\n // left to right as displayed.\n track.indexSequence.push(nodeIndex);\n }\n });\n });\n}\n// Tracks enter and leave a node this far outside it horizontally. Note that\n// getImageDimensions() below only adds it on the right: the pannable area\n// understates the left edge of the drawing by this much.\nconst NODE_HORIZONTAL_SLACK = 20;\n// get the minimum and maximum coordinates used in the image to calculate image dimensions\nfunction getImageDimensions() {\n // Sentinels, deliberately crossed: if nothing below runs, minZoom() and\n // alignSVG() detect the empty content rather than computing a negative scale.\n const bounds = { minX: 99, maxX: -99, minY: 99, maxY: -99 };\n nodes.forEach(node => {\n if (!node)\n return;\n if (node.x !== undefined) {\n bounds.minX = Math.min(bounds.minX, node.x);\n bounds.maxX = Math.max(bounds.maxX, node.x + NODE_HORIZONTAL_SLACK + node.pixelWidth);\n }\n if (node.y !== undefined) {\n bounds.minY = Math.min(bounds.minY, node.y - 10);\n bounds.maxY = Math.max(bounds.maxY, node.y + node.contentHeight + 10);\n }\n });\n tracks.forEach(track => {\n track.path.forEach(segment => {\n const y = segment.y ?? 0;\n bounds.maxY = Math.max(bounds.maxY, y + track.width);\n bounds.minY = Math.min(bounds.minY, y);\n });\n });\n imageBounds = bounds;\n}\n// Resolve a (possibly reverse-oriented) node name to its layout node. Every\n// caller runs after generateNodeMap, so a miss is a programming error.\nfunction nodeByName(nodeName) {\n const index = nodeMap.get(forward(nodeName));\n const node = index === undefined ? undefined : nodes[index];\n if (node === undefined) {\n throw new Error(`Unknown node ${nodeName}`);\n }\n return node;\n}\n// map node names to node indices\nfunction generateNodeMap() {\n nodeMap = new Map();\n nodes.forEach((node, index) => {\n if (node) {\n nodeMap.set(node.name, index);\n }\n });\n return nodeMap;\n}\n// adds a successor-array to each node containing the indices of the nodes coming directly after the current node\nfunction generateNodeSuccessors() {\n const successorSets = nodes.map(() => new Set());\n const predecessorSets = nodes.map(() => new Set());\n const addEdges = (track) => {\n for (let i = 0; i < track.indexSequence.length - 1; i += 1) {\n const current = Math.abs(track.indexSequence[i]);\n const follower = Math.abs(track.indexSequence[i + 1]);\n successorSets[current].add(follower);\n predecessorSets[follower].add(current);\n }\n };\n tracks.forEach(addEdges);\n if (config.showReads && reads.length > 0) {\n reads.forEach(addEdges);\n }\n nodes.forEach((node, i) => {\n if (node) {\n node.successors = Array.from(successorSets[i]);\n node.predecessors = Array.from(predecessorSets[i]);\n }\n });\n}\nfunction generateNodeOrderOfSingleTrack(sequence) {\n let forwardOrder = 0;\n let backwardOrder = 0;\n let minOrder = 0;\n sequence.forEach(nodeIndex => {\n const idx = Math.abs(nodeIndex);\n if (nodeIndex < 0) {\n if (nodeOrders[idx] === undefined)\n nodeOrders[idx] = backwardOrder;\n const order = nodeOrders[idx];\n if (order < minOrder)\n minOrder = order;\n forwardOrder = order;\n backwardOrder = order - 1;\n }\n else {\n if (nodeOrders[idx] === undefined)\n nodeOrders[idx] = forwardOrder;\n const order = nodeOrders[idx];\n forwardOrder = order + 1;\n backwardOrder = order;\n }\n });\n if (minOrder < 0) {\n increaseOrderForAllNodes(-minOrder);\n }\n}\n// calculate the order-value of nodes contained in sequence which are to the left of the first node which already has an order-value\nfunction generateNodeOrderTrackBeginning(sequence) {\n let anchorIndex = 0;\n let minOrder = 0;\n while (anchorIndex < sequence.length &&\n nodeOrders[Math.abs(sequence[anchorIndex])] === undefined) {\n anchorIndex += 1; // anchor = first node in common with existing graph\n }\n if (anchorIndex >= sequence.length) {\n return null;\n }\n const anchorSeqVal = sequence[anchorIndex];\n let currentOrder;\n let increment;\n if (anchorSeqVal >= 0) {\n // regular node\n currentOrder = nodeOrders[anchorSeqVal] - 1;\n increment = -1;\n }\n else {\n // reverse node\n currentOrder = nodeOrders[-anchorSeqVal] + 1;\n increment = 1;\n }\n for (let j = anchorIndex - 1; j >= 0; j -= 1) {\n // assign order to nodes which are left of anchor node\n const idx = Math.abs(sequence[j]);\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder += increment;\n }\n }\n if (minOrder < 0) {\n increaseOrderForAllNodes(-minOrder);\n }\n return anchorIndex;\n}\n// Sentinel order for nodes no track or read reaches.\nexport const UNREACHABLE_ORDER = -1;\n// Replace every unassigned entry with UNREACHABLE_ORDER, producing a dense\n// array. Holes matter here: `new Array(n)` is all holes and forEach/map skip\n// them, so an in-place forEach would leave the sentinel unset.\nexport function fillUnassignedOrders(orders) {\n const filled = [];\n for (const order of orders) {\n filled.push(order ?? UNREACHABLE_ORDER);\n }\n return filled;\n}\n// generate global sequence of nodes from left to right, starting with first track and adding other tracks sequentially\nfunction generateNodeOrder() {\n let modifiedSequence;\n let currentOrder;\n let rightIndex;\n let leftIndex;\n let minOrder = 0;\n const tracksAndReads = config.showReads && reads.length > 0 ? tracks.concat(reads) : tracks;\n const reachability = {\n stamp: new Int32Array(nodes.length),\n generation: 0,\n };\n // fill() makes the array dense: `new Array(n)` alone is all holes, which\n // forEach skips, so neither the sentinel pass nor the copy-back below ran.\n nodeOrders = new Array(nodes.length).fill(undefined);\n nodes.forEach(node => {\n if (node) {\n node.order = undefined;\n }\n });\n generateNodeOrderOfSingleTrack(tracks[0].indexSequence);\n for (let i = 1; i < tracksAndReads.length; i += 1) {\n if (DEBUG)\n console.log(`generating order for track ${i + 1}`);\n rightIndex = generateNodeOrderTrackBeginning(tracksAndReads[i].indexSequence);\n if (rightIndex === null) {\n if (tracksAndReads[i].type === 'haplotype') {\n generateNodeOrderOfSingleTrack(tracksAndReads[i].indexSequence);\n }\n else {\n tracksAndReads.splice(i, 1);\n reads.splice(i - tracks.length, 1);\n i -= 1;\n }\n continue;\n }\n modifiedSequence = uninvert(tracksAndReads[i].indexSequence);\n while (rightIndex < modifiedSequence.length) {\n // move right until the end of the sequence\n leftIndex = rightIndex;\n rightIndex += 1;\n while (rightIndex < modifiedSequence.length &&\n nodeOrders[modifiedSequence[rightIndex]] === undefined) {\n rightIndex += 1;\n }\n if (rightIndex < modifiedSequence.length) {\n // middle segment between two anchors\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] + 1;\n for (let j = leftIndex + 1; j < rightIndex; j += 1) {\n nodeOrders[modifiedSequence[j]] = currentOrder;\n currentOrder += 1;\n }\n if (nodeOrders[modifiedSequence[rightIndex]] >\n nodeOrders[modifiedSequence[leftIndex]]) {\n if (nodeOrders[modifiedSequence[rightIndex]] < currentOrder) {\n increaseOrderForSuccessors(modifiedSequence[rightIndex], modifiedSequence[rightIndex - 1], currentOrder);\n }\n }\n else {\n if (tracksAndReads[i].indexSequence[rightIndex] >= 0 &&\n !isSuccessor(modifiedSequence[rightIndex], modifiedSequence[leftIndex], reachability)) {\n // no real reversal\n increaseOrderForSuccessors(modifiedSequence[rightIndex], modifiedSequence[rightIndex - 1], currentOrder);\n }\n else {\n // real reversal\n if (tracksAndReads[i].indexSequence[leftIndex] < 0 ||\n (nodes[modifiedSequence[leftIndex + 1]].degree < 2 &&\n nodeOrders[modifiedSequence[rightIndex]] <\n nodeOrders[modifiedSequence[leftIndex]])) {\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] - 1;\n for (let j = leftIndex + 1; j < rightIndex; j += 1) {\n nodeOrders[modifiedSequence[j]] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder -= 1;\n }\n }\n }\n }\n }\n else {\n // right segment to the right of last anchor\n if (tracksAndReads[i].indexSequence[leftIndex] >= 0) {\n // elongate towards the right\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] + 1;\n for (let j = leftIndex + 1; j < modifiedSequence.length; j += 1) {\n const idx = modifiedSequence[j];\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n currentOrder += 1;\n }\n }\n }\n else {\n // elongate towards the left\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] - 1;\n for (let j = leftIndex + 1; j < modifiedSequence.length; j += 1) {\n const idx = modifiedSequence[j];\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder -= 1;\n }\n }\n }\n }\n }\n }\n if (minOrder < 0)\n increaseOrderForAllNodes(-minOrder);\n // Nodes unreachable from any track get UNREACHABLE_ORDER so every node ends\n // up with a defined order; downstream code uses `order >= 0` to skip them.\n const finalOrders = fillUnassignedOrders(nodeOrders);\n nodeOrders = finalOrders;\n finalOrders.forEach((order, i) => {\n const node = nodes[i];\n if (node !== undefined) {\n node.order = order;\n }\n });\n}\nfunction isSuccessor(first, second, scratch) {\n scratch.generation += 1;\n const { stamp, generation } = scratch;\n const stack = [first];\n stamp[first] = generation;\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === second)\n return true;\n for (const childIndex of nodes[current].successors) {\n if (stamp[childIndex] !== generation) {\n stamp[childIndex] = generation;\n stack.push(childIndex);\n }\n }\n }\n return false;\n}\n// get order number of the rightmost node\nfunction getMaxOrder() {\n let max = -1;\n nodeOrders.forEach(order => {\n if (order !== undefined && order > max)\n max = order;\n });\n return max;\n}\n// generates sequence keeping the order but switching all reversed (negative) nodes to forward nodes\nfunction uninvert(sequence) {\n return sequence.map(v => Math.abs(v));\n}\n// increases the order-value of all nodes by amount\nfunction increaseOrderForAllNodes(amount) {\n nodeOrders.forEach((order, i) => {\n if (order !== undefined)\n nodeOrders[i] = order + amount;\n });\n}\n// increases the order-value for currentNode and (if necessary) successor nodes recursively\nfunction increaseOrderForSuccessors(startingNode, tabuNode, newOrder) {\n const increasedOrders = new Map();\n // Walked with a head pointer rather than shift(), which is O(length) per pop.\n const queue = [[startingNode, newOrder]];\n let head = 0;\n while (head < queue.length) {\n const [currentNode, currentOrder] = queue[head];\n head += 1;\n const currentNodeOrder = nodeOrders[currentNode];\n if (currentNodeOrder !== undefined && currentNodeOrder < currentOrder) {\n if (!increasedOrders.has(currentNode) ||\n increasedOrders.get(currentNode) < currentOrder) {\n increasedOrders.set(currentNode, currentOrder);\n nodes[currentNode].successors.forEach(successor => {\n if (nodeOrders[successor] > currentNodeOrder &&\n successor !== tabuNode) {\n // only increase order of successors to the right of currentNode\n queue.push([successor, currentOrder + 1]);\n }\n });\n if (currentNode !== startingNode) {\n nodes[currentNode].predecessors.forEach(predecessor => {\n if (nodeOrders[predecessor] > currentNodeOrder &&\n predecessor !== tabuNode) {\n // only increase order of predecessors to the right of currentNode\n queue.push([predecessor, currentOrder + 1]);\n }\n });\n }\n }\n }\n }\n increasedOrders.forEach((value, key) => {\n nodeOrders[key] = value;\n });\n}\n// calculates the node degree: the number of tracks passing through the node / the node height\nfunction generateNodeDegree() {\n nodes.forEach(node => {\n node.tracks = [];\n });\n tracks.forEach(track => {\n track.indexSequence.forEach(nodeIndex => {\n nodes[Math.abs(nodeIndex)].tracks.push(track.id);\n });\n });\n nodes.forEach(node => {\n if (node.tracks !== undefined)\n node.degree = node.tracks.length;\n });\n}\n// Optimize the orientations for nodes in the global `nodes` for displaying the\n// paths in the global `tracks` and the read paths, if applicable, in the\n// global `reads`\nfunction switchNodeOrientation() {\n const pivotPath = tracks[0];\n let countPaths = tracks.slice(1, tracks.length);\n if (config.showReads && reads.length > 0) {\n countPaths = countPaths.concat(reads);\n }\n switchNodeOrientationForPaths(countPaths, pivotPath);\n if (config.showReads && reads.length > 0) {\n // Any changes should be committed back\n for (let i = 0; i < reads.length; i++) {\n if (reads[i] !== countPaths[i + tracks.length - 1]) {\n throw new Error('Read inequality');\n }\n }\n }\n}\n// If more of the given paths pass through a specific node in reverse direction than in\n// regular direction, switch its orientation. Processes all paths' nodes in\n// place, so if you want e.g. a first track with nodes fixed in that\n// orientation, pass it as pivotPath.\n// References and modifies the global nodes variable.\nfunction switchNodeOrientationForPaths(paths, pivotPath) {\n const toSwitch = new Map();\n const pivotNames = pivotPath ? new Set(pivotPath.sequence) : null;\n for (const path of paths) {\n const sequence = path.sequence;\n for (let j = 0; j < sequence.length; j += 1) {\n const nodeName = forward(sequence[j]);\n const currentNode = nodeByName(nodeName);\n if (pivotNames && !pivotNames.has(nodeName)) {\n // do not change orientation for nodes which are part of the pivot path\n const prevOrder = j > 0 ? nodeByName(sequence[j - 1]).order : undefined;\n const nextOrder = j < sequence.length - 1\n ? nodeByName(sequence[j + 1]).order\n : undefined;\n if ((prevOrder === undefined || prevOrder < currentNode.order) &&\n (nextOrder === undefined || currentNode.order < nextOrder)) {\n // Node is visited in increasing order along the path. Reverse\n // visits count towards switching, forward visits against it.\n addToSwitchScore(toSwitch, nodeName, isReverse(sequence[j]) ? 1 : -1);\n }\n if ((prevOrder === undefined || prevOrder > currentNode.order) &&\n (nextOrder === undefined || currentNode.order > nextOrder)) {\n // Node is visited in *decreasing* order along the path, so is already\n // backward: the votes are the other way round.\n addToSwitchScore(toSwitch, nodeName, isReverse(sequence[j]) ? -1 : 1);\n }\n }\n }\n }\n for (const path of paths) {\n path.sequence.forEach((node, nodeIndex) => {\n const score = toSwitch.get(forward(node));\n if (score !== undefined && score > 0) {\n // This node is backward more so flip it around\n path.sequence[nodeIndex] = flip(node);\n path.indexSequence[nodeIndex] = -path.indexSequence[nodeIndex];\n }\n });\n }\n // invert the sequence within the nodes and mark them as \"switched\"\n toSwitch.forEach((value, key) => {\n if (value > 0) {\n const node = nodeByName(key);\n node.seq = getReverseComplement(node.seq);\n node.switched = true;\n }\n });\n}\nfunction addToSwitchScore(scores, nodeName, delta) {\n scores.set(nodeName, (scores.get(nodeName) ?? 0) + delta);\n}\n// calculates the concrete values for the nodes' x-coordinates\nfunction generateNodeXCoords() {\n let currentX = 0;\n let nextX = 20;\n let currentOrder = -1;\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n const extra = calculateExtraSpace();\n sortedNodes.forEach(node => {\n if (node.order >= 0) {\n if (node.order > currentOrder) {\n currentOrder = node.order;\n currentX = nextX + 10 * extra[node.order];\n }\n node.x = currentX;\n nextX = Math.max(nextX, currentX + 40 + node.pixelWidth);\n }\n });\n}\n// calculates additional horizontal space needed between two nodes\n// two neighboring nodes have to be moved further apart if there is a lot going on in between them\n// -> edges turning to vertical orientation should not overlap\nfunction calculateExtraSpace() {\n const leftSideEdges = [];\n const rightSideEdges = [];\n const fallAngleAdjustment = [];\n const extra = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n leftSideEdges.push(0);\n rightSideEdges.push(0);\n fallAngleAdjustment.push(0);\n }\n tracks.forEach(track => {\n for (let i = 1; i < track.path.length; i += 1) {\n const seg = track.path[i];\n const prevSeg = track.path[i - 1];\n // Track is going to the same node, account for space taken up by edges looping around\n if (seg.order === prevSeg.order) {\n // repeat or translocation\n if (seg.isForward) {\n leftSideEdges[seg.order] = leftSideEdges[seg.order] + 1;\n }\n else {\n rightSideEdges[seg.order] = rightSideEdges[seg.order] + 1;\n }\n }\n else {\n // Track is going to a different node; account for space needed to limit rise/fall angle\n const yDifference = Math.abs((seg.y ?? 0) - (prevSeg.y ?? 0));\n //TODO: Extra space should also be accounted when there are too many tracks curving at the nodes\n fallAngleAdjustment[seg.order] = Math.max(yDifference / 17.5, fallAngleAdjustment[seg.order]);\n }\n }\n });\n extra.push(Math.max(0, leftSideEdges[0] - 1));\n for (let i = 1; i <= maxOrder; i += 1) {\n // Extra space uses space needed for edges(tracks looping), or space needed to limit rise/fall angle, whichever is larger\n extra.push(Math.max(Math.max(0, leftSideEdges[i] - 1) +\n Math.max(0, rightSideEdges[i - 1] - 1), fallAngleAdjustment[i]));\n }\n return extra;\n}\n// create and fill assignment-variable, which contains info about tracks and lanes for each order-value\nfunction generateLaneAssignment() {\n let segmentNumber;\n let currentNodeIndex;\n let currentNodeIsForward;\n let currentNode;\n let previousNode;\n let previousNodeIsForward;\n // For each horizontal order slot, for each track number, holds the\n // SegmentAssignment object for the visit of that track to that order slot.\n // When an order slot is visited multiple times, holds whatever\n // SegmentAssignment was created most recently.\n const prevSegmentPerOrderPerTrack = [];\n // Index into assignments[order] by node, so addToAssignment can find an\n // existing entry without scanning the whole order slot.\n const assignmentByOrderAndNode = [];\n // create empty variables\n for (let i = 0; i <= maxOrder; i += 1) {\n assignments[i] = [];\n assignmentByOrderAndNode[i] = new Map();\n prevSegmentPerOrderPerTrack[i] = [];\n for (let j = 0; j < tracks.length; j += 1) {\n prevSegmentPerOrderPerTrack[i][j] = null;\n }\n }\n tracks.forEach((track, trackNo) => {\n // Trace along each track and create Segment objects in the track's path\n // field, and SegmentAssignment objects in NodeAssignment objects in all\n // the order slots that are visited by the track. Set up all the\n // cross-reverencing indexes and work out when we need segments to let\n // tracks pass nodes and turn around, but leave all the assigned lane\n // values empty for now.\n // add info for start of track\n currentNodeIndex = Math.abs(track.indexSequence[0]);\n currentNodeIsForward = isForwardIndex(track.indexSequence[0]);\n currentNode = nodes[currentNodeIndex];\n track.path = [];\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, 0, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber = 1;\n for (let i = 1; i < track.sequence.length; i += 1) {\n previousNode = currentNode;\n previousNodeIsForward = currentNodeIsForward;\n currentNodeIndex = Math.abs(track.indexSequence[i]);\n currentNodeIsForward = isForwardIndex(track.indexSequence[i]);\n currentNode = nodes[currentNodeIndex];\n if (currentNode.order > previousNode.order) {\n if (!previousNodeIsForward) {\n // backward to forward at previous node\n track.path.push({\n order: previousNode.order,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(previousNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n for (let j = previousNode.order + 1; j < currentNode.order; j += 1) {\n // forward without nodes\n track.path.push({\n order: j,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(j, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n if (!currentNodeIsForward) {\n // forward to backward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n // current Node forward\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n else if (currentNode.order < previousNode.order) {\n if (previousNodeIsForward) {\n // turnaround from fw to bw at previous node\n track.path.push({\n order: previousNode.order,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(previousNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n for (let j = previousNode.order - 1; j > currentNode.order; j -= 1) {\n // bachward without nodes\n track.path.push({\n order: j,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(j, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n if (currentNodeIsForward) {\n // backward to forward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n // backward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n else {\n if (currentNodeIsForward !== previousNodeIsForward) {\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: !currentNodeIsForward,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n }\n });\n // Now sweep left to right across order slots and assign vertical lanes to all the segments.\n for (let i = 0; i <= maxOrder; i += 1) {\n generateSingleLaneAssignment(assignments[i], i); // this is where the lanes get assigned\n }\n}\nfunction addToAssignment(order, nodeIndex, trackNo, segmentID, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode) {\n const segment = {\n trackID: trackNo,\n segmentID,\n compareToFromSame: prevSegmentPerOrderPerTrack[order][trackNo] ?? null,\n };\n // A null node means the track is passing through / turning around here rather\n // than visiting a node, so such segments never share a NodeAssignment.\n const existing = nodeIndex === null\n ? undefined\n : assignmentByOrderAndNode[order].get(nodeIndex);\n if (existing === undefined) {\n const assignment = {\n type: 'single',\n node: nodeIndex,\n tracks: [segment],\n };\n assignments[order].push(assignment);\n if (nodeIndex !== null) {\n assignmentByOrderAndNode[order].set(nodeIndex, assignment);\n }\n }\n else {\n // add to existing node in assignment\n existing.type = 'multiple';\n existing.tracks.push(segment);\n }\n prevSegmentPerOrderPerTrack[order][trackNo] = segment;\n}\n// looks at assignment and sets idealY and idealLane by looking at where the tracks come from\nfunction getIdealLanesAndCoords(assignment, order) {\n let index;\n assignment.forEach(node => {\n node.idealLane = 0;\n node.tracks.forEach(track => {\n if (track.segmentID === 0) {\n track.idealLane = track.trackID;\n track.idealY = null;\n }\n else {\n if (tracks[track.trackID].path[track.segmentID - 1].order ===\n order - 1) {\n track.idealLane =\n tracks[track.trackID].path[track.segmentID - 1].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[track.segmentID - 1].y;\n }\n else if (track.segmentID < tracks[track.trackID].path.length - 1 &&\n tracks[track.trackID].path[track.segmentID + 1].order === order - 1) {\n track.idealLane =\n tracks[track.trackID].path[track.segmentID + 1].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[track.segmentID + 1].y;\n }\n else {\n index = track.segmentID - 1;\n while (index >= 0 &&\n tracks[track.trackID].path[index].order !== order - 1) {\n index -= 1;\n }\n if (index < 0) {\n track.idealLane = track.trackID;\n track.idealY = null;\n }\n else {\n track.idealLane =\n tracks[track.trackID].path[index].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[index].y;\n }\n }\n }\n node.idealLane += track.idealLane;\n });\n node.idealLane /= node.tracks.length;\n });\n}\n// assigns the optimal lanes for a single horizontal position (=order)\n// first an ideal lane is calculated for each track (which is ~ the lane of its predecessor)\n// then the nodes are sorted by their average ideal lane\n// and the whole construct is then moved up or down if necessary\nfunction generateSingleLaneAssignment(assignment, order) {\n let currentLane = 0;\n const potentialAdjustmentValues = new Set();\n let currentY = 20;\n let prevNameIsNull = false;\n let prevTrack = -1;\n getIdealLanesAndCoords(assignment, order);\n assignment.sort(compareByIdealLane);\n assignment.forEach(node => {\n if (node.node !== null) {\n nodes[node.node].topLane = currentLane;\n if (prevNameIsNull)\n currentY -= 10;\n nodes[node.node].y = currentY;\n nodes[node.node].contentHeight = 0;\n prevNameIsNull = false;\n }\n else {\n if (prevNameIsNull)\n currentY -= 25;\n else if (currentY > 20)\n currentY -= 10;\n prevNameIsNull = true;\n }\n node.tracks.sort(compareByIdealLane);\n node.tracks.forEach(track => {\n track.lane = currentLane;\n if (track.trackID === prevTrack && node.node === null && prevNameIsNull) {\n currentY += 10;\n }\n tracks[track.trackID].path[track.segmentID].lane = currentLane;\n tracks[track.trackID].path[track.segmentID].y = currentY;\n if (track.idealY != null) {\n potentialAdjustmentValues.add(track.idealY - currentY);\n }\n currentLane += 1;\n currentY += tracks[track.trackID].width;\n if (node.node !== null) {\n nodes[node.node].contentHeight += tracks[track.trackID].width;\n }\n prevTrack = track.trackID;\n });\n currentY += 25;\n });\n adjustVertically(assignment, potentialAdjustmentValues);\n}\n// moves all tracks at a single horizontal location (=order) up/down to minimize lane changes\nfunction adjustVertically(assignment, potentialAdjustmentValues) {\n let verticalAdjustment = 0;\n let minAdjustmentCost = Number.MAX_SAFE_INTEGER;\n potentialAdjustmentValues.forEach(moveBy => {\n if (getVerticalAdjustmentCost(assignment, moveBy) < minAdjustmentCost) {\n minAdjustmentCost = getVerticalAdjustmentCost(assignment, moveBy);\n verticalAdjustment = moveBy;\n }\n });\n assignment.forEach(node => {\n if (node.node !== null) {\n nodes[node.node].y += verticalAdjustment;\n }\n node.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n seg.y = seg.y + verticalAdjustment;\n });\n });\n}\n// Budge down all nodes and out-of-node tracks below this node by this amount\nfunction adjustVertically3(node, adjustBy) {\n if (node.order < 0 || assignments[node.order] === undefined)\n return;\n assignments[node.order].forEach(assignmentNode => {\n if (assignmentNode.node !== null) {\n const aNode = nodes[assignmentNode.node];\n if (aNode !== node && aNode.y > node.y) {\n aNode.y += adjustBy;\n assignmentNode.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n seg.y = seg.y + adjustBy;\n });\n }\n }\n else {\n // track-segment not within a node\n assignmentNode.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n if (seg.y >= node.y) {\n seg.y = seg.y + adjustBy;\n }\n });\n }\n });\n if (nodesPerOrder[node.order].length > 0) {\n nodesPerOrder[node.order].forEach(nodeIndex => {\n if (nodes[nodeIndex] !== node && nodes[nodeIndex].y > node.y) {\n nodes[nodeIndex].y += adjustBy;\n }\n });\n }\n}\n// calculates cost of vertical adjustment as vertical distance * width of track\nfunction getVerticalAdjustmentCost(assignment, moveBy) {\n let result = 0;\n assignment.forEach(node => {\n node.tracks.forEach(track => {\n if (track.idealY != null && tracks[track.trackID].type !== 'read') {\n result +=\n Math.abs(track.idealY -\n moveBy -\n tracks[track.trackID].path[track.segmentID].y) * tracks[track.trackID].width;\n }\n });\n });\n return result;\n}\nfunction compareByIdealLane(a, b) {\n if (a.idealLane !== undefined) {\n if (b.idealLane !== undefined) {\n if (a.idealLane < b.idealLane)\n return -1;\n else if (a.idealLane > b.idealLane)\n return 1;\n return 0;\n }\n return -1;\n }\n if (b.idealLane !== undefined) {\n return 1;\n }\n return 0;\n}\nfunction compareNodesByOrder(a, b) {\n if (!a) {\n if (!b)\n return 0;\n return -1;\n }\n if (!b)\n return 1;\n if (a.order !== undefined) {\n if (b.order !== undefined) {\n if (a.order < b.order)\n return -1;\n else if (a.order > b.order)\n return 1;\n if (a.y !== undefined && b.y !== undefined) {\n if (a.y < b.y)\n return -1;\n else if (a.y > b.y)\n return 1;\n }\n return 0;\n }\n return -1;\n }\n if (b.order !== undefined)\n return 1;\n return 0;\n}\nfunction addTrackFeatures() {\n let nodeStart;\n let nodeEnd;\n let feature = {};\n bed.forEach(line => {\n let i = 0;\n while (i < tracks.length && tracks[i].name !== line.track)\n i += 1;\n if (i < tracks.length) {\n nodeStart = 0;\n tracks[i].path.forEach(node => {\n if (node.node !== null) {\n feature = {};\n nodeEnd = nodeStart + nodes[node.node].sequenceLength - 1;\n if (nodeStart >= line.start && nodeStart <= line.end) {\n feature.start = 0;\n }\n if (nodeStart < line.start && nodeEnd >= line.start) {\n feature.start = line.start - nodeStart;\n }\n if (nodeEnd <= line.end && nodeEnd >= line.start) {\n feature.end = nodeEnd - nodeStart;\n if (nodeEnd < line.end)\n feature.continue = true;\n }\n if (nodeEnd > line.end && nodeStart <= line.end) {\n feature.end = line.end - nodeStart;\n }\n if (feature.start !== undefined) {\n feature.type = line.type;\n feature.name = line.name;\n if (node.features === undefined)\n node.features = [];\n node.features.push(feature);\n }\n nodeStart = nodeEnd + 1;\n }\n });\n }\n });\n}\nfunction calculateTrackWidth() {\n // flag: if vg returns freq of 0 for all tracks, we will increase width manually\n let allAreFour = true;\n const NARROW_WIDTH = 4;\n const WIDE_WIDTH = config.trackWidth;\n tracks.forEach(track => {\n if (track.freq !== undefined) {\n // custom track width\n track.width = Math.round((Math.log(track.freq) + 1) * NARROW_WIDTH);\n }\n else {\n // default track width\n track.width = WIDE_WIDTH;\n if (track.type !== undefined && track.type === 'read') {\n track.width = NARROW_WIDTH;\n }\n }\n if (track.width !== NARROW_WIDTH) {\n allAreFour = false;\n }\n });\n if (allAreFour) {\n tracks.forEach(track => {\n if (track.freq !== undefined) {\n track.width = WIDE_WIDTH;\n }\n });\n }\n}\nfunction getReadXStart(read) {\n const seg = read.path[0];\n const node = nodes[seg.node];\n const offset = read.firstNodeOffset ?? 0;\n // read starts in forward direction, or backward from the node's far end\n return clampedXCoordinateOfBaseWithinNode(node, seg.isForward ? offset : node.sequenceLength - offset);\n}\nfunction getReadXEnd(read) {\n const seg = read.path[read.path.length - 1];\n const node = nodes[seg.node];\n const cover = read.finalNodeCoverLength ?? 0;\n // read ends in forward direction, or backward from the node's far end\n return clampedXCoordinateOfBaseWithinNode(node, seg.isForward ? cover : node.sequenceLength - cover);\n}\n// returns the x coordinate (in pixels) of (the left side) of the given base\n// position within the given node, or null if the position is past its end\nexport function getXCoordinateOfBaseWithinNode(node, base) {\n if (base > node.sequenceLength)\n return null; // equality is allowed\n return clampedXCoordinateOfBaseWithinNode(node, base);\n}\n// Same, but a base outside the node maps to the nearest node edge. Callers that\n// have to produce a coordinate no matter what use this: falling back to 0 would\n// put the shape at the far left of the whole image instead.\nexport function clampedXCoordinateOfBaseWithinNode(node, base) {\n const [nodeLeftX, nodeRightX] = nodePixelCoordinatesInX(node);\n if (node.sequenceLength === 0) {\n return nodeLeftX;\n }\n const clamped = Math.min(Math.max(base, 0), node.sequenceLength);\n return nodeLeftX + (clamped / node.sequenceLength) * (nodeRightX - nodeLeftX);\n}\n// transforms the info in the tracks' path attribute into actual coordinates\n// and saves them in shapes.rectangles and shapes.curves\nfunction generateSVGShapesFromPath() {\n let xStart;\n let xEnd;\n let yStart;\n let yEnd;\n let trackColor;\n let trackAlpha;\n let highlight;\n let dummy;\n let reversalFlag;\n for (let i = 0; i <= maxOrder; i += 1) {\n extraLeft.push(0);\n extraRight.push(0);\n }\n // generate x coords where each order starts and ends\n const orderStartX = [];\n const orderEndX = [];\n nodes.forEach(node => {\n if (node.x !== undefined) {\n orderStartX[node.order] = node.x;\n if (orderEndX[node.order] === undefined) {\n orderEndX[node.order] = node.x + node.pixelWidth;\n }\n else {\n orderEndX[node.order] = Math.max(orderEndX[node.order], node.x + node.pixelWidth);\n }\n }\n });\n // Helps generation of verticalRectangles, correct increments of extraRight and extraLeft\n tracks.sort(compareTrackByInitialOrdering);\n tracks.forEach(track => {\n highlight = 'plain';\n // Both are pure functions of (track, highlight); alpha doesn't even look at\n // highlight. Recomputing them per path segment was pure overhead.\n trackColor = generateTrackColor(track, highlight);\n trackAlpha = generateTrackAlpha(track);\n let colorHighlight = highlight;\n const colorForCurrentHighlight = () => {\n if (colorHighlight !== highlight) {\n colorHighlight = highlight;\n trackColor = generateTrackColor(track, highlight);\n }\n return trackColor;\n };\n // start of path\n yStart = track.path[0].y;\n if (track.type !== 'read') {\n if (track.sequence[0].startsWith('-')) {\n // The track starts with an inversed node\n xStart = orderEndX[track.path[0].order] + 20;\n }\n else {\n // The track starts with a forward node\n xStart = orderStartX[track.path[0].order] - 20;\n }\n }\n else {\n xStart = getReadXStart(track);\n }\n // middle of path\n for (let i = 0; i < track.path.length; i += 1) {\n if (track.path[i].y === yStart) {\n if (track.path[i].features !== undefined) {\n reversalFlag =\n i > 0 && track.path[i - 1].order === track.path[i].order;\n dummy = createFeatureRectangle(track.path[i], orderStartX[track.path[i].order], orderEndX[track.path[i].order], highlight, track, xStart, yStart, trackColor, reversalFlag);\n highlight = dummy.highlight;\n xStart = dummy.xStart;\n }\n }\n else {\n if (track.path[i - 1].isForward) {\n xEnd = orderEndX[track.path[i - 1].order];\n }\n else {\n xEnd = orderStartX[track.path[i - 1].order];\n }\n if (xEnd !== xStart) {\n trackColor = colorForCurrentHighlight();\n shapes.rectangles.push({\n xStart: Math.min(xStart, xEnd),\n yStart,\n xEnd: Math.max(xStart, xEnd),\n yEnd: yStart + track.width - 1,\n color: trackColor,\n alpha: trackAlpha,\n // TODO: This is not actually the index of the track!\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (track.path[i].order - 1 === track.path[i - 1].order) {\n // regular forward connection\n xStart = xEnd;\n xEnd = orderStartX[track.path[i].order];\n yEnd = track.path[i].y;\n trackColor = colorForCurrentHighlight();\n shapes.curves.push({\n xStart,\n yStart,\n xEnd: xEnd + 1,\n yEnd,\n width: track.width,\n color: trackColor,\n alpha: trackAlpha,\n laneChange: Math.abs(track.path[i].lane - track.path[i - 1].lane),\n id: track.id,\n name: track.name,\n type: track.type,\n nodeStart: track.path[i - 1]?.node,\n nodeEnd: track.path[i]?.node,\n });\n xStart = xEnd;\n yStart = yEnd;\n }\n else if (track.path[i].order + 1 === track.path[i - 1].order) {\n // regular backward connection\n xStart = xEnd;\n xEnd = orderEndX[track.path[i].order];\n yEnd = track.path[i].y;\n trackColor = colorForCurrentHighlight();\n shapes.curves.push({\n xStart: xStart + 1,\n yStart,\n xEnd,\n yEnd,\n width: track.width,\n color: trackColor,\n alpha: trackAlpha,\n laneChange: Math.abs(track.path[i].lane - track.path[i - 1].lane),\n id: track.id,\n name: track.name,\n type: track.type,\n nodeStart: track.path[i - 1]?.node,\n nodeEnd: track.path[i]?.node,\n });\n xStart = xEnd;\n yStart = yEnd;\n }\n else {\n // change of direction\n if (track.path[i - 1].isForward) {\n yEnd = track.path[i].y;\n generateTurnaround(1, xEnd, yStart, yEnd, track.width, trackColor, track.id, track.path[i].order, track.type, track.name);\n xStart = orderEndX[track.path[i].order];\n yStart = track.path[i].y;\n }\n else {\n yEnd = track.path[i].y;\n generateTurnaround(-1, xEnd, yStart, yEnd, track.width, trackColor, track.id, track.path[i].order, track.type, track.name);\n xStart = orderStartX[track.path[i].order];\n yStart = track.path[i].y;\n }\n }\n if (track.path[i].features !== undefined) {\n reversalFlag = track.path[i - 1].order === track.path[i].order;\n dummy = createFeatureRectangle(track.path[i], orderStartX[track.path[i].order], orderEndX[track.path[i].order], highlight, track, xStart, yStart, trackColor, reversalFlag);\n highlight = dummy.highlight;\n xStart = dummy.xStart;\n }\n }\n }\n // ending edges\n if (track.type !== 'read') {\n if (!track.path[track.path.length - 1].isForward) {\n // The track ends with an inversed node\n xEnd = orderStartX[track.path[track.path.length - 1].order] - 20;\n }\n else {\n // The track ends with a forward node\n xEnd = orderEndX[track.path[track.path.length - 1].order] + 20;\n }\n }\n else {\n xEnd = getReadXEnd(track);\n }\n shapes.rectangles.push({\n xStart: Math.min(xStart, xEnd),\n yStart,\n xEnd: Math.max(xStart, xEnd),\n yEnd: yStart + track.width - 1,\n color: trackColor,\n alpha: trackAlpha,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n });\n}\n// Sankey-mode synthesis: one synthetic \"read\" per (srcSigned \u2192 dstSigned) edge,\n// fed through the normal placeReads/generateSVGShapesFromPath pipeline so the\n// band gets the same lane assignment, loop topology, bezier shape, AND color\n// scheme as a real read \u2014 colors come from generateTrackColor's natural\n// `id % palette.length` rotation, no custom palette needed.\n//\n// Synthetic-track ids live above this base so they don't collide with real\n// track ids and so click/hover handlers can show count info instead of the\n// per-read info dialog.\nconst COARSENED_ID_BASE = 1_000_000_000;\nexport const isCoarsenedId = (id) => id >= COARSENED_ID_BASE;\n// Build one synthetic read per (srcSigned \u2192 dstSigned) edge in `source`\n// (real reads, or the alt haplotype tracks). The synthetic reads replace the\n// real ones BEFORE placeReads runs, so:\n// \u2022 placeReads sees N_edges reads instead of N_reads (huge node-height win),\n// \u2022 each band gets a lane and an \"right-down-left-up-right\" loop topology\n// for free (placeReads handles reversals and out-of-order jumps),\n// \u2022 generateSVGShapesFromPath emits exactly one TrackCurve per band using\n// the same bezier as a real read \u2014 no special draw path needed.\n//\n// The synthetic track's `sequence` is just the two signed node names. After\n// generateTrackIndexSequences runs, indexSequence is set and the rest of the\n// layout follows.\nfunction buildCoarsenedSyntheticBands(source, unit) {\n coarsenedEdgeMeta = new Map();\n const edges = new Map();\n // When ignoring strand, (+A\u2192+B) and (-B\u2192-A) refer to the same underlying\n // graph connection \u2014 collapse both into one canonical key. We pick the\n // lexicographically smaller of the two orientations so a stable canonical\n // form is chosen; whichever orientation is seen first determines the\n // visual band direction.\n const ignoreStrand = config.ignoreStrand;\n for (const item of source) {\n const seq = item.indexSequence;\n if (!seq || seq.length < 2)\n continue;\n for (let i = 0; i < seq.length - 1; i += 1) {\n const sSigned = seq[i];\n const dSigned = seq[i + 1];\n const sIdx = Math.abs(sSigned);\n const dIdx = Math.abs(dSigned);\n const srcNode = nodes[sIdx];\n const dstNode = nodes[dIdx];\n if (!srcNode || !dstNode)\n continue;\n let key;\n if (ignoreStrand) {\n const a = `${sSigned}>${dSigned}`;\n const b = `${-dSigned}>${-sSigned}`;\n key = a < b ? a : b;\n }\n else {\n key = `${sSigned}>${dSigned}`;\n }\n const existing = edges.get(key);\n if (existing === undefined) {\n edges.set(key, {\n sSigned,\n dSigned,\n sName: sSigned < 0 ? `-${srcNode.name}` : srcNode.name,\n dName: dSigned < 0 ? `-${dstNode.name}` : dstNode.name,\n count: 1,\n sourceTrackID: item.sourceTrackID,\n });\n }\n else {\n existing.count += 1;\n }\n }\n }\n // Square-root scaling on count gives heavy edges visible weight without\n // letting one massive edge dwarf everything else. Min stays near READ_WIDTH\n // so single-read edges still look like a normal read; max is generous so\n // hot edges read as obvious \"highways.\"\n let maxEdgeCount = 0;\n for (const e of edges.values()) {\n if (e.count > maxEdgeCount)\n maxEdgeCount = e.count;\n }\n const BAND_MIN_WIDTH = READ_WIDTH;\n const BAND_MAX_WIDTH = 60;\n const widthForCount = (c) => {\n if (maxEdgeCount <= 1)\n return BAND_MIN_WIDTH;\n return (BAND_MIN_WIDTH +\n (BAND_MAX_WIDTH - BAND_MIN_WIDTH) * Math.sqrt(c / maxEdgeCount));\n };\n const synthetic = [];\n let i = 0;\n for (const edge of edges.values()) {\n const id = COARSENED_ID_BASE + i;\n const label = `${edge.count.toLocaleString()} ${unit}${edge.count === 1 ? '' : 's'}: Node ${edge.sName} \u2192 Node ${edge.dName}`;\n coarsenedEdgeMeta.set(id, { count: edge.count, label });\n // Place each synthetic band so it touches *only* its endpoint nodes' edges:\n // \u2022 firstNodeOffset = src.sequenceLength \u2192 curve exits at src's right\n // pixel edge, no rectangle drawn inside src.\n // \u2022 finalNodeCoverLength = 0 \u2192 curve enters at dst's left pixel edge,\n // no rectangle drawn inside dst.\n // The crucial side effect is in placeReadSet's conflict check: at any\n // node B with an incoming edge (A\u2192B) and an outgoing edge (B\u2192C), the\n // incoming's finalNodeCoverLength=0 and the outgoing's firstNodeOffset=\n // B.length leave them with no horizontal overlap, so they share a single\n // lane. Node heights collapse from ~(N_in + N_out) lanes down to\n // ~max(N_in, N_out) lanes.\n //\n // Note: reverseReversedReads will swap firstNodeOffset/finalNodeCoverLength\n // for fully-reverse synthetics. The math works out: pre-flip values of\n // (srcLen, 0) become post-flip (newSrcLen, 0), which is the same pattern\n // \u2014 bands stay edge-only regardless of orientation.\n const srcLen = nodes[Math.abs(edge.sSigned)]?.sequenceLength ?? 0; // node may be absent for a dangling edge\n synthetic.push({\n id,\n sourceTrackID: edge.sourceTrackID,\n type: 'read',\n // Human-readable so the name surfaces sensibly in any UI that uses it\n // (right-click \u2192 add to group, etc.) \u2014 and so trackTooltipText fallback\n // shows something meaningful.\n name: label,\n sequence: [edge.sName, edge.dName],\n indexSequence: [],\n path: [],\n // Pre-set width so placeReads / placeReadSet allocate the right vertical\n // space and assignReadsToNodes (patched to honor pre-set widths) leaves\n // it alone.\n width: widthForCount(edge.count),\n firstNodeOffset: srcLen,\n finalNodeCoverLength: 0,\n sequenceNew: [\n { nodeName: edge.sName, mismatches: [] },\n { nodeName: edge.dName, mismatches: [] },\n ],\n mapping_quality: 0,\n is_secondary: false,\n sample_name: null,\n read_group: null,\n score: edge.count,\n });\n i += 1;\n }\n // Single-line debug summary so the user can verify node sizing.\n let tallestName = '?';\n let tallestH = 0;\n for (const n of nodes) {\n if (!n)\n continue;\n const h = n.contentHeight;\n if (h > tallestH) {\n tallestH = h;\n tallestName = n.name;\n }\n }\n if (DEBUG) {\n console.log(`[coarsened] ${unit}s_in=${source.length} edges_out=${synthetic.length} ` +\n `tallestNodeBeforePlace=${tallestName}(${tallestH.toFixed(1)})`);\n }\n return synthetic;\n}\nfunction createFeatureRectangle(node, nodeXStart, nodeXEnd, highlight, track, rectXStart, yStart, trackColor, reversalFlag) {\n let currentHighlight = highlight;\n let c;\n let co;\n let featureXStart;\n let featureXEnd;\n nodeXStart -= 8;\n nodeXEnd += 8;\n const nodeWidth = nodes[node.node].sequenceLength;\n node.features.sort((a, b) => a.start - b.start);\n node.features.forEach(feature => {\n if (currentHighlight !== feature.type) {\n // finish incoming rectangle\n c = generateTrackColor(track, currentHighlight);\n if (node.isForward) {\n featureXStart =\n nodeXStart +\n Math.round((feature.start * (nodeXEnd - nodeXStart + 1)) / nodeWidth);\n // overwrite narrow post-inversion rectangle if highlight starts near beginning of node\n if (reversalFlag && featureXStart < nodeXStart + 8) {\n featureXEnd =\n nodeXStart +\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n co = generateTrackColor(track, feature.type);\n shapes.featureRectangles.push({\n xStart: featureXStart,\n yStart,\n xEnd: featureXEnd,\n yEnd: yStart + track.width - 1,\n color: co,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (featureXStart > rectXStart + 1) {\n shapes.featureRectangles.push({\n xStart: rectXStart,\n yStart,\n xEnd: featureXStart - 1,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n }\n else {\n featureXStart =\n nodeXEnd -\n Math.round((feature.start * (nodeXEnd - nodeXStart + 1)) / nodeWidth);\n // overwrite narrow post-inversion rectangle if highlight starts near beginning of node\n if (reversalFlag && featureXStart > nodeXEnd - 8) {\n featureXEnd =\n nodeXEnd -\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n co = generateTrackColor(track, feature.type);\n shapes.featureRectangles.push({\n xStart: featureXEnd,\n yStart,\n xEnd: featureXStart,\n yEnd: yStart + track.width - 1,\n color: co,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (rectXStart > featureXStart + 1) {\n shapes.featureRectangles.push({\n xStart: featureXStart + 1,\n yStart,\n xEnd: rectXStart,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n }\n rectXStart = featureXStart;\n currentHighlight = feature.type;\n }\n if (feature.end < nodeWidth - 1 || feature.continue === undefined) {\n // finish internal rectangle\n c = generateTrackColor(track, currentHighlight);\n if (node.isForward) {\n featureXEnd =\n nodeXStart +\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n shapes.featureRectangles.push({\n xStart: rectXStart,\n yStart,\n xEnd: featureXEnd,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n else {\n featureXEnd =\n nodeXEnd -\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n shapes.featureRectangles.push({\n xStart: featureXEnd,\n yStart,\n xEnd: rectXStart,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n rectXStart = featureXEnd + 1;\n currentHighlight = 'plain';\n }\n });\n return { xStart: rectXStart, highlight: currentHighlight };\n}\nconst MIN_BEND_WIDTH = 7;\n// Emit the vertical rectangles + rounded corners for a track turning around at\n// one end of an order slot. The two directions are mirror images of each other:\n// a forward-to-reverse turn bulges out to the right of the node and consumes an\n// `extraRight` slot, a reverse-to-forward turn bulges out to the left and\n// consumes an `extraLeft` slot. `dir` is +1 for right, -1 for left.\nfunction generateTurnaround(dir, x, yStart, yEnd, trackWidth, trackColor, trackID, order, type, trackName) {\n const extra = dir === 1 ? extraRight : extraLeft;\n const offset = 10 * extra[order];\n // The turn's apex, and the outward direction from it.\n const apex = x + dir * (offset + 5);\n const radius = MIN_BEND_WIDTH;\n const stem = Math.min(MIN_BEND_WIDTH, trackWidth);\n const yTop = Math.min(yStart, yEnd);\n const yBottom = Math.max(yStart, yEnd);\n // The incoming/outgoing stubs run from the node's edge out to the apex. The\n // one-pixel asymmetry between the two directions is carried over verbatim\n // from the original pair of functions.\n const stubNear = dir === 1 ? x : apex - 1;\n const stubFar = dir === 1 ? apex : x;\n const horizontal = (segTop) => {\n shapes.verticalRectangles.push({\n xStart: stubNear,\n yStart: segTop,\n xEnd: stubFar,\n yEnd: segTop + trackWidth - 1,\n color: trackColor,\n id: trackID,\n name: trackName,\n type,\n });\n };\n // elongate the incoming and outgoing rectangles a bit past the node\n horizontal(yStart);\n shapes.verticalRectangles.push({\n xStart: dir === 1 ? apex + radius : apex - radius - stem,\n yStart: yTop + trackWidth + radius - 1,\n xEnd: dir === 1 ? apex + radius + stem - 1 : apex - radius - 1,\n yEnd: yBottom - radius + 1,\n color: trackColor,\n id: trackID,\n name: trackName,\n type,\n });\n horizontal(yEnd);\n const inner = apex + dir * radius;\n const outer = apex + dir * (radius + stem);\n // bottom 90 degree bend\n let d = `M ${apex} ${yBottom}`;\n d += ` Q ${inner} ${yBottom} ${inner} ${yBottom - radius}`;\n d += ` H ${outer}`;\n d += ` Q ${outer} ${yBottom + trackWidth} ${apex} ${yBottom + trackWidth}`;\n d += ' Z ';\n shapes.corners.push({ path: d, color: trackColor, id: trackID, type });\n // top 90 degree bend\n d = `M ${apex} ${yTop}`;\n d += ` Q ${outer} ${yTop} ${outer} ${yTop + trackWidth + radius}`;\n d += ` H ${inner}`;\n d += ` Q ${inner} ${yTop + trackWidth} ${apex} ${yTop + trackWidth}`;\n d += ' Z ';\n shapes.corners.push({ path: d, color: trackColor, id: trackID, type });\n extra[order] += 1;\n}\nexport function nodePixelCoordinatesInX(node) {\n // Add and subtract 4 to account for stroke width - TODO: figure out what the 4 means\n const nodeLeftX = node.x - 4;\n const nodeRightX = node.x + node.pixelWidth + 4;\n return [nodeLeftX, nodeRightX];\n}\n// calculate node widths depending on sequence lengths and chosen calculation method\nfunction generateNodeWidth() {\n // Promote the input's optional sequenceLength; Node declares it required, so\n // widen to InputNode to make the still-possibly-undefined read explicit.\n nodes.forEach((node) => {\n if (node.sequenceLength === undefined) {\n node.sequenceLength = node.seq.length;\n }\n });\n switch (config.nodeWidthOption) {\n case 'compressed':\n nodes.forEach(node => {\n node.width = 1 + Math.log(node.sequenceLength) / Math.log(2);\n node.pixelWidth = Math.round((node.width - 1) * 8.401);\n });\n break;\n case 'small':\n nodes.forEach(node => {\n node.width = node.sequenceLength / 100;\n node.pixelWidth = Math.round((node.width - 1) * 8.401);\n });\n break;\n case 'fixed':\n // when there's no reads in the node, it should be a little wider\n nodes.forEach(node => {\n node.width = 10;\n node.pixelWidth = Math.round(node.width * 8.401);\n });\n break;\n case 'normal': {\n // The sequence is drawn in a monospace font, so one character's width,\n // which the caller measures, sizes every node.\n const charWidth = config.charWidth;\n nodes.forEach(node => {\n node.width = node.sequenceLength;\n const len = node.seq?.length ?? 1;\n node.pixelWidth = Math.round(charWidth * len);\n });\n break;\n }\n default:\n throw new Error(`${config.nodeWidthOption} not implemented`);\n }\n}\n// remove redundant nodes\n// two nodes A and B can be merged if all tracks leaving A go directly into B\n// and all tracks entering B come directly from A\n// (plus no inversions involved)\nfunction mergeNodes() {\n let nodeName;\n let nodeName2;\n // One set per node index, including the hole at 0 (Array.from materializes\n // holes, unlike forEach/map).\n const predSets = Array.from(nodes, () => new Set());\n const succSets = Array.from(nodes, () => new Set());\n let tracksAndReads;\n if (config.showReads && reads.length > 0)\n tracksAndReads = tracks.concat(reads);\n else\n tracksAndReads = tracks;\n tracksAndReads.forEach(track => {\n for (let i = 0; i < track.sequence.length; i += 1) {\n if (!isReverse(track.sequence[i])) {\n // forward Node\n if (i > 0) {\n nodeName = track.sequence[i - 1];\n predSets[nodeMap.get(track.sequence[i])].add(nodeName);\n if (isReverse(nodeName)) {\n // add 2 predecessors, to make sure there is no node merging in this case\n predSets[nodeMap.get(track.sequence[i])].add(forward(nodeName));\n }\n }\n else if (track.type === 'haplotype') {\n predSets[nodeMap.get(track.sequence[i])].add('None');\n }\n if (i < track.sequence.length - 1) {\n nodeName = track.sequence[i + 1];\n succSets[nodeMap.get(track.sequence[i])].add(nodeName);\n if (isReverse(nodeName)) {\n // add 2 successors, to make sure there is no node merging in this case\n succSets[nodeMap.get(track.sequence[i])].add(forward(nodeName));\n }\n }\n else if (track.type === 'haplotype') {\n succSets[nodeMap.get(track.sequence[i])].add('None');\n }\n }\n else {\n // reverse Node\n nodeName = forward(track.sequence[i]);\n if (i > 0) {\n nodeName2 = track.sequence[i - 1];\n if (isReverse(nodeName2)) {\n succSets[nodeMap.get(nodeName)].add(forward(nodeName2));\n }\n else {\n // add 2 successors, to make sure there is no node merging in this case\n succSets[nodeMap.get(nodeName)].add(nodeName2);\n succSets[nodeMap.get(nodeName)].add(reverse(nodeName2));\n }\n }\n else if (track.type === 'haplotype') {\n succSets[nodeMap.get(nodeName)].add('None');\n }\n if (i < track.sequence.length - 1) {\n nodeName2 = track.sequence[i + 1];\n if (isReverse(nodeName2)) {\n predSets[nodeMap.get(nodeName)].add(forward(nodeName2));\n }\n else {\n predSets[nodeMap.get(nodeName)].add(nodeName2);\n predSets[nodeMap.get(nodeName)].add(reverse(nodeName2));\n }\n }\n else if (track.type === 'haplotype') {\n predSets[nodeMap.get(nodeName)].add('None');\n }\n }\n }\n });\n // convert sets to arrays\n const pred = predSets.map(s => Array.from(s));\n const succ = succSets.map(s => Array.from(s));\n // update reads which pass through merging nodes\n if (config.showReads && reads.length > 0) {\n // sort nodes by order, then by y-coordinate\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n // iterate over all nodes and calculate their position within the new merged node\n // Both maps carry an entry for each orientation of every node name (\"7\"\n // and \"-7\"), because the read update below looks up whichever orientation\n // the read happens to visit.\n const mergeOffset = new Map();\n // maps to leftmost node of a node's \"merging cascade\"\n const mergeOrigin = new Map();\n const offsetOf = (nodeName) => {\n const offset = mergeOffset.get(nodeName);\n if (offset === undefined) {\n throw new Error(`No merge offset recorded for node ${nodeName}`);\n }\n return offset;\n };\n const originOf = (nodeName) => {\n const origin = mergeOrigin.get(nodeName);\n if (origin === undefined) {\n throw new Error(`No merge origin recorded for node ${nodeName}`);\n }\n return origin;\n };\n sortedNodes.forEach(node => {\n const predecessor = mergeableWithPred(nodeMap.get(node.name), pred, succ);\n if (predecessor) {\n // Nodes are visited in order, so the predecessor of a merge cascade is\n // always recorded before the node that follows it.\n const offset = offsetOf(predecessor) + nodeByName(predecessor).sequenceLength;\n mergeOffset.set(node.name, offset);\n mergeOffset.set(reverse(node.name), offset);\n mergeOrigin.set(node.name, originOf(predecessor));\n mergeOrigin.set(reverse(node.name), originOf(predecessor));\n }\n else {\n mergeOffset.set(node.name, 0);\n mergeOffset.set(reverse(node.name), 0);\n mergeOrigin.set(node.name, node.name);\n mergeOrigin.set(reverse(node.name), node.name);\n }\n });\n reads.forEach(read => {\n const sequenceNew = read.sequenceNew ?? [];\n read.firstNodeOffset =\n (read.firstNodeOffset ?? 0) + offsetOf(read.sequence[0]);\n read.finalNodeCoverLength =\n (read.finalNodeCoverLength ?? 0) +\n offsetOf(read.sequence[read.sequence.length - 1]);\n for (let i = read.sequence.length - 1; i >= 0; i -= 1) {\n const nodeName = forward(read.sequence[i]);\n const predecessor = mergeableWithPred(nodeMap.get(nodeName), pred, succ);\n if (predecessor &&\n mergeableWithSucc(nodeMap.get(predecessor), pred, succ)) {\n if (i > 0) {\n read.sequence.splice(i, 1);\n // adjust position of mismatches\n const predLength = nodeByName(predecessor).sequenceLength;\n sequenceNew[i].mismatches.forEach(mismatch => {\n mismatch.pos += predLength;\n });\n // append mismatches to previous entry's mismatches\n sequenceNew[i - 1].mismatches = sequenceNew[i - 1].mismatches.concat(sequenceNew[i].mismatches);\n sequenceNew.splice(i, 1);\n }\n else {\n read.sequence[0] = originOf(read.sequence[0]);\n const firstEntry = sequenceNew[0];\n const firstOffset = offsetOf(firstEntry.nodeName);\n firstEntry.mismatches.forEach(mismatch => {\n mismatch.pos += firstOffset;\n });\n firstEntry.nodeName = originOf(firstEntry.nodeName);\n }\n }\n }\n });\n }\n // update node sequences + sequence lengths\n for (let i = 0; i < nodes.length; i += 1) {\n if (mergeableWithSucc(i, pred, succ) && !mergeableWithPred(i, pred, succ)) {\n let donor = i;\n while (mergeableWithSucc(donor, pred, succ)) {\n donor = nodeMap.get(forward(succ[donor][0]));\n nodes[i].sequenceLength += nodes[donor].sequenceLength;\n nodes[i].seq += nodes[donor].seq;\n }\n }\n }\n // actually merge the nodes by removing the corresponding nodes from track data\n tracks.forEach(track => {\n for (let i = track.sequence.length - 1; i >= 0; i -= 1) {\n nodeName = forward(track.sequence[i]);\n const nodeIndex = nodeMap.get(nodeName);\n if (mergeableWithPred(nodeIndex, pred, succ)) {\n track.sequence.splice(i, 1);\n }\n }\n });\n // remove the nodes from node-array\n for (let i = nodes.length - 1; i >= 0; i -= 1) {\n if (mergeableWithPred(i, pred, succ)) {\n nodes.splice(i, 1);\n }\n }\n}\nfunction mergeableWithPred(index, pred, succ) {\n if (pred[index].length !== 1)\n return false;\n if (pred[index][0] === 'None')\n return false;\n const predecessor = forward(pred[index][0]);\n const predecessorIndex = nodeMap.get(predecessor);\n if (succ[predecessorIndex].length !== 1)\n return false;\n if (succ[predecessorIndex][0] === 'None')\n return false;\n return predecessor;\n}\nfunction mergeableWithSucc(index, pred, succ) {\n if (succ[index].length !== 1)\n return false;\n if (succ[index][0] === 'None')\n return false;\n const successor = forward(succ[index][0]);\n const successorIndex = nodeMap.get(successor);\n if (pred[successorIndex].length !== 1)\n return false;\n if (pred[successorIndex][0] === 'None')\n return false;\n return true;\n}\nfunction filterReads(readList) {\n const focusNames = config.focusReadNames\n ? new Set(config.focusReadNames)\n : null;\n return readList.filter(read => read.is_secondary !== true &&\n (read.mapping_quality ?? 0) >= config.mappingQualityCutoff &&\n (focusNames === null ||\n (read.name !== undefined && focusNames.has(read.name))));\n}\n", "function groupBy(items, key) {\n const groups = new Map();\n for (const item of items) {\n const k = key(item);\n const group = groups.get(k);\n if (group === undefined) {\n groups.set(k, [item]);\n }\n else {\n group.push(item);\n }\n }\n return groups;\n}\nfunction compareCurvesByXYStartValue(a, b) {\n if (a.xStart < b.xStart)\n return 1;\n else if (a.xStart > b.xStart)\n return -1;\n else if (a.yStart > b.yStart)\n return 1;\n else if (a.yStart < b.yStart)\n return -1;\n return 0;\n}\n// The curves of one track type in draw order, each with its `path` set. Curves\n// between the same pair of nodes form a group whose control points fan out, so\n// a bundle of tracks changing lanes together stays parallel rather than\n// crossing at one x.\nexport function curvePaths(curves, type) {\n const groupedCurves = groupBy(curves.filter(curve => curve.type === type), curve => `${curve.nodeStart},${curve.nodeEnd}`);\n groupedCurves.forEach(curveGroup => {\n // Control point adjustment range: 30% to 70% of the original x values\n let adjustValue = 0.3;\n let adjustIncrement = 0.4 / curveGroup.length;\n // Ignore Beziar Curve skewing when a group is not too big\n if (curveGroup.length <= 5) {\n adjustValue = 0.5;\n adjustIncrement = 0;\n }\n curveGroup.sort(compareCurvesByXYStartValue);\n curveGroup.forEach(curve => {\n let xAdjusted;\n // NextAdjusted is used to try to draw a parallel curve on the way back\n let xNextAdjusted;\n if (curve.yStart < curve.yEnd) {\n xAdjusted =\n curve.xStart + (curve.xEnd - curve.xStart) * (1 - adjustValue);\n adjustValue += adjustIncrement;\n xNextAdjusted =\n curve.xStart + (curve.xEnd - curve.xStart) * (1 - adjustValue);\n }\n else {\n xAdjusted = curve.xStart + (curve.xEnd - curve.xStart) * adjustValue;\n adjustValue += adjustIncrement;\n xNextAdjusted = curve.xStart + (curve.xEnd - curve.xStart) * adjustValue;\n }\n let d = `M ${curve.xStart} ${curve.yStart}`;\n d += ` C ${xAdjusted} ${curve.yStart} ${xAdjusted} ${curve.yEnd} ${curve.xEnd} ${curve.yEnd}`;\n d += ` V ${curve.yEnd + curve.width}`;\n d += ` C ${xNextAdjusted} ${curve.yEnd + curve.width} ${xNextAdjusted} ${curve.yStart + curve.width} ${curve.xStart} ${curve.yStart + curve.width}`;\n d += ' Z';\n curve.path = d;\n });\n });\n // One flat list ordered by group, then by the within-group sort\n const groupKeys = Array.from(groupedCurves.keys());\n groupKeys.sort(function (a, b) {\n const aParts = a.split(',').map(Number);\n const bParts = b.split(',').map(Number);\n const a0 = aParts[0] ?? 0;\n const a1 = aParts[1] ?? 0;\n const b0 = bParts[0] ?? 0;\n const b1 = bParts[1] ?? 0;\n if (a0 < b0) {\n return -1;\n }\n else if (a0 > b0) {\n return 1;\n }\n else {\n if (a1 < b1) {\n return -1;\n }\n else if (a1 > b1) {\n return 1;\n }\n else {\n return 0;\n }\n }\n });\n const flattenedGroups = [];\n for (const key of groupKeys) {\n const group = groupedCurves.get(key);\n if (group !== undefined) {\n flattenedGroups.push(...group);\n }\n }\n return flattenedGroups;\n}\n// A node's outline: a rounded box 9 units outside the node's x extent and its\n// content height, the tracks through it inside.\nexport function nodeOutlinePath(node) {\n // top left arc\n let d = `M ${node.x - 9} ${node.y} Q ${node.x - 9} ${node.y - 9} ${node.x} ${node.y - 9}`;\n let x = node.x;\n let y = node.y - 9;\n // top straight\n if (node.width > 1) {\n x += node.pixelWidth;\n d += ` L ${x} ${y}`;\n }\n // top right arc\n d += ` Q ${x + 9} ${y} ${x + 9} ${y + 9}`;\n x += 9;\n y += 9;\n // right straight\n if (node.contentHeight > 0) {\n y += node.contentHeight - 0;\n d += ` L ${x} ${y}`;\n }\n // bottom right arc\n d += ` Q ${x} ${y + 9} ${x - 9} ${y + 9}`;\n x -= 9;\n y += 9;\n // bottom straight\n if (node.width > 1) {\n x -= node.pixelWidth;\n d += ` L ${x} ${y}`;\n }\n // bottom left arc\n d += ` Q ${x - 9} ${y} ${x - 9} ${y - 9}`;\n x -= 9;\n y -= 9;\n // left straight\n if (node.contentHeight > 0) {\n y -= node.contentHeight - 0;\n d += ` L ${x} ${y}`;\n }\n return d;\n}\n", "import { layoutTubeMap } from '@gmod/tubemap-core'\n\nimport { isBackbone } from '../anchoredNodes'\nimport { pathOrigin } from '../pathAnchoring'\nimport { pathCssColor, pathGreyCssColor } from '../pathColors'\nimport {\n canonicalStrand,\n readColor,\n stepName,\n tubeMapReads,\n} from '../tubeMap/reads'\n\nimport type { Coarsened } from '../tubeMap/coarsen'\nimport type { Graph, LayoutResult, NodeSegment } from '../types'\nimport type {\n InputNode,\n InputTrack,\n LayoutNode,\n TubeMapLayout,\n} from '@gmod/tubemap-core'\n\n// The sequenceTubeMap drawing of a graph's paths: every P or W line a tube,\n// every node a box the tubes pass through, laid out by @gmod/tubemap-core. The\n// canvas draws none of it; TubeMapOverlay draws the shapes, and nodePositions\n// is each box's centreline, for the hit test, the fit and the labels.\n//\n// Two x axes. `own` is the tube map's: columns in node order, a node as wide as\n// log2 of its length, which is the compact picture sequenceTubeMap draws.\n// `reference` pins each column to the reference bp its backbone node covers,\n// so the tubes line up with the linear view's other tracks; the overlay makes\n// room for the curves between columns when it draws (tubeMapWarp.ts).\n\n// A column of the tube map: the nodes at one `order`, the tube x they were\n// drawn over, and under the reference axis the reference bp they stand for.\nexport interface TubeMapColumn {\n order: number\n x0: number\n x1: number\n bp0: number\n bp1: number\n}\n\nexport interface TubeMapDrawing {\n layout: TubeMapLayout\n // each path's tube colour, in the order the graph states its paths\n pathColors: string[]\n // tube y minus this is the drawing's y, so the drawing starts at 0\n yOffset: number\n // set on the reference axis, sorted by order\n columns?: TubeMapColumn[]\n // set when the cut was folded before layout (coarsen.ts): the drawing's node\n // ids are the coarse graph's, not the cut's\n coarse?: Coarsened\n}\n\n// Strand of every visit a path makes to a segment, in walk order, so the k-th\n// time a path reaches a segment reads the k-th entry. Visits name a path\n// without the range suffix `odgi extract` appends (pathOrigin).\nexport function visitStrands(graph: Graph) {\n const strands = new Map<string, ('+' | '-')[]>()\n for (const [segment, visits] of graph.pathVisits ?? []) {\n for (const visit of visits) {\n const key = `${visit.path}\\t${segment}`\n const list = strands.get(key)\n if (list) {\n list.push(visit.strand)\n } else {\n strands.set(key, [visit.strand])\n }\n }\n }\n return strands\n}\n\n// A tube map track per path, the reference first: the layout straightens\n// track 0 and orders every other node around it. A step reads its node in\n// reverse where the path walks the segment against the strand the node is\n// drawn in.\nexport function tubeMapTracks(graph: Graph): InputTrack[] {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const strands = visitStrands(graph)\n const paths = (graph.paths ?? []).map((path, index) => ({ path, index }))\n const reference = paths.findIndex(\n p => pathOrigin(p.path.name).name === graph.referencePath,\n )\n if (reference > 0) {\n paths.unshift(...paths.splice(reference, 1))\n }\n return paths.flatMap(({ path, index }) => {\n const visitName = pathOrigin(path.name).name\n const seen = new Map<string, number>()\n const sequence = path.nodeIds.flatMap(id => {\n const node = nodeById.get(id)\n if (!node) {\n return []\n }\n const k = seen.get(node.name) ?? 0\n seen.set(node.name, k + 1)\n const strand =\n strands.get(`${visitName}\\t${node.name}`)?.[k] ?? canonicalStrand(node)\n return [stepName(node, strand)]\n })\n return sequence.length > 0\n ? [\n {\n id: index,\n name: path.name,\n sequence,\n sourceTrackID: 0,\n type: 'haplotype' as const,\n },\n ]\n : []\n })\n}\n\nfunction tubeMapNodes(graph: Graph): InputNode[] {\n return graph.nodes.map(n => ({\n name: n.id,\n seq: '',\n sequenceLength: Math.max(1, n.length),\n }))\n}\n\nexport function hasTubeMapPaths(graph: Graph) {\n return (graph.paths?.length ?? 0) > 0\n}\n\n// Tubes narrow as paths are added, so ninety haplotypes stack about as tall as\n// forty at full width rather than filling a screen and a half each.\nconst STACK_PX = 600\nconst MIN_TUBE_PX = 3\nconst MAX_TUBE_PX = 15\n\nexport function tubeWidth(trackCount: number) {\n return Math.max(\n MIN_TUBE_PX,\n Math.min(MAX_TUBE_PX, Math.floor(STACK_PX / Math.max(1, trackCount))),\n )\n}\n\nfunction runTubeMap(graph: Graph) {\n const paths = graph.paths ?? []\n const tracks = tubeMapTracks(graph)\n const reads = graph.reads\n ? tubeMapReads(graph, graph.reads, paths.length)\n : []\n const tubeColor = reads.length > 0 ? pathGreyCssColor : pathCssColor\n const pathColors = paths.map((_, i) => tubeColor(i, paths.length))\n const layout = layoutTubeMap(tubeMapNodes(graph), tracks, reads, {\n nodeWidthOption: 'compressed',\n trackWidth: tubeWidth(tracks.length),\n trackColor: track =>\n track.type === 'read' ? readColor(track) : pathColors[track.id]!,\n })\n return layout ? { layout, pathColors } : undefined\n}\n\n// Drawn nodes only: an unreached one has no x.\nfunction drawnNodes(layout: TubeMapLayout) {\n const nodes: LayoutNode[] = []\n layout.nodes.forEach(node => {\n if (node.order >= 0) {\n nodes.push(node)\n }\n })\n return nodes\n}\n\nfunction centreline(node: LayoutNode, yOffset: number, x0: number, x1: number) {\n const y = node.y + node.contentHeight / 2 - yOffset\n return [\n { x: x0, y },\n { x: x1, y },\n ]\n}\n\nfunction extentOf(layout: TubeMapLayout, yOffset: number) {\n return {\n minY: layout.bounds.minY - yOffset,\n maxY: layout.bounds.maxY - yOffset,\n }\n}\n\nexport function tubeMapLayout(graph: Graph): LayoutResult | undefined {\n const run = hasTubeMapPaths(graph) ? runTubeMap(graph) : undefined\n if (!run) {\n return undefined\n }\n const { layout, pathColors } = run\n const yOffset = layout.bounds.minY\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const node of drawnNodes(layout)) {\n nodePositions[node.name] = centreline(\n node,\n yOffset,\n node.x,\n node.x + node.pixelWidth,\n )\n }\n const { minY, maxY } = extentOf(layout, yOffset)\n return {\n nodePositions,\n tubeMap: { layout, yOffset, pathColors },\n extent: {\n minX: layout.bounds.minX,\n maxX: layout.bounds.maxX,\n minY,\n maxY,\n },\n }\n}\n\n// Each column's reference bp: the span of its backbone nodes, whose merged\n// length the layout summed, or a point where the previous column ended for a\n// column of inserted sequence alone. Clamped to run left to right, so an\n// inversion the reference walks backwards cannot fold the axis over.\nfunction tubeMapColumns(graph: Graph, layout: TubeMapLayout): TubeMapColumn[] {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const byOrder = new Map<number, LayoutNode[]>()\n for (const node of drawnNodes(layout)) {\n const list = byOrder.get(node.order)\n if (list) {\n list.push(node)\n } else {\n byOrder.set(node.order, [node])\n }\n }\n const columns: TubeMapColumn[] = []\n let end = -Infinity\n for (const order of [...byOrder.keys()].sort((a, b) => a - b)) {\n const nodes = byOrder.get(order)!\n let bp0 = Infinity\n let bp1 = -Infinity\n for (const node of nodes) {\n const graphNode = nodeById.get(node.name)\n if (graphNode && isBackbone(graphNode)) {\n bp0 = Math.min(bp0, graphNode.stable.start)\n bp1 = Math.max(bp1, graphNode.stable.start + node.sequenceLength)\n }\n }\n if (bp0 === Infinity) {\n bp0 = bp1 = end\n }\n if (bp0 < end) {\n bp0 = end\n bp1 = Math.max(end, bp1)\n }\n end = Math.max(end, bp1)\n columns.push({\n order,\n x0: Math.min(...nodes.map(n => n.x)),\n x1: Math.max(...nodes.map(n => n.x + n.pixelWidth)),\n bp0,\n bp1,\n })\n }\n // columns before the first backbone node take its start\n const first = columns.find(c => c.bp0 !== -Infinity)\n for (const column of columns) {\n if (column.bp0 === -Infinity) {\n column.bp0 = column.bp1 = first?.bp0 ?? 0\n }\n }\n return columns\n}\n\nfunction pathsReachBackbone(graph: Graph) {\n return graph.nodes.some(isBackbone) && hasTubeMapPaths(graph)\n}\n\nexport function tubeMapReferenceLayout(graph: Graph): LayoutResult | undefined {\n const run = pathsReachBackbone(graph) ? runTubeMap(graph) : undefined\n if (!run) {\n return undefined\n }\n const { layout, pathColors } = run\n const columns = tubeMapColumns(graph, layout)\n const byOrder = new Map(columns.map(c => [c.order, c]))\n const yOffset = layout.bounds.minY\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const node of drawnNodes(layout)) {\n const column = byOrder.get(node.order)!\n nodePositions[node.name] = centreline(node, yOffset, column.bp0, column.bp1)\n }\n return {\n nodePositions,\n tubeMap: { layout, yOffset, columns, pathColors },\n referenceAxis: true,\n pixelRows: true,\n extent: extentOf(layout, yOffset),\n }\n}\n", "import { panSNContig, 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// tell nodes on the reference walk's path from nodes off it. Off the path is\n// an alternative route through the graph, not sequence the reference lacks: at\n// a duplication the graph may route a copy the reference carries through nodes\n// of its own, so an off-path run is no copy count.\n\nexport interface WalkRun {\n // bp offset from the start of this walk's slice\n start: number\n bp: number\n onReference: boolean\n // lowest reference bp an on-reference run covers; the run covers `bp` of\n // reference contiguously. In a tandem array that is the reference copy the\n // graph threads this copy through, which need not be the one it most\n // resembles.\n referenceStart?: number\n // the walk crosses that reference from its end back to its start\n reversed?: true\n // bases of the walk's contig between two pieces the cut returned: the walk\n // left the window's nodes and came back, and the cut holds nothing of them\n gap?: true\n}\n\n// Where a row's bar lies on the walk's own contig: the contig position at the\n// bar's left end, and whether positions fall rightward along it\nexport interface WalkAxis {\n contig: string\n start: number\n reversed: boolean\n}\n\nexport interface WalkRow {\n name: string\n label: string\n sample: string\n haplotype?: number\n bp: number\n offReferenceBp: number\n // bp of the gap runs, counted in `bp` and not in `offReferenceBp`\n gapBp: 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 // undefined where a piece states no start or overlaps the one before, so\n // bar offsets don't map linearly onto the contig\n axis?: WalkAxis\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// A walk's steps through the cut: its node ids, and between two pieces of it\n// the bp of contig the cut does not hold\ntype Step = string | number\n\n// The pieces of one walk in contig order, with the bp between them as gaps.\n// A piece with no start, or one that overlaps the last, follows it directly.\nfunction stepsOf(pieces: GraphPath[], lengthOf: Map<string, number>): Step[] {\n const steps: Step[] = []\n let end: number | undefined\n for (const piece of pieces) {\n if (end !== undefined && piece.start !== undefined && piece.start > end) {\n steps.push(piece.start - end)\n }\n steps.push(...piece.nodeIds)\n const bp = piece.nodeIds.reduce(\n (sum, id) => sum + (lengthOf.get(id) ?? 0),\n 0,\n )\n end = piece.start === undefined ? undefined : piece.start + bp\n }\n return steps\n}\n\n// Where each step lies on the walk's contig, steps and gaps alike; undefined\n// where a piece states no start or overlaps the one before\nfunction stepSpans(pieces: GraphPath[], lengthOf: Map<string, number>) {\n const spans: { start: number; end: number }[] = []\n let end: number | undefined\n for (const piece of pieces) {\n if (piece.start === undefined || (end !== undefined && piece.start < end)) {\n return undefined\n }\n if (end !== undefined && piece.start > end) {\n spans.push({ start: end, end: piece.start })\n }\n let pos = piece.start\n for (const id of piece.nodeIds) {\n const len = lengthOf.get(id) ?? 0\n spans.push({ start: pos, end: pos + len })\n pos += len\n }\n end = pos\n }\n return spans\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 steps: Step[],\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: steps, complete: true, from: -1, to: -1 }\n }\n let i0 = -1\n let i1 = -1\n let bestEnd = -Infinity\n let bestStart = Infinity\n steps.forEach((id, i) => {\n const s = typeof id === 'string' ? span.get(id) : undefined\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: steps, complete: false, from: -1, to: -1 }\n }\n const ids = steps.slice(Math.min(i0, i1) + 1, Math.max(i0, i1))\n return {\n ids: i0 < i1 ? ids : ids.reverse(),\n complete: true,\n from: i0,\n to: i1,\n }\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. A cut over several\n // fragments of the reference holds one record per fragment.\n const isReference = (p: GraphPath) =>\n pathOrigin(p.name).name === graph.referencePath\n const referencePieces = paths.some(isReference)\n ? paths.filter(isReference).sort((a, b) => (a.start ?? 0) - (b.start ?? 0))\n : paths.slice(0, 1)\n const first = referencePieces[0]\n const others = paths.filter(p => !referencePieces.includes(p))\n if (!first || others.length === 0) {\n return undefined\n }\n // a cut hands a walk back as one record per piece inside its nodes\n const walks = new Map<string, GraphPath[]>()\n for (const path of others) {\n walks.set(path.name, [...(walks.get(path.name) ?? []), path])\n }\n const lengthOf = new Map(graph.nodes.map(n => [n.id, n.length]))\n const referenceStart = first.start ?? 0\n\n const cut = region && region.end > region.start ? region : undefined\n const span = new Map<string, { start: number; end: number }>()\n for (const piece of referencePieces) {\n let pos = piece.start ?? 0\n for (const id of piece.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\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 = (pieces: GraphPath[]): WalkRow => {\n const path = pieces[0]!\n const ordered = [...pieces].sort((a, b) => (a.start ?? 0) - (b.start ?? 0))\n const { ids, complete, from, to } = sliceBetween(\n stepsOf(ordered, lengthOf),\n span,\n cut,\n flanked,\n )\n const spans = stepSpans(ordered, lengthOf)\n const contig = path.contig ?? panSNContig(pathOrigin(path.name).name)\n const axis: WalkAxis | undefined = !spans?.length\n ? undefined\n : from < 0\n ? { contig, start: spans[0]!.start, reversed: false }\n : from < to\n ? { contig, start: spans[from]!.end, reversed: false }\n : { contig, start: spans[from]!.start, reversed: true }\n const runs: WalkRun[] = []\n let bp = 0\n let offReferenceBp = 0\n let gapBp = 0\n // which way the last run steps through the reference, 0 while it holds\n // one node\n let step = 0\n for (const id of ids) {\n if (typeof id === 'number') {\n runs.push({ start: bp, bp: id, onReference: false, gap: true })\n bp += id\n gapBp += id\n step = 0\n continue\n }\n const len = lengthOf.get(id) ?? 0\n const s = span.get(id)\n const last = runs.at(-1)\n const at = last?.referenceStart\n const forward =\n s && at !== undefined && step >= 0 && at + last!.bp === s.start\n const backward = s && at !== undefined && step <= 0 && s.end === at\n if (!s && last && !last.onReference && !last.gap) {\n last.bp += len\n } else if (forward || backward) {\n last!.bp += len\n if (!forward) {\n last!.referenceStart = s.start\n last!.reversed = true\n }\n step = forward ? 1 : -1\n } else {\n runs.push({\n start: bp,\n bp: len,\n onReference: s !== undefined,\n referenceStart: s?.start,\n })\n step = 0\n }\n bp += len\n if (!s) {\n offReferenceBp += len\n }\n }\n return {\n name: path.name,\n label: labelOf(path),\n sample: sampleOf(path),\n haplotype: path.haplotype,\n bp,\n offReferenceBp,\n gapBp,\n complete,\n runs,\n axis,\n }\n }\n\n const origin = cut ? cut.start : referenceStart\n return {\n origin,\n unit,\n reference: rowOf(referencePieces),\n rows: [...walks.values()]\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 {\n backboneNodes,\n backbonePositions,\n overlapsWindow,\n} 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 // The bars start at the window, so row 0 draws only the backbone over it;\n // the cut's context nodes would run left of the bars, under the labels.\n const overWindow = region\n ? backbone.filter(n => overlapsWindow(n, region))\n : backbone\n const nodePositions = backbonePositions(\n overWindow.length > 0 ? overWindow : backbone,\n )\n const rowLabels: RowLabel[] = [\n { label: walks.reference.label, y: 0 },\n ...walks.rows.map((row, i) => ({\n label: row.label,\n y: (i + 1) * ROW_HEIGHT_PX,\n })),\n ]\n return {\n nodePositions,\n rowLabels,\n referenceAxis: true,\n pixelRows: true,\n extent: walkRowsExtent(walks),\n }\n}\n", "import { isBackbone, isOffReference } from './anchoredNodes'\nimport { anchoredLayout } from './layout/anchoredLayout'\nimport { orderedLayout } from './layout/orderedLayout'\nimport { sampleRowLayout } from './layout/sampleRowLayout'\nimport {\n hasTubeMapPaths,\n tubeMapLayout,\n tubeMapReferenceLayout,\n} from './layout/tubeMapLayout'\nimport { walkRowLayout } from './layout/walkRowLayout'\n\nimport type { Graph, LayoutResult } from './types'\n\n// The force layout's menu label. jbrowse-components' check-menu-labels scans\n// this src/ for string literals, so a documented **Layout \u2192 \u2026** path is held\n// to what the dropdown renders.\nexport const FORCE_LAYOUT_LABEL = 'Force-directed layout'\n\n// One table drives the persisted enum, the toolbar dropdown, and the dispatch in\n// the model, so adding a layout is one entry rather than several edits that can\n// disagree.\n//\n// `run` returning undefined means \"this mode can't draw this graph\" and hands\n// off to the remote Bandage FMMM engine. 'auto' uses it when a GFA has no\n// rank-0 backbone \u2014 neither declared in rGFA tags nor derivable from P/W lines\n// (pathAnchoring.ts) \u2014 and 'force' is simply the mode that always takes it.\nexport interface LayoutMode {\n value: string\n label: string\n description: string\n // `region` is the interval the cut was made for, absent for a whole-file\n // import. The reference-anchored modes scale their rows and their allele\n // floor against it rather than against the backbone they were handed, which\n // a long-range allele's far anchor stretches well past the window\n // (referenceSpan). `keepRows` is the sample rows on screen, which only the\n // sample-row layout reads (LayoutResult.sampleRows).\n run: (\n graph: Graph,\n region?: { start: number; end: number },\n keepRows?: readonly string[],\n ) => LayoutResult | undefined\n // whether this mode can draw this graph at all; the dropdown greys out the\n // rest rather than hiding them, so the reason a mode is unavailable stays\n // visible instead of the menu silently changing shape between graphs\n available: (graph: Graph) => boolean\n // whether a cut for this mode carries a window-width margin each side, to\n // pan over before the next cut. A mode that draws a picture of the window\n // \u2014 force, ordered, and walk rows, whose bars are lengths through it \u2014 is\n // cut to the window alone.\n cutMargins: boolean\n // whether this mode measures each haplotype's walk whole, so a GBZ cut\n // follows every snarl that leaves the window rather than breaking a walk\n // where it leaves the window's snarls: at a duplication array the lighter\n // cut hands one walk back in pieces\n wholeWalks: boolean\n // whether `run` draws this graph itself, as opposed to handing off to the\n // engine. Not the same question as `available` and not derivable from it:\n // 'force' is available for every graph and draws none of them itself. The two\n // coincide for the anchored modes, which is stated by sharing the predicate\n // rather than by restating it.\n drawsLocally: (graph: Graph) => boolean\n // whether the canvas draws this mode's nodes, which genes, bubble halos,\n // deletion arcs, the reference strip, facet panels and an SVG figure sit\n // on. A tube map and walk rows draw a picture of their own over it.\n drawsNodes: boolean\n}\n\nconst hasBackbone = (graph: Graph) => graph.nodes.some(isBackbone)\nconst hasWalks = (graph: Graph) =>\n hasBackbone(graph) && (graph.paths?.length ?? 0) > 1\nconst hasTubeMapBackbone = (graph: Graph) =>\n hasBackbone(graph) && hasTubeMapPaths(graph)\nconst hasAlleles = (graph: Graph) =>\n hasBackbone(graph) && graph.nodes.some(isOffReference)\n\n// `satisfies` rather than a `: LayoutMode[]` annotation: the annotation widened\n// every `value` to string, which collapsed LayoutModeValue to string and left the\n// persisted enum unable to state which values it accepts.\nexport const LAYOUT_MODES = [\n {\n value: 'auto',\n label: 'Anchored',\n description:\n 'x is reference bp, one row per stable rank. Needs rGFA tags or a reference path.',\n run: anchoredLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n drawsNodes: true,\n },\n {\n value: 'samplerows',\n label: 'Sample rows',\n description:\n 'x is reference bp, one row per contributing assembly. Needs rGFA tags or a reference path.',\n run: sampleRowLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasAlleles,\n drawsLocally: hasAlleles,\n drawsNodes: true,\n },\n {\n value: 'walkrows',\n label: 'Walk rows',\n description:\n \"x is each walk's own bp: one bar per haplotype, blue on the reference's path and purple off it, so a repeat expansion reads as bar length, and each bar boxes its haplotype's own genes where the session has them. Needs W or P lines.\",\n run: walkRowLayout,\n cutMargins: false,\n wholeWalks: true,\n available: hasWalks,\n drawsLocally: hasWalks,\n drawsNodes: false,\n },\n {\n value: 'ordered',\n label: 'Ordered',\n description:\n 'x is reference order, not bp: every node gets room, bubbles read as lenses. Needs rGFA tags or a reference path.',\n run: orderedLayout,\n cutMargins: false,\n wholeWalks: false,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n drawsNodes: true,\n },\n {\n value: 'tubemap',\n label: 'Tube map',\n description:\n 'sequenceTubeMap: every path a coloured tube through boxed nodes, columns in node order and node widths log-scaled. Needs P or W lines.',\n run: tubeMapLayout,\n cutMargins: false,\n wholeWalks: false,\n available: hasTubeMapPaths,\n drawsLocally: hasTubeMapPaths,\n drawsNodes: false,\n },\n {\n value: 'tubemapref',\n label: 'Tube map on reference',\n description:\n 'The tube map with each column at the reference bp it covers, so the tubes line up with the tracks around them. Needs P or W lines and a reference path.',\n run: tubeMapReferenceLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasTubeMapBackbone,\n drawsLocally: hasTubeMapBackbone,\n drawsNodes: false,\n },\n {\n value: 'force',\n label: FORCE_LAYOUT_LABEL,\n description: 'OGDF FMMM, via the external Bandage engine.',\n run: () => undefined,\n cutMargins: false,\n wholeWalks: false,\n available: () => true,\n drawsLocally: () => false,\n drawsNodes: true,\n },\n] as const satisfies readonly LayoutMode[]\n\nexport type LayoutModeValue = (typeof LAYOUT_MODES)[number]['value']\n\nexport const LAYOUT_MODE_VALUES = LAYOUT_MODES.map(m => m.value)\n\n// Whether the drawing a mode produces for a graph comes from the FMMM engine.\n// The settings that only the engine reads \u2014 layout quality, bubble spread \u2014\n// are shown against this, so a control that cannot do anything to the drawing\n// on screen says so instead of appearing to work.\nexport function modeUsesLayoutEngine(value: string, graph: Graph) {\n return !layoutModeByValue(value).drawsLocally(graph)\n}\n\nexport function layoutModeByValue(value: string) {\n // An unknown value can only come from a snapshot written by a build that had\n // a mode this one doesn't. It falls back to 'auto' rather than to the model's\n // own default of 'force', because 'auto' declines any graph it cannot draw and\n // hands off to force anyway, so this is the fallback that covers both.\n return LAYOUT_MODES.find(m => m.value === value) ?? LAYOUT_MODES[0]\n}\n", "import { SATURATED_PATH_COUNT } from './bubbleLine'\n\nimport type { MinigraphBubble } from './bubbleLine'\n\n// What kind of variation a bubble is, read off the numbers `gfatools bubble`\n// states: the reference interval it replaces, its shortest and longest route,\n// how many routes, and whether one of them inverts. The reference route is one\n// of the routes and its length is the interval, so the shortest and longest\n// against the interval say whether the alternatives add sequence, remove it,\n// or swap it. Whether it is a repeat is not in those numbers: a\n// presence/absence insertion carried by many haplotypes has the same shape as\n// an expanding array, so that comes from the session's repeat annotation.\nexport type BubbleKind =\n | 'snp'\n | 'substitution'\n | 'insertion'\n | 'deletion'\n | 'inversion'\n | 'repeat'\n | 'superbubble'\n | 'complex'\n\nexport interface BubbleClass {\n kind: BubbleKind\n label: string\n}\n\n// An annotated tandem repeat array over the cut (repeats/repeatFeatures).\n// Its refName is not compared: the arrays are read over the same window as\n// the bubbles, which spell the reference their own way (PanSN).\nexport interface RepeatSpan {\n start: number\n end: number\n name?: string\n}\n\n// How far past its reference length an array's alleles may run and still be\n// copies of it: KIV-2's 40 kb array carries 175 kb alleles\nconst REPEAT_GROWTH = 10\n\n// The array covering at least half the bubble's reference interval, or the\n// point of an insertion, and long enough to account for its alleles: a\n// microsatellite at an insertion point does not make the insertion a repeat.\nfunction coveringRepeat(b: MinigraphBubble, repeats: readonly RepeatSpan[]) {\n const span = b.end - b.start\n const growth = b.longestAlleleLength - b.shortestAlleleLength\n return repeats.find(\n r =>\n growth <= REPEAT_GROWTH * (r.end - r.start) &&\n (span === 0\n ? r.start <= b.start && b.start <= r.end\n : Math.min(b.end, r.end) - Math.max(b.start, r.start) >= span / 2),\n )\n}\n\nconst SUPERBUBBLE_SEGMENTS = 40\n\nexport function formatBp(bp: number) {\n if (bp >= 10_000) {\n return `${(bp / 1000).toFixed(0)} kb`\n }\n if (bp >= 1000) {\n return `${(bp / 1000).toFixed(1)} kb`\n }\n return `${bp} bp`\n}\n\nfunction formatBpRange(shortest: number, longest: number) {\n const low = formatBp(shortest)\n const high = formatBp(longest)\n const unit = high.slice(high.indexOf(' '))\n return low.endsWith(unit)\n ? `${low.slice(0, -unit.length)}\u2013${high}`\n : `${low}\u2013${high}`\n}\n\nfunction routes(count: number) {\n if (count >= SATURATED_PATH_COUNT) {\n return '\u22652.1B routes'\n }\n if (count >= 100_000) {\n return `${(count / 1000).toFixed(0)}k routes`\n }\n return `${count} routes`\n}\n\nexport function classifyBubble(\n b: MinigraphBubble,\n repeats: readonly RepeatSpan[] = [],\n): BubbleClass {\n const c = classifyShape(b, repeats)\n return b.partial ? { ...c, label: `${c.label}, partial` } : c\n}\n\nfunction classifyShape(\n b: MinigraphBubble,\n repeats: readonly RepeatSpan[],\n): BubbleClass {\n const refSpan = b.end - b.start\n const {\n shortestAlleleLength: shortest,\n longestAlleleLength: longest,\n pathCount,\n segmentCount,\n inversion,\n } = b\n const range = formatBpRange(shortest, longest)\n if (segmentCount >= SUPERBUBBLE_SEGMENTS) {\n return {\n kind: 'superbubble',\n label: `${range} superbubble, ${segmentCount} segments, ${routes(pathCount)}${inversion ? ', inv' : ''}`,\n }\n }\n if (inversion) {\n return { kind: 'inversion', label: `${formatBp(longest)} inv` }\n }\n // routes of one length are substitutions inside an array, not copies\n const repeat = longest !== shortest ? coveringRepeat(b, repeats) : undefined\n if (repeat) {\n const name = repeat.name ? ` (${repeat.name})` : ''\n return {\n kind: 'repeat',\n label: `${range} repeat array${name}, ${routes(pathCount)}`,\n }\n }\n const alleles = pathCount > 2 ? `, ${pathCount} alleles` : ''\n if (shortest === longest && shortest === refSpan) {\n return refSpan <= 1\n ? { kind: 'snp', label: 'SNP' }\n : { kind: 'substitution', label: `${formatBp(refSpan)} sub` }\n }\n if (refSpan === 0 || shortest === refSpan) {\n return {\n kind: 'insertion',\n label: `\u2264${formatBp(longest - refSpan)} ins${alleles}`,\n }\n }\n if (longest === refSpan) {\n return {\n kind: 'deletion',\n label: `${formatBp(refSpan - shortest)} del${alleles}`,\n }\n }\n return {\n kind: 'complex',\n label: `${formatBp(refSpan)} ref \u2192 ${range}, ${pathCount} alleles`,\n }\n}\n\n// One hue per kind, the deletion arc's near-black and the reference blue kept\n// clear of, so a glyph is not mistaken for either.\nexport const BUBBLE_KIND_COLORS: Record<BubbleKind, string> = {\n snp: '#5b6b7a',\n substitution: '#5b6b7a',\n insertion: '#2f8fd6',\n deletion: '#c94040',\n inversion: '#e07b00',\n repeat: '#8e3fbf',\n superbubble: '#8e3fbf',\n complex: '#8e3fbf',\n}\n\n// The kind as a short name, for a place that has no room for the label\nexport const BUBBLE_KIND_NAMES: Record<BubbleKind, string> = {\n snp: 'SNP',\n substitution: 'substitution',\n insertion: 'insertion',\n deletion: 'deletion',\n inversion: 'inversion',\n repeat: 'repeat array',\n superbubble: 'superbubble',\n complex: 'site',\n}\n\nexport function bubbleSegmentIds(b: MinigraphBubble) {\n return b.segments.split(',').filter(Boolean)\n}\n", "// Panels this far apart, each a title row over a drawing padded this much\nexport const FACET_GAP_PX = 8\nexport const FACET_TITLE_PX = 34\nexport const FACET_PAD_PX = 12\nconst MIN_FACET_PANEL_PX = 60\n\nexport interface FacetGrid {\n columns: number\n width: number\n height: number\n // the drawing's scale in each panel\n scale: number\n total: number\n}\n\n// How `count` panels of one drawing tile a pane `width` wide that may grow to\n// `room` tall. Unless `columns` says, as many go across as draws each panel\n// largest, as facet_wrap chooses its grid: four square drawings go two by\n// two in a tall pane and four across in a wide one, and a drawing far wider\n// than it is tall stacks. A row layout's rows are px, so its scale is x\n// alone and its panels stack. Each panel is as tall as its drawing needs.\nexport function facetGrid({\n count,\n bounds,\n pixelRows,\n width,\n room,\n columns,\n}: {\n count: number\n bounds: { w: number; h: number }\n pixelRows: boolean\n width: number\n room: number\n columns?: number\n}): FacetGrid {\n function tile(across: number): FacetGrid {\n const rows = Math.ceil(count / across)\n const panelWidth = Math.floor(\n (width - (across - 1) * FACET_GAP_PX) / across,\n )\n const panelRoom =\n (room - rows * FACET_TITLE_PX - (rows - 1) * FACET_GAP_PX) / rows\n const fitX = (panelWidth - 2 * FACET_PAD_PX) / bounds.w\n const fitY =\n bounds.h > 0 ? (panelRoom - 2 * FACET_PAD_PX) / bounds.h : Infinity\n const scale = pixelRows ? fitX : Math.min(fitX, fitY)\n const drawn = pixelRows ? bounds.h : bounds.h * scale\n const height = Math.floor(\n Math.max(\n MIN_FACET_PANEL_PX,\n Math.min(drawn + 2 * FACET_PAD_PX, panelRoom),\n ),\n )\n return {\n columns: across,\n width: panelWidth,\n height,\n scale,\n total: rows * (height + FACET_TITLE_PX) + (rows - 1) * FACET_GAP_PX,\n }\n }\n if (columns !== undefined) {\n return tile(Math.max(1, Math.min(count, Math.round(columns))))\n }\n let best = tile(1)\n for (let across = 2; across <= count; across++) {\n const grid = tile(across)\n if (grid.scale > best.scale) {\n best = grid\n }\n }\n return best\n}\n\nexport type FacetBy = 'walk' | 'sample'\n\n/**\n * The facet as a session or a figure spec writes it, in the shape every\n * JBrowse display's `facet` takes: the field the panels split on, `walk` for\n * a panel per lifted walk or `sample` for a row per sample and a column per\n * haplotype, the order the panels take, and how many go across under `walk`.\n */\nexport interface FacetSetting {\n field: '' | FacetBy\n /** The walks, or under `sample` the samples, whose panels come first, in order. */\n domain: string[]\n /** How many panels go across; unset takes whichever count draws each largest. */\n columns?: number\n}\n\n/** What a session or a spec may write for the facet: the field bare, or the setting's members. */\nexport type FacetInput =\n string | { field?: string; domain?: readonly string[]; columns?: number }\n\n/** A facet written bare is its field, and a field the pane cannot split on is none. */\nexport function facetSettingOf(facet: FacetInput | undefined): FacetSetting {\n const raw = typeof facet === 'string' ? { field: facet } : (facet ?? {})\n const field = raw.field === 'walk' || raw.field === 'sample' ? raw.field : ''\n return {\n field,\n domain: [...(raw.domain ?? [])],\n ...(raw.columns === undefined ? {} : { columns: raw.columns }),\n }\n}\n\n// The listed names first, in the domain's order, then the rest as they came\nfunction domainFirst(names: readonly string[], domain: readonly string[]) {\n const rank = new Map(domain.map((name, i) => [name, i]))\n const listed = (name: string) => rank.get(name) ?? domain.length\n return names\n .map((name, i) => ({ name, i }))\n .sort((a, b) => listed(a.name) - listed(b.name) || a.i - b.i)\n .map(({ name }) => name)\n}\n\n// Which grid cell each walk's panel takes, and how many go across when the\n// arrangement fixes it. By walk the panels wrap in reading order. By sample\n// each sample takes a row and each haplotype a column, as facet_grid(sample ~\n// haplotype) lays out a plot, so a sample's haplotypes read across its row and\n// a haploid reference sits in the first column. Names that are not\n// `sample#haplotype#contig`, or two walks landing in one cell, wrap instead.\n// A `domain` puts the walks, or the samples, it lists first.\nexport function facetCells(\n names: string[],\n by: FacetBy,\n domain: readonly string[] = [],\n) {\n const wrapOrder = domainFirst(names, domain)\n const wrap = {\n columns: undefined,\n count: names.length,\n cells: names.map(name => wrapOrder.indexOf(name)),\n }\n if (by === 'walk') {\n return wrap\n }\n const places = names.map(name => {\n const [sample, haplotype, contig] = name.split('#')\n const n = Number(haplotype)\n return contig !== undefined && Number.isInteger(n) && n >= 0\n ? { sample: sample!, column: Math.max(0, n - 1) }\n : undefined\n })\n const rows = domainFirst(\n [...new Set(places.flatMap(p => (p ? [p.sample] : [])))],\n domain,\n )\n const columns = Math.max(1, ...places.map(p => (p ? p.column + 1 : 1)))\n const cells = places.map(p =>\n p ? rows.indexOf(p.sample) * columns + p.column : -1,\n )\n return cells.includes(-1) || new Set(cells).size < cells.length\n ? wrap\n : { columns, count: rows.length * columns, cells }\n}\n", "// A drawing as a plain element tree, so the hosts render the same picture\n// their own way: the plugin as React elements, BandageJS as DOM nodes, a\n// figure as markup. Attribute names are SVG's own (`stroke-width`,\n// `text-anchor`); serializeEl is the one place text becomes markup.\n\nexport type ElAttrs = Record<string, string | number | undefined>\n\nexport interface El {\n tag: string\n attrs: ElAttrs\n children: (El | string)[]\n}\n\nexport function el(\n tag: string,\n attrs: ElAttrs = {},\n ...children: (El | string | undefined | false)[]\n): El {\n return {\n tag,\n attrs,\n children: children.filter((c): c is El | string => !!c || c === ''),\n }\n}\n\nfunction escapeText(s: string) {\n return s\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n}\n\nfunction escapeAttr(s: string) {\n return escapeText(s).replaceAll('\"', '&quot;')\n}\n\nexport function serializeEl(node: El | string): string {\n if (typeof node === 'string') {\n return escapeText(node)\n }\n const attrs = Object.entries(node.attrs)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => ` ${k}=\"${escapeAttr(String(v))}\"`)\n .join('')\n return `<${node.tag}${attrs}>${node.children.map(serializeEl).join('')}</${node.tag}>`\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { el } from '../el'\nimport { LABEL_CHAR_PX } from '../overlayLabels'\nimport { rampHueCss, rampStops } from '../referenceRampCss'\nimport { REFERENCE_RAMP_ALT_CSS } from '../renderer/GeometryBuilder'\n\nimport type { WalkAxis, WalkRow, WalkRows, WalkRun } from './walkRows'\nimport type { El } from '../el'\nimport type { GeneModel } from '../genes/genePins'\n\n// What walk rows draw, shared by the hosts: walkRowsTree is the drawing as an\n// element tree, which the plugin renders as React elements, BandageJS as DOM\n// nodes and the figure export as markup, so the bars, readouts, genes and key\n// agree everywhere.\n\nexport const ON_REFERENCE = '#2f8fd6'\nexport const OFF_REFERENCE = '#8e3fbf'\nexport const OUTSIDE_CUT = '#bdbdbd'\nexport const GENE_INK = '#1c1c22'\nexport const BAR_PX = 12\nexport const GAP_PX = 4\n// a unit separator is dropped when a unit is under this many px\nconst MIN_TILE_PX = 3\n\nexport function kb(bp: number) {\n return `${(bp / 1000).toFixed(bp < 10_000 ? 1 : 0)} kb`\n}\n\nfunction units(bp: number, unit: number | undefined) {\n return unit ? ` \u2248 ${Math.round(bp / unit)} units` : ''\n}\n\n// a repeat genotype's allele length for a walk, as the plugin's repeat\n// catalogue pairs one with a row\nexport interface ReadoutCall {\n bp: number\n spanningReads?: number\n // whether the walk's length and the called allele agree, where both are\n // measurements\n agrees?: boolean\n}\n\n// The text at the end of a bar: its length, what it carries against the\n// reference row, and what the cut left out\nexport function walkRowReadout(\n row: Pick<WalkRow, 'bp' | 'gapBp' | 'complete'>,\n reference: Pick<WalkRow, 'bp'> | undefined,\n unit?: number,\n call?: ReadoutCall,\n) {\n const { bp, gapBp, complete } = row\n if (!reference) {\n return `${kb(bp)}${units(bp, unit)}`\n }\n const delta = bp - reference.bp\n const against =\n delta === 0 ? '' : ` (${delta > 0 ? '+' : '\u2212'}${kb(Math.abs(delta))})`\n const outside = gapBp > 0 ? ` \u00B7 ${kb(gapBp)} outside the cut` : ''\n const called = call\n ? ` \u00B7 called ${kb(call.bp)}${call.spanningReads === 0 ? ' \u00B7 no spanning read' : ''}`\n : ''\n return `${kb(bp)}${units(bp, unit)}${against}${outside}${complete ? '' : ' \u00B7 partial walk'}${called}`\n}\n\n// A readout right of its bar's end, or inside it where it would leave the pane\nexport function readoutPlacement(text: string, endX: number, width: number) {\n const x = endX + 6\n const fits = x + text.length * LABEL_CHAR_PX < width\n return fits\n ? { x, anchor: 'start' as const, halo: false }\n : { x: endX - 6, anchor: 'end' as const, halo: true }\n}\n\n// Bar offsets one unit apart, so copies are countable, none where a unit is\n// under a few px\nexport function unitTicks(bp: number, unit: number, pxPerBp: number) {\n if (unit * pxPerBp < MIN_TILE_PX) {\n return []\n }\n const ticks: number[] = []\n for (let k = unit; k < bp; k += unit) {\n ticks.push(k)\n }\n return ticks\n}\n\n// A run's paint: a flat colour, or under the reference-position ramp the hues\n// a gradient along a shared run takes, since it covers its reference\n// contiguously\nexport function runPaint(\n run: WalkRun,\n ramp: { start: number; end: number } | undefined,\n): { fill: string } | { stops: string[] } {\n if (run.gap) {\n return { fill: OUTSIDE_CUT }\n }\n if (!ramp) {\n return { fill: run.onReference ? ON_REFERENCE : OFF_REFERENCE }\n }\n if (run.referenceStart === undefined) {\n return { fill: REFERENCE_RAMP_ALT_CSS }\n }\n const stops = rampStops({ ...run, start: run.referenceStart }, ramp)\n return stops.length === 1\n ? { fill: rampHueCss(stops[0]!) }\n : { stops: stops.map(rampHueCss) }\n}\n\n// A contig span as offsets along a row's bar\nexport function alongRow(axis: WalkAxis, start: number, end: number) {\n return axis.reversed\n ? { start: axis.start - end, end: axis.start - start }\n : { start: start - axis.start, end: end - axis.start }\n}\n\n// The contig span a row's bar covers\nexport function rowSpan(axis: WalkAxis, bp: number) {\n return axis.reversed\n ? { start: axis.start - bp, end: axis.start }\n : { start: axis.start, end: axis.start + bp }\n}\n\nexport interface RowGene {\n name: string\n start: number\n end: number\n exons: { start: number; end: number }[]\n}\n\n// Each row's genes as offsets along its bar, from genes the host read for that\n// row's own contig, cut to the bar\nexport function placeRowGenes(\n rows: WalkRow[],\n byRow: Map<string, GeneModel[]>,\n) {\n const placed = new Map<string, RowGene[]>()\n for (const row of rows) {\n const genes = byRow.get(row.name)\n if (!row.axis || !genes) {\n continue\n }\n const axis = row.axis\n const clip = ({ start, end }: { start: number; end: number }) => ({\n start: Math.max(0, start),\n end: Math.min(row.bp, end),\n })\n placed.set(\n row.name,\n genes\n .map(g => ({\n name: g.name,\n ...clip(alongRow(axis, g.start, g.end)),\n exons: g.exons\n .map(e => clip(alongRow(axis, e.start, e.end)))\n .filter(e => e.end > e.start),\n }))\n .filter(g => g.end > g.start)\n .sort((a, b) => a.start - b.start),\n )\n }\n return placed\n}\n\nconst GENE_FONT_PX = 9\nconst GENE_CHAR_PX = 5.2\n\nexport interface GeneBox {\n name: string\n x: number\n y: number\n w: number\n h: number\n exons: { x: number; w: number }[]\n // the name inside the box where it fits and lands on no other name, shorter\n // genes taking their room first\n label?: { x: number; y: number; size: number }\n}\n\n// A row's genes as boxes in screen px around its bar at `y`\nexport function rowGeneBoxes(\n genes: RowGene[],\n X: (offset: number) => number,\n y: number,\n): GeneBox[] {\n const boxes = genes.map(g => {\n const x = X(g.start)\n return {\n g,\n x,\n w: Math.max(2, X(g.end) - x),\n }\n })\n const taken: [number, number][] = []\n const named = new Set(\n [...boxes]\n .sort((a, b) => a.w - b.w)\n .filter(({ g, x, w }) => {\n const half = (g.name.length * GENE_CHAR_PX) / 2\n const at: [number, number] = [x + w / 2 - half, x + w / 2 + half]\n if (\n 2 * half > w + 6 ||\n taken.some(([a, b]) => at[0] < b + 2 && at[1] > a - 2)\n ) {\n return false\n }\n taken.push(at)\n return true\n }),\n )\n return boxes.map(box => ({\n name: box.g.name,\n x: box.x,\n y: y - BAR_PX / 2 - 2,\n w: box.w,\n h: BAR_PX + 4,\n exons: box.g.exons.map(e => ({\n x: X(e.start),\n w: Math.max(1, X(e.end) - X(e.start)),\n })),\n label: named.has(box)\n ? { x: box.x + box.w / 2, y: y + 3, size: GENE_FONT_PX }\n : undefined,\n }))\n}\n\nexport type KeySwatch =\n | { kind: 'bar'; fill: string }\n | { kind: 'gap'; fill: string }\n | { kind: 'gene' }\n\nexport interface KeyEntry {\n swatch: KeySwatch\n label: string\n // a line under the entries, saying what the key leaves out\n note?: true\n}\n\n// The rows a host could read no genes for\nexport interface GeneGaps {\n untracked: number\n // rows whose walk states no contig coordinates to read genes over\n unplaced?: number\n unread: number\n}\n\nconst rowsText = (n: number) => `${n} row${n === 1 ? '' : 's'}`\n\n// What the bar colours mean: whether a run is on the reference walk's path.\n// Off the path is an alternative route, no statement about the sequence: at a\n// duplication the graph may route a copy the reference carries through nodes\n// of its own.\nexport function walkRowsKey(\n bars: WalkRows,\n o: {\n ramp?: { start: number; end: number }\n rampCss?: string\n genes?: GeneGaps\n } = {},\n): KeyEntry[] {\n const entries: KeyEntry[] = [\n {\n swatch: {\n kind: 'bar',\n fill: o.ramp ? (o.rampCss ?? ON_REFERENCE) : ON_REFERENCE,\n },\n label: `on ${bars.reference.label}'s path`,\n },\n {\n swatch: {\n kind: 'bar',\n fill: o.ramp ? REFERENCE_RAMP_ALT_CSS : OFF_REFERENCE,\n },\n label: `off ${bars.reference.label}'s path`,\n },\n ]\n if ([bars.reference, ...bars.rows].some(row => row.gapBp > 0)) {\n entries.push({\n swatch: { kind: 'gap', fill: OUTSIDE_CUT },\n label: 'walked outside the cut',\n })\n }\n if (o.genes) {\n entries.push({\n swatch: { kind: 'gene' },\n label: \"genes, each row's own annotation\",\n })\n if (o.genes.untracked) {\n entries.push({\n swatch: { kind: 'gene' },\n label: `no gene track for ${rowsText(o.genes.untracked)}`,\n note: true,\n })\n }\n if (o.genes.unplaced) {\n entries.push({\n swatch: { kind: 'gene' },\n label: `no contig coordinates for ${rowsText(o.genes.unplaced)}`,\n note: true,\n })\n }\n if (o.genes.unread) {\n entries.push({\n swatch: { kind: 'gene' },\n label: `genes not read for the last ${rowsText(o.genes.unread)}`,\n note: true,\n })\n }\n }\n return entries\n}\n\n// walk rows whose rows may carry the allele a repeat genotype called\nexport type WalkRowsWithCalls = Omit<WalkRows, 'reference' | 'rows'> & {\n reference: WalkRow & { call?: ReadoutCall }\n rows: (WalkRow & { call?: ReadoutCall })[]\n}\n\nconst CALL_TICK = '#111'\nconst UNBACKED_TICK = '#9e9e9e'\nexport const DISAGREES = '#c62828'\n\n// The key at (x, y), one entry after another along a line, and about how\n// wide it is\nexport function walkRowsKeyTree(entries: KeyEntry[], x: number, y: number) {\n let at = 0\n const parts = entries.flatMap(e => {\n const swatch = e.note\n ? undefined\n : e.swatch.kind === 'gene'\n ? el('rect', {\n x: at,\n y: 5,\n width: 18,\n height: 8,\n fill: 'none',\n stroke: GENE_INK,\n 'stroke-width': 1.5,\n })\n : el('rect', {\n x: at,\n y: e.swatch.kind === 'gap' ? 7 : 5,\n width: 18,\n height: e.swatch.kind === 'gap' ? GAP_PX : 8,\n rx: 2,\n fill: e.swatch.fill,\n })\n const text = el(\n 'text',\n {\n x: e.note ? at : at + 23,\n y: 13,\n 'font-style': e.note ? 'italic' : undefined,\n fill: e.note ? '#666' : undefined,\n },\n e.label,\n )\n at += (e.note ? 0 : 23) + e.label.length * LABEL_CHAR_PX + 16\n return swatch ? [swatch, text] : [text]\n })\n return {\n width: at,\n tree: el(\n 'g',\n {\n transform: `translate(${x} ${y})`,\n 'font-family': 'Helvetica, Arial, sans-serif',\n 'font-size': 11,\n },\n ...parts,\n ),\n }\n}\n\nexport interface WalkRowsFrame {\n scaleX: number\n scaleY: number\n translateX: number\n translateY: number\n width: number\n height: number\n // the rows' pitch and bar height in px, ROW_HEIGHT_PX and BAR_PX unless a\n // dense strip shrinks them\n rowPx?: number\n barPx?: number\n // false where rows are too thin to letter, which leaves readouts to hover\n readouts?: boolean\n}\n\n// Runs under a pixel wide merged into one, painted as the bp most of them\n// carry: a base-level cut splits a bar at every SNP, which draws as noise and\n// costs an element per run. Gaps stay apart.\nexport function coalesceRuns(runs: WalkRun[], pxPerBp: number) {\n const minBp = 1 / pxPerBp\n const out: WalkRun[] = []\n let bucket: WalkRun[] = []\n const flush = () => {\n if (bucket.length === 0) {\n return\n }\n if (bucket.length === 1) {\n out.push(bucket[0]!)\n } else {\n let on = 0\n let off = 0\n for (const r of bucket) {\n if (r.onReference) {\n on += r.bp\n } else {\n off += r.bp\n }\n }\n const onReference = on >= off\n const lead = bucket.find(r => r.onReference === onReference)!\n out.push({\n start: bucket[0]!.start,\n bp: on + off,\n onReference,\n referenceStart: onReference ? lead.referenceStart : undefined,\n ...(lead.reversed ? { reversed: true as const } : {}),\n })\n }\n bucket = []\n }\n let held = 0\n for (const run of runs) {\n if (run.gap || run.bp >= minBp) {\n flush()\n held = 0\n out.push(run)\n continue\n }\n bucket.push(run)\n held += run.bp\n if (held >= minBp) {\n flush()\n held = 0\n }\n }\n flush()\n return out\n}\n\nfunction geneTree(box: GeneBox, y: number) {\n return el(\n 'g',\n { class: 'row-gene', 'data-testid': 'graph-walk-gene' },\n el('title', {}, box.name),\n ...box.exons.map(e =>\n el('rect', {\n x: e.x,\n y: y - BAR_PX / 2,\n width: e.w,\n height: BAR_PX,\n fill: GENE_INK,\n opacity: 0.35,\n }),\n ),\n el('rect', {\n x: box.x,\n y: box.y,\n width: box.w,\n height: box.h,\n fill: 'none',\n stroke: GENE_INK,\n 'stroke-width': 1.5,\n }),\n box.label &&\n el(\n 'text',\n {\n x: box.label.x,\n y: box.label.y,\n 'font-family': 'sans-serif',\n 'font-size': box.label.size,\n 'font-weight': 600,\n 'text-anchor': 'middle',\n fill: GENE_INK,\n stroke: 'white',\n 'stroke-width': 2.5,\n 'paint-order': 'stroke',\n },\n box.name,\n ),\n )\n}\n\n// Walk rows over a pane: each row's runs, unit separators, its genes, the\n// allele a repeat genotype called and its readout. `idPrefix` keeps gradient\n// ids apart where several panes share a document.\nexport function walkRowsTree(\n bars: WalkRowsWithCalls,\n frame: WalkRowsFrame,\n o: {\n ramp?: { start: number; end: number }\n rowGenes?: Map<string, RowGene[]>\n idPrefix?: string\n } = {},\n) {\n const X = (bp: number) => bp * frame.scaleX + frame.translateX\n const rowPx = frame.rowPx ?? ROW_HEIGHT_PX\n const barPx = frame.barPx ?? BAR_PX\n const Y = (row: number) => row * rowPx * frame.scaleY + frame.translateY\n const { origin, unit, reference, rows } = bars\n const along = (offset: number) => X(origin + offset)\n const prefix = o.idPrefix ?? 'walkrow'\n const rowTree = (\n row: WalkRow & { call?: ReadoutCall },\n i: number,\n ): El | undefined => {\n const y = Y(i)\n if (y < -barPx || y > frame.height + barPx) {\n return undefined\n }\n const runs = coalesceRuns(row.runs, frame.scaleX).flatMap(run => {\n const paint = runPaint(run, o.ramp)\n const h = run.gap ? Math.min(GAP_PX, barPx) : barPx\n const id = `${prefix}-${i}-${run.start}`\n const rect = el('rect', {\n x: along(run.start),\n y: y - h / 2,\n width: Math.max(1, run.bp * frame.scaleX),\n height: h,\n fill: 'stops' in paint ? `url(#${id})` : paint.fill,\n })\n return 'stops' in paint\n ? [\n el(\n 'linearGradient',\n { id },\n ...paint.stops.map((color, k) =>\n el('stop', {\n offset: k / (paint.stops.length - 1),\n 'stop-color': color,\n }),\n ),\n ),\n rect,\n ]\n : [rect]\n })\n const ticks = unit\n ? unitTicks(row.bp, unit, frame.scaleX).map(k =>\n el('line', {\n x1: along(k),\n x2: along(k),\n y1: y - barPx / 2,\n y2: y + barPx / 2,\n stroke: 'white',\n 'stroke-width': 1,\n }),\n )\n : []\n const { call } = row\n const text = walkRowReadout(\n row,\n i === 0 ? undefined : reference,\n unit,\n call,\n )\n const at = readoutPlacement(text, along(row.bp), frame.width)\n const readouts = frame.readouts ?? true\n return el(\n 'g',\n {\n 'data-testid': i === 0 ? 'graph-walk-reference' : 'graph-walk-row',\n },\n ...runs,\n ...ticks,\n ...rowGeneBoxes(o.rowGenes?.get(row.name) ?? [], along, y).map(box =>\n geneTree(box, y),\n ),\n call &&\n el('rect', {\n 'data-testid': 'graph-walk-call',\n x: along(call.bp) - 1,\n y: y - barPx / 2 - 3,\n width: 2,\n height: barPx + 6,\n fill: call.spanningReads === 0 ? UNBACKED_TICK : CALL_TICK,\n }),\n readouts &&\n el(\n 'text',\n {\n x: at.x,\n y: y + 4,\n 'font-family': 'sans-serif',\n 'font-size': 11,\n fill: call?.agrees === false ? DISAGREES : '#333',\n stroke: at.halo ? 'white' : undefined,\n 'stroke-width': at.halo ? 3 : undefined,\n 'paint-order': 'stroke',\n 'text-anchor': at.anchor,\n },\n text,\n ),\n )\n }\n return el('g', {}, ...[reference, ...rows].map(rowTree))\n}\n", "import {\n abgrToCssRgba,\n normalizedRgbToCssRgba,\n} from '@jbrowse/core/util/colorBits'\nimport { syncCanvasSize } from '@jbrowse/render-core/canvas2dUtils'\nimport { Canvas2DRenderingBackendBase } from '@jbrowse/render-core/renderingBackendBase'\n\nimport { brightenAbgr } from './colorBits'\n\nimport type {\n Arrowhead,\n RenderBatch,\n Renderer,\n TransformUniform,\n} from './types'\n\n// Everything is drawn as a handful of paths, one per distinct (colour, weight),\n// rather than one path per thing. A drawing is mostly runs of one colour, and\n// Canvas2D's cost is per path verb and per fill or stroke, not per pixel: the\n// same 15k-node graph took 414 ms a frame as one fill per mesh triangle and 11\n// ms as batched strokes (agent-docs/GRAPH_SCALE_AND_LOD.md). Highlighted things\n// are drawn last, in their own paths, so they sit on top of what they brighten.\n//\n// Extends the shared Canvas2D base rather than standing alone, which is how\n// `setErrorHandler` arrives \u2014 `useRenderingBackend` requires it. The base's\n// no-op implementation is the correct one here: Canvas2D allocates no GPU\n// resources, so there is no OOM channel to forward.\nexport class Canvas2DRenderer\n extends Canvas2DRenderingBackendBase\n implements Renderer\n{\n private transform: TransformUniform | null = null\n private batch: RenderBatch | null = null\n private nodeHighlights: ReadonlyMap<string, number> = new Map()\n private highlightedEdge: number | null = null\n private highlightFactor = 1\n\n // render-core's, not a local `width * devicePixelRatio`: it clamps the\n // backing store at MAX_CANVAS_DIM_PX, reads the ratio through `getDpr()` so\n // this agrees with the transform the model builds, and writes the css size\n // independently of the backing size.\n resize(width: number, height: number) {\n syncCanvasSize(this.ctx.canvas, width, height)\n }\n\n uploadGeometry(batch: RenderBatch) {\n this.batch = batch\n // A rebuild renumbers the strokes, so the old edge no longer addresses the\n // same run; the model re-applies the current hover against the new batch.\n this.highlightedEdge = null\n }\n\n setNodeHighlights(factors: ReadonlyMap<string, number>) {\n this.nodeHighlights = factors\n }\n\n setEdgeHighlight(edgeIndex: number | null, factor: number) {\n this.highlightedEdge = edgeIndex\n this.highlightFactor = factor\n }\n\n updateTransform(transform: TransformUniform) {\n this.transform = transform\n }\n\n render(clearColor: [number, number, number, number]) {\n const t = this.transform\n if (!t) {\n return\n }\n const ctx = this.ctx\n const { width, height } = ctx.canvas\n\n // a clear colour short of opaque leaves what is under the canvas showing\n if (clearColor[3] < 1) {\n ctx.clearRect(0, 0, width, height)\n }\n ctx.fillStyle = normalizedRgbToCssRgba(\n [clearColor[0], clearColor[1], clearColor[2]],\n clearColor[3],\n )\n ctx.fillRect(0, 0, width, height)\n\n const batch = this.batch\n if (!batch) {\n return\n }\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n this.renderEdges(batch)\n this.renderNodes(batch)\n this.renderArrows(batch)\n }\n\n private renderEdges(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n const hl =\n this.highlightedEdge === null\n ? undefined\n : batch.edgeCurveRuns.get(this.highlightedEdge)\n for (const { color, items } of groupByPaint(batch.edgeCurves, (e, i) => ({\n color: inRun(hl, i)\n ? brightenAbgr(e.color, this.highlightFactor)\n : e.color,\n weight: e.thickness,\n last: inRun(hl, i),\n }))) {\n ctx.strokeStyle = abgrToCssRgba(color)\n // thickness is the half-width in css px, so the stroke is twice it at\n // the device ratio the transform already carries\n ctx.lineWidth = items[0]!.thickness * 2 * t.dpr\n ctx.beginPath()\n for (const e of items) {\n const first = e.curves[0]!\n ctx.moveTo(\n first.x0 * t.scaleX + t.translateX,\n first.y0 * t.scaleY + t.translateY,\n )\n for (const c of e.curves) {\n ctx.bezierCurveTo(\n c.cx0 * t.scaleX + t.translateX,\n c.cy0 * t.scaleY + t.translateY,\n c.cx1 * t.scaleX + t.translateX,\n c.cy1 * t.scaleY + t.translateY,\n c.x1 * t.scaleX + t.translateX,\n c.y1 * t.scaleY + t.translateY,\n )\n }\n }\n ctx.stroke()\n }\n }\n\n private renderNodes(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n // stroke index -> brighten factor, for the nodes the model lifted\n const lifted = new Map<number, number>()\n for (const [nodeId, factor] of this.nodeHighlights) {\n const run = batch.nodeStrokeRuns.get(nodeId)\n if (run) {\n for (let i = run.start; i < run.start + run.count; i++) {\n lifted.set(i, factor)\n }\n }\n }\n for (const { color, items } of groupByPaint(batch.nodeStrokes, (s, i) => {\n const factor = lifted.get(i)\n return {\n color: factor === undefined ? s.color : brightenAbgr(s.color, factor),\n weight: s.thickness,\n last: factor !== undefined,\n }\n })) {\n ctx.strokeStyle = abgrToCssRgba(color)\n ctx.lineWidth = items[0]!.thickness * 2 * t.dpr\n ctx.beginPath()\n for (const s of items) {\n const p0 = s.points[0]!\n ctx.moveTo(\n p0.x * t.scaleX + t.translateX,\n p0.y * t.scaleY + t.translateY,\n )\n for (let i = 1, l = s.points.length; i < l; i++) {\n const p = s.points[i]!\n ctx.lineTo(\n p.x * t.scaleX + t.translateX,\n p.y * t.scaleY + t.translateY,\n )\n }\n }\n ctx.stroke()\n }\n }\n\n private renderArrows(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n const hl =\n this.highlightedEdge === null\n ? undefined\n : batch.arrowRuns.get(this.highlightedEdge)\n for (const { color, items } of groupByPaint(batch.arrows, (a, i) => ({\n color: inRun(hl, i)\n ? brightenAbgr(a.color, this.highlightFactor)\n : a.color,\n weight: 0,\n last: inRun(hl, i),\n }))) {\n ctx.fillStyle = abgrToCssRgba(color)\n ctx.beginPath()\n for (const a of items) {\n const [tip, left, notch, right] = arrowheadOutline(a, t)\n ctx.moveTo(tip.x, tip.y)\n ctx.lineTo(left.x, left.y)\n ctx.lineTo(notch.x, notch.y)\n ctx.lineTo(right.x, right.y)\n ctx.closePath()\n }\n ctx.fill()\n }\n }\n\n override dispose() {\n super.dispose()\n this.batch = null\n this.nodeHighlights = new Map()\n this.highlightedEdge = null\n }\n}\n\n// How far the back of the head is cut in toward the tip, as a fraction of its\n// length. The notch leaves the edge showing through the back of the head, so\n// the head reads as the end of the edge rather than a triangle laid over it.\nconst ARROW_NOTCH = 0.25\n\n// Tip, left barb, notch and right barb in backing-store px.\nexport function arrowheadOutline(a: Arrowhead, t: TransformUniform) {\n const ux = Math.cos(a.angle)\n const uy = Math.sin(a.angle)\n const tipX = a.x * t.scaleX + t.translateX\n const tipY = a.y * t.scaleY + t.translateY\n const length = a.length * t.dpr\n const halfWidth = a.halfWidth * t.dpr\n const backX = tipX - ux * length\n const backY = tipY - uy * length\n const notchBack = length * (1 - ARROW_NOTCH)\n return [\n { x: tipX, y: tipY },\n { x: backX - uy * halfWidth, y: backY + ux * halfWidth },\n { x: tipX - ux * notchBack, y: tipY - uy * notchBack },\n { x: backX + uy * halfWidth, y: backY - ux * halfWidth },\n ] as const\n}\n\nfunction inRun(run: { start: number; count: number } | undefined, i: number) {\n return run !== undefined && i >= run.start && i < run.start + run.count\n}\n\ninterface Paint {\n color: number\n weight: number\n // drawn after everything else, so a highlight lands on top of its neighbours\n last: boolean\n}\n\n// Items bucketed by what they are painted with, in first-seen order, with the\n// `last` ones after the rest.\nfunction groupByPaint<T>(\n items: T[],\n paintOf: (item: T, index: number) => Paint,\n) {\n const groups = new Map<string, { color: number; items: T[] }>()\n const deferred = new Map<string, { color: number; items: T[] }>()\n for (let i = 0, l = items.length; i < l; i++) {\n const item = items[i]!\n const { color, weight, last } = paintOf(item, i)\n const into = last ? deferred : groups\n const key = `${color}:${weight}`\n const group = into.get(key)\n if (group) {\n group.items.push(item)\n } else {\n into.set(key, { color, items: [item] })\n }\n }\n return [...groups.values(), ...deferred.values()]\n}\n", "import { packAbgr } from '@jbrowse/core/util/colorBits'\n\nimport {\n REFERENCE_RAMP_ALT_COLOR,\n REFERENCE_RAMP_MAX_HUE,\n hslToRgb,\n} from './renderer/GeometryBuilder'\n\n// How a lifted walk colours its lane, stated the way a grammar of graphics\n// states an encoding: a field, the quantity the walk has at each node it\n// visits, mapped through a scheme, the scale from that quantity to a colour.\n// Colour does one job at a time by default: a walk lifted alone shades light\n// to dark along itself, which follows it round a loop, and walks lifted\n// together each take one flat colour, which says which lane is which. The\n// reference takes grey, so no hue means anything but a haplotype.\n//\n// A session states it per walk, and leaves out whatever takes the default:\n//\n// \"walkLayers\": [\n// { \"walk\": \"GRCh38#0#chr6\" },\n// { \"walk\": \"HG00133#1#CM090050.1\", \"color\": { \"scheme\": \"purple\" } }\n// ]\n\nexport const WALK_FIELDS = [\n { value: 'progress', label: 'Progress along the walk' },\n { value: 'walk', label: 'One colour for the walk' },\n { value: 'reference', label: 'Reference position' },\n] as const\n\nexport type WalkField = (typeof WALK_FIELDS)[number]['value']\n\n// Each family runs from a light colour where the walk starts to a dark one of\n// another hue where it ends, so a walk reads in hue as well as lightness, and\n// no two families share a stretch of the colour wheel: one walk's gradient\n// never passes through another's colours. The reference takes grey to black, so\n// that no hue on a lane means anything but a haplotype. The rainbow is the\n// reference-position ramp, so it goes with that field only.\n//\n// A dichromat sees one axis, blue to yellow, so cyan to navy and yellow to red\n// stay apart for every reader and come first; pink and lime fold into them.\nexport const WALK_SCHEMES = [\n { value: 'grey', label: 'Grey to black', ends: ['#a3a3a3', '#151515'] },\n { value: 'blue', label: 'Cyan to navy', ends: ['#6ee0f0', '#0b2c7a'] },\n { value: 'red', label: 'Yellow to red', ends: ['#ffd24a', '#b3121b'] },\n { value: 'purple', label: 'Pink to plum', ends: ['#f5a3d8', '#5e1a6e'] },\n { value: 'green', label: 'Lime to forest', ends: ['#b6e36c', '#0b5d3b'] },\n { value: 'rainbow', label: 'Rainbow' },\n] as const\n\nexport type WalkScheme = (typeof WALK_SCHEMES)[number]['value']\n\nexport interface WalkEncoding {\n field: WalkField\n scheme: WalkScheme\n}\n\nexport interface WalkLayer {\n walk: string\n color?: Partial<WalkEncoding>\n}\n\n// the families lifted haplotypes take by default, in the order they were picked\nconst FAMILIES: WalkScheme[] = ['blue', 'red', 'purple', 'green']\n\nconst isField = (v: unknown): v is WalkField =>\n WALK_FIELDS.some(f => f.value === v)\nconst isScheme = (v: unknown): v is WalkScheme =>\n WALK_SCHEMES.some(s => s.value === v)\n\n// A stated field or scheme this build does not have reads as unset\nexport function resolveEncoding(\n layer: WalkLayer,\n reference: boolean,\n picked: number,\n alone = true,\n): WalkEncoding {\n const { field: askedField, scheme: askedScheme } = layer.color ?? {}\n const field = isField(askedField) ? askedField : alone ? 'progress' : 'walk'\n const family = reference ? 'grey' : FAMILIES[picked % FAMILIES.length]!\n const scheme = isScheme(askedScheme)\n ? askedScheme === 'rainbow' && field !== 'reference'\n ? family\n : askedScheme\n : family\n return { field, scheme }\n}\n\n// Where a flat lane sits on its family: halfway between its ends\nconst BASE_T = 0.5\n\nconst toLinear = (c: number) =>\n c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4\nconst toGamma = (c: number) =>\n c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055\n\n// sRGB hex to OKLab, where a straight line between two colours changes\n// lightness evenly, so a family's steps read alike from end to end\nfunction oklab(hex: string) {\n const [r, g, b] = [1, 3, 5].map(i =>\n toLinear(Number.parseInt(hex.slice(i, i + 2), 16) / 255),\n ) as [number, number, number]\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ]\n}\n\nfunction oklabToRgb([L, a, b]: number[]) {\n const l = (L! + 0.3963377774 * a! + 0.2158037573 * b!) ** 3\n const m = (L! - 0.1055613458 * a! - 0.0638541728 * b!) ** 3\n const s = (L! - 0.0894841775 * a! - 1.291485548 * b!) ** 3\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ].map(c => toGamma(Math.max(0, Math.min(1, c))) * 255)\n}\n\n// A scheme at t in [0, 1] as rgb 0-255\nexport function schemeRgb(scheme: WalkScheme, t: number) {\n const x = Math.max(0, Math.min(1, t))\n const family = WALK_SCHEMES.find(s => s.value === scheme)\n if (!family || !('ends' in family)) {\n return hslToRgb(x * REFERENCE_RAMP_MAX_HUE, 0.7, 0.5).map(c => c * 255)\n }\n const from = oklab(family.ends[0])\n const to = oklab(family.ends[1])\n return oklabToRgb(from.map((v, i) => v + (to[i]! - v) * x))\n}\n\nexport function schemeCss(scheme: WalkScheme, t: number) {\n const [r, g, b] = schemeRgb(scheme, t).map(Math.round)\n return `rgb(${r}, ${g}, ${b})`\n}\n\nexport function schemeColor(scheme: WalkScheme, t: number) {\n const [r, g, b] = schemeRgb(scheme, t).map(Math.round)\n return packAbgr(r!, g!, b!, 255)\n}\n\n// A field's value at a node through the walk's scheme; `t` undefined is a node\n// the field has nothing to say about, off the reference for `reference`\nexport function encodedColor({ field, scheme }: WalkEncoding, t?: number) {\n return field === 'walk'\n ? schemeColor(scheme, BASE_T)\n : t === undefined\n ? NO_VALUE_COLOR\n : schemeColor(scheme, t)\n}\n\nexport const NO_VALUE_COLOR = REFERENCE_RAMP_ALT_COLOR\n\n// A key's colours: the walk's one colour, or the scheme across the field\nexport function encodingStops({ field, scheme }: WalkEncoding) {\n return field === 'walk'\n ? [{ offset: 0, color: schemeCss(scheme, BASE_T) }]\n : Array.from({ length: 7 }, (_, i) => ({\n offset: i / 6,\n color: schemeCss(scheme, i / 6),\n }))\n}\n\n// A swatch for the legend: the walk's colour, or the scheme across the field\nexport function encodingSwatchCss(encoding: WalkEncoding) {\n const stops = encodingStops(encoding)\n return stops.length === 1\n ? stops[0]!.color\n : `linear-gradient(to right, ${stops.map(s => `${s.color} ${s.offset * 100}%`).join(', ')})`\n}\n", "import { formatBp, groupDigits } from './graphLabels'\n\nimport type { LiftedWalk } from './walkHighlight'\n\n// `contig:start-end (length)`\nexport function rangeText(\n contig: string | undefined,\n start: number,\n end: number,\n) {\n const s = Math.round(start)\n const e = Math.round(end)\n return `${contig ? `${contig}:` : ''}${groupDigits(s)}-${groupDigits(e)} (${formatBp(e - s)})`\n}\n\n// A lifted walk's key in words, for a legend row or a facet panel's title: its\n// length against the reference and the bp it runs reversed, and for a lane that\n// shades, the stretch its scale runs over as `contig:start-end (length)`, to\n// write under a short bar of that scale. A flat lane's stretch is `hover`.\nexport function walkKey(\n walk: LiftedWalk,\n reference?: { name?: string; start: number; end: number },\n) {\n const { field } = walk.encoding\n const own = walk.range\n ? rangeText(walk.range.contig, walk.range.start, walk.range.end)\n : undefined\n const scale =\n field === 'progress'\n ? own\n : field === 'reference' && reference\n ? rangeText(reference.name, reference.start, reference.end)\n : undefined\n // what the walk spans on its contig, which its pieces may not all show\n const spanned = walk.range ? walk.range.end - walk.range.start : walk.bp\n const outside = spanned - walk.bp\n return {\n delta:\n walk.referenceBp === undefined || spanned === walk.referenceBp\n ? ''\n : ` ${spanned > walk.referenceBp ? '+' : '\u2212'}${formatBp(Math.abs(spanned - walk.referenceBp))}`,\n outside: outside > 0 ? `, ${formatBp(outside)} outside the cut` : '',\n reversed:\n walk.reversedBp > 0 ? `, ${formatBp(walk.reversedBp)} reversed` : '',\n shades: field !== 'walk',\n scale,\n hover: scale === own ? undefined : own,\n }\n}\n\n// Where a node sits on a walk's own contig, from how far along the walk it\n// is: the stretch the walk's records give it at its first visit\nexport function walkPosition(walk: LiftedWalk, nodeId: string, length: number) {\n const progress = walk.progress.get(nodeId)\n const range = walk.range\n if (progress === undefined || !range) {\n return undefined\n }\n const start = Math.round(\n range.start + progress * (range.end - range.start) - length / 2,\n )\n return `${range.contig}:${groupDigits(start)}-${groupDigits(start + length)}`\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { alongRow } from './walkRowDraw'\nimport { el } from '../el'\nimport { pathOrigin } from '../pathAnchoring'\n\nimport type { WalkRowsFrame } from './walkRowDraw'\nimport type { WalkRow, WalkRows } from './walkRows'\nimport type { Graph, GraphNode } from '../types'\n\n// Walk rows as a strip under a node layout, linked to it: a node's visits as\n// ticks on each bar, and the node under a point on a bar. Rows are small\n// multiples of one mapping, x the walk's own bp and colour whether it runs on\n// the reference's path, so the strip shows every row and sizes them to fit,\n// down to a dense overview, rather than asking which to show.\n\nconst PAD_PX = 6\nconst MIN_ROW_PX = 3\n// rows thinner than this go unlabelled, their names and readouts on hover\nexport const LABELLED_ROW_PX = 12\nconst READOUT_ROOM = 1.3\nconst MARK_INK = '#111'\nconst TICK_PX = 3\n\nexport interface StripMark {\n row: string\n start: number\n bp: number\n}\n\n// Where each row's walk passes `node`, as offsets along its bar: once per\n// visit, so a walk through a collapsed repeat gets a tick per pass\nexport function stripMarks(graph: Graph, rows: WalkRow[], node: GraphNode) {\n const visits = graph.pathVisits?.get(node.name) ?? []\n const marks: StripMark[] = []\n for (const row of rows) {\n const axis = row.axis\n if (!axis) {\n continue\n }\n const path = pathOrigin(row.name).name\n for (const v of visits) {\n if (v.path !== path) {\n continue\n }\n const { start, end } = alongRow(axis, v.start, v.start + node.length)\n if (end > 0 && start < row.bp) {\n marks.push({ row: row.name, start, bp: end - start })\n }\n }\n }\n return marks\n}\n\ninterface Spans {\n ids: string[]\n starts: number[]\n ends: number[]\n}\n\nconst spanCache = new WeakMap<Graph, Map<string, Spans | undefined>>()\n\n// A walk's node steps at their contig positions, in contig order; undefined\n// where a piece states no start\nfunction spansOf(graph: Graph, name: string) {\n let byName = spanCache.get(graph)\n if (!byName) {\n byName = new Map()\n spanCache.set(graph, byName)\n }\n if (byName.has(name)) {\n return byName.get(name)\n }\n const lengthOf = new Map(graph.nodes.map(n => [n.id, n.length]))\n const pieces = (graph.paths ?? []).filter(p => p.name === name)\n let spans: Spans | undefined\n if (pieces.every(p => p.start !== undefined)) {\n spans = { ids: [], starts: [], ends: [] }\n for (const piece of [...pieces].sort((a, b) => a.start! - b.start!)) {\n let pos = piece.start!\n for (const id of piece.nodeIds) {\n const len = lengthOf.get(id) ?? 0\n spans.ids.push(id)\n spans.starts.push(pos)\n spans.ends.push(pos + len)\n pos += len\n }\n }\n }\n byName.set(name, spans)\n return spans\n}\n\n// The node under a point on a row's bar, `offset` bp from its left end: of the\n// nodes within half a pixel either side, the longest, so a pointer at 100 bp a\n// pixel still lands on something that draws\nexport function segmentAt(\n graph: Graph,\n row: WalkRow,\n offset: number,\n pxPerBp: number,\n) {\n const axis = row.axis\n const spans = axis && spansOf(graph, row.name)\n if (!axis || !spans || offset < 0 || offset > row.bp) {\n return undefined\n }\n const at = axis.reversed ? axis.start - offset : axis.start + offset\n const tol = 0.5 / pxPerBp\n const bar = axis.reversed\n ? { start: axis.start - row.bp, end: axis.start }\n : { start: axis.start, end: axis.start + row.bp }\n const from = Math.max(at - tol, bar.start)\n const to = Math.min(at + tol, bar.end)\n let lo = 0\n let hi = spans.starts.length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (spans.ends[mid]! <= from) {\n lo = mid + 1\n } else {\n hi = mid\n }\n }\n let best: string | undefined\n let bestLen = -1\n let bestHolds = false\n for (let i = lo; i < spans.starts.length && spans.starts[i]! < to; i++) {\n const len = spans.ends[i]! - spans.starts[i]!\n const holds = spans.starts[i]! <= at && at < spans.ends[i]!\n if (len > bestLen || (len === bestLen && holds && !bestHolds)) {\n bestLen = len\n best = spans.ids[i]\n bestHolds = holds\n }\n }\n return best\n}\n\nexport interface StripFrame extends WalkRowsFrame {\n rowPx: number\n barPx: number\n readouts: boolean\n // whether rows are tall enough to carry their labels\n labelled: boolean\n}\n\n// The strip's frame: every row, the reference's first, at the pitch that fits\n// `maxHeight`, from the walk-rows layout's own down to MIN_ROW_PX, and x fitted\n// so the longest bar and its readout fill the width past the label column\nexport function walkStripFrame(\n bars: WalkRows,\n o: { width: number; maxHeight: number; labelPx: number },\n): StripFrame {\n const count = bars.rows.length + 1\n const rowPx = Math.max(\n MIN_ROW_PX,\n Math.min(ROW_HEIGHT_PX, Math.floor((o.maxHeight - 2 * PAD_PX) / count)),\n )\n const labelled = rowPx >= LABELLED_ROW_PX\n const readouts = rowPx >= 14\n const barPx = Math.max(2, Math.round(rowPx * 0.6))\n const longest = Math.max(bars.reference.bp, ...bars.rows.map(r => r.bp), 1)\n const left = labelled ? o.labelPx : PAD_PX\n const usable = Math.max(1, o.width - left - PAD_PX)\n const scaleX = usable / (longest * (readouts ? READOUT_ROOM : 1))\n return {\n scaleX,\n scaleY: 1,\n translateX: left - bars.origin * scaleX,\n translateY: PAD_PX + rowPx / 2,\n width: o.width,\n height: count * rowPx + 2 * PAD_PX,\n rowPx,\n barPx,\n readouts,\n labelled,\n }\n}\n\n// The row under a pane point of the strip, as its index among the reference\n// row and the rows below it, and its bar offset, where the point is on its bar\nexport function stripRowAt(\n bars: WalkRows,\n frame: StripFrame,\n x: number,\n y: number,\n) {\n const i = Math.round((y - frame.translateY) / frame.rowPx)\n const row = [bars.reference, ...bars.rows][i]\n const offset = (x - frame.translateX) / frame.scaleX - bars.origin\n return row && i >= 0 && offset >= 0 && offset <= row.bp\n ? { index: i, row, offset }\n : undefined\n}\n\n// A node's visits as ticks above and below each bar, leaving its colour seen\nexport function walkMarksTree(\n bars: WalkRows,\n frame: StripFrame,\n marks: StripMark[],\n) {\n const index = new Map(\n [bars.reference, ...bars.rows].map((row, i) => [row.name, i]),\n )\n const X = (offset: number) =>\n (bars.origin + offset) * frame.scaleX + frame.translateX\n return el(\n 'g',\n { 'data-testid': 'graph-walk-marks' },\n ...marks.flatMap(m => {\n const i = index.get(m.row)\n if (i === undefined) {\n return []\n }\n const y = i * frame.rowPx + frame.translateY\n const x = X(m.start)\n const w = Math.max(2, m.bp * frame.scaleX)\n const tick = (top: number) =>\n el('rect', { x, y: top, width: w, height: TICK_PX, fill: MARK_INK })\n return [\n tick(y - frame.barPx / 2 - TICK_PX - 1),\n tick(y + frame.barPx / 2 + 1),\n ]\n }),\n )\n}\n", "import { abgrToCssRgba } from '@jbrowse/core/util/colorBits'\n\nimport { isBackbone } from './anchoredNodes'\nimport {\n LIFT_BACKDROP_CSS,\n computeColorSchemeRange,\n getNodeColor,\n} from './renderer/GeometryBuilder'\n\nimport type { ResolvedColorScheme } from './colorSchemes'\nimport type { ReferenceRamp } from './renderer/GeometryBuilder'\nimport type { Graph, NodeSegment } from './types'\n\n// A force or ordered drawing inside a linear view has no bp axis, so a strip\n// along the top of the track draws each reference segment at its bp there, in\n// the colour its node has in the graph below: the reference-position ramp's\n// hue, or while walks are lifted a row per walk in its lane's colours, pale\n// where that walk skips the segment. The strip leaves the linear view's\n// gridlines showing, so each block reads as a feature at its bp, and while the\n// blocks on screen are wide enough to read one by one, touching ones alternate\n// between two tiers, as a feature track stacks features that would touch. A\n// base-level cut splits the backbone at every SNP, and tiers there would be a\n// barcode that reads as data, so it draws as one band. The lit node gets a leader from its span on the strip\n// to where the graph drew it, as the variant matrix ties a column to its\n// variant.\n\nexport const REFERENCE_STRIP_PX = 12\n// the strip, and the gap under it the fit leaves the drawing\nexport const REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8\n// a triangle at a strip end, in a cap cleared of the strip: reference the\n// graph draws runs past that edge\nconst OVERHANG_PX = 8\nconst OVERHANG_CAP_PX = OVERHANG_PX + 3\n// the average width, in css px, the blocks on screen need to be staggered\nconst MIN_STAGGER_PX = 12\n\nexport interface StripBlock {\n node: string\n bp0: number\n bp1: number\n // top to bottom: a row per lifted walk, else the node's own colour\n colors: string[]\n // pale on some row\n faded: boolean\n}\n\nexport function referenceStripBlocks(\n graph: Graph,\n {\n colorScheme,\n referenceRamp,\n walks,\n }: {\n colorScheme: ResolvedColorScheme\n referenceRamp?: ReferenceRamp\n // the lifted walks' lane colours at the nodes each visits\n walks?: readonly { colors: ReadonlyMap<string, number> }[]\n },\n) {\n const range = { ...computeColorSchemeRange(graph), referenceRamp }\n const out: StripBlock[] = []\n graph.nodes.forEach((node, index) => {\n if (isBackbone(node)) {\n const colors = walks\n ? walks.map(w => {\n const lane = w.colors.get(node.id)\n return lane === undefined ? LIFT_BACKDROP_CSS : abgrToCssRgba(lane)\n })\n : [abgrToCssRgba(getNodeColor(node, index, colorScheme, range))]\n out.push({\n node: node.id,\n bp0: node.stable.start,\n bp1: node.stable.start + node.length,\n colors,\n faded: colors.includes(LIFT_BACKDROP_CSS),\n })\n }\n })\n return out.sort((a, b) => a.bp0 - b.bp0)\n}\n\n// bp to screen x, as the linear view places it; a reversed block's is negative\nexport interface BpFrame {\n scale: number\n translateX: number\n}\n\nfunction span(frame: BpFrame, bp0: number, bp1: number): [number, number] {\n const a = bp0 * frame.scale + frame.translateX\n const b = bp1 * frame.scale + frame.translateX\n return a < b ? [a, b] : [b, a]\n}\n\n// A SNP's block is a sliver, so the pointer gets a few px either side of one\nconst HIT_SLOP_PX = 2\n\n// How much backbone the graph draws past each edge of the window, in bp\nexport function stripOverhang(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n width: number,\n) {\n const a = -frame.translateX / frame.scale\n const b = (width - frame.translateX) / frame.scale\n const lo = Math.min(a, b)\n const hi = Math.max(a, b)\n let below = 0\n let above = 0\n for (const block of blocks) {\n below += Math.max(0, Math.min(block.bp1, lo) - block.bp0)\n above += Math.max(0, block.bp1 - Math.max(block.bp0, hi))\n }\n // a reversed window puts low bp on the right\n return frame.scale >= 0\n ? { left: Math.round(below), right: Math.round(above) }\n : { left: Math.round(above), right: Math.round(below) }\n}\n\nexport function stripBlockAt(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n sx: number,\n sy: number,\n) {\n if (sy < 0 || sy > REFERENCE_STRIP_PX + HIT_SLOP_PX) {\n return undefined\n }\n let hit: string | undefined\n let best = HIT_SLOP_PX\n for (const b of blocks) {\n const [x0, x1] = span(frame, b.bp0, b.bp1)\n const dist = Math.max(0, x0 - sx, sx - x1)\n if (dist <= best) {\n best = dist\n hit = b.node\n }\n }\n return hit\n}\n\n// Where the graph drew a node, in screen px: the middle of its polyline\nexport function nodeAnchor(\n segments: readonly NodeSegment[] | undefined,\n toScreen: (p: NodeSegment) => { x: number; y: number },\n) {\n const p = segments?.[Math.floor(segments.length / 2)]\n return p ? toScreen(p) : undefined\n}\n\nexport interface StripLit {\n start: number\n end: number\n anchor?: { x: number; y: number }\n}\n\n// Each block in whole device pixels, left to right, none sharing a pixel.\n// Fractional edges antialias: two opaque neighbours leave a pale seam between\n// them and two faded ones a dark seam where they overlap, stripes that read\n// as data. A block narrower than a pixel gets its pixel unless a block to its\n// left already has it.\nexport interface StripPixel {\n colors: string[]\n x0: number\n x1: number\n // the tier a staggered strip draws the block in\n tier?: number\n}\n\nexport function stripPixels(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n width: number,\n dpr: number,\n): StripPixel[] {\n const placed = blocks\n .map(b => {\n const [x0, x1] = span(frame, b.bp0, b.bp1)\n const px0 = Math.round(x0 * dpr)\n return {\n colors: b.colors,\n x0,\n px0,\n px1: Math.max(Math.round(x1 * dpr), px0 + 1),\n }\n })\n .filter(b => b.px1 > 0 && b.px0 < width * dpr)\n .sort((a, b) => a.x0 - b.x0)\n const out: StripPixel[] = []\n let claimed = -Infinity\n // where each tier's last block ends, in device px\n const tierEnds = [-Infinity, -Infinity]\n for (const { colors, px0, px1 } of placed) {\n const start = Math.max(px0, claimed)\n if (px1 > start) {\n const tier = tierEnds[0]! < start ? 0 : 1\n tierEnds[tier] = px1\n out.push({ colors, x0: start / dpr, x1: px1 / dpr, tier })\n claimed = px1\n }\n }\n const covered = out.length > 0 ? out.at(-1)!.x1 - out[0]!.x0 : 0\n return covered >= out.length * MIN_STAGGER_PX\n ? out\n : out.map(({ tier: _, ...b }) => b)\n}\n\n// A triangle the strip's height with its tip at the strip's edge, pointing\n// off it, in a cap cleared of the strip; `inward` is +1 from the left edge and\n// -1 from the right\nfunction drawOverhang(\n ctx: CanvasRenderingContext2D,\n edgeX: number,\n inward: number,\n ink: string,\n) {\n const capX = inward > 0 ? edgeX : edgeX - OVERHANG_CAP_PX\n ctx.clearRect(capX, 0, OVERHANG_CAP_PX, REFERENCE_STRIP_PX)\n const tipX = edgeX + inward\n const baseX = tipX + inward * OVERHANG_PX\n ctx.beginPath()\n ctx.moveTo(tipX, REFERENCE_STRIP_PX / 2)\n ctx.lineTo(baseX, 0)\n ctx.lineTo(baseX, REFERENCE_STRIP_PX)\n ctx.closePath()\n ctx.fillStyle = ink\n ctx.fill()\n}\n\n// Rows split the strip's height at whole device pixels, as blocks split its\n// width\nfunction rowEdge(row: number, rows: number, dpr: number) {\n return Math.round((row * REFERENCE_STRIP_PX * dpr) / rows) / dpr\n}\n\nexport function drawReferenceStrip(\n ctx: CanvasRenderingContext2D,\n blocks: readonly StripBlock[],\n frame: BpFrame,\n {\n width,\n lit,\n darkMode,\n dpr = 1,\n }: {\n width: number\n lit?: StripLit\n darkMode?: boolean\n dpr?: number\n },\n) {\n const ink = darkMode ? '#ffffff' : '#18181c'\n ctx.clearRect(0, 0, width, REFERENCE_STRIP_ZONE_PX)\n for (const b of stripPixels(blocks, frame, width, dpr)) {\n // a row per lifted walk, else the block's tier, else the whole strip\n const { tier } = b\n const staggered = tier !== undefined && b.colors.length === 1\n const rows = staggered ? 2 : b.colors.length\n b.colors.forEach((color, i) => {\n const row = staggered ? tier : i\n const y0 = rowEdge(row, rows, dpr)\n ctx.fillStyle = color\n ctx.fillRect(b.x0, y0, b.x1 - b.x0, rowEdge(row + 1, rows, dpr) - y0)\n })\n }\n const overhang = stripOverhang(blocks, frame, width)\n if (overhang.left > 0) {\n drawOverhang(ctx, 0, 1, ink)\n }\n if (overhang.right > 0) {\n drawOverhang(ctx, width, -1, ink)\n }\n if (lit) {\n const [x0, x1] = span(frame, lit.start, lit.end)\n const w = Math.max(x1 - x0, 3)\n const left = (x0 + x1) / 2 - w / 2\n ctx.strokeStyle = ink\n ctx.lineWidth = 1.5\n ctx.strokeRect(left, 0.75, w, REFERENCE_STRIP_PX - 1.5)\n if (lit.anchor) {\n ctx.beginPath()\n ctx.moveTo(left + w / 2, REFERENCE_STRIP_PX)\n ctx.lineTo(lit.anchor.x, lit.anchor.y)\n ctx.lineWidth = 1\n ctx.stroke()\n }\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { formatBp } from '../graphLabels'\n\nimport type { TubeMapFrame } from './draw'\nimport type { Graph } from '../types'\nimport type { TubeMapLayout } from '@gmod/tubemap-core'\n\n// The reference's boxes carry bp, so a reference coordinate maps into tube x\n// through the box that holds it. On the own axis a box is log-scaled, so bp\n// per tube px varies box to box; on the reference axis frame.ts warps the\n// result back onto the linear view's bp.\n\nexport interface Box {\n bp0: number\n bp1: number\n x0: number\n x1: number\n}\n\n// keyed by the graph's refName, `GRCh38#0#chr6`\nexport function referenceBoxes(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const byRefName = new Map<string, Box[]>()\n // sparse: an unreached node has no entry\n layout.nodes.forEach(node => {\n const graphNode = nodeById.get(node.name)\n if (node.order >= 0 && graphNode && isBackbone(graphNode)) {\n const { refName, start } = graphNode.stable\n const boxes =\n byRefName.get(refName) ?? byRefName.set(refName, []).get(refName)!\n boxes.push({\n bp0: start,\n bp1: start + node.sequenceLength,\n x0: node.x,\n x1: node.x + node.pixelWidth,\n })\n }\n })\n for (const boxes of byRefName.values()) {\n boxes.sort((a, b) => a.bp0 - b.bp0)\n }\n return byRefName\n}\n\nexport type ReferenceBoxes = ReturnType<typeof referenceBoxes>\n\n// A bp the cut has no box for (a gap in the backbone) takes the next box's\n// left edge. A bp on a boundary between boxes belongs to the box after it, or\n// to the one before for the end of a span, so a span stays off the curves\n// between.\nexport function tubeX(boxes: readonly Box[], bp: number, isEnd = false) {\n let lo = 0\n let hi = boxes.length - 1\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n const bp1 = boxes[mid]!.bp1\n if (isEnd ? bp1 < bp : bp1 <= bp) {\n lo = mid + 1\n } else {\n hi = mid\n }\n }\n const box = boxes[lo]!\n if (bp <= box.bp0) {\n return box.x0\n }\n const span = box.bp1 - box.bp0\n return span > 0\n ? box.x0 + ((Math.min(bp, box.bp1) - box.bp0) / span) * (box.x1 - box.x0)\n : box.x0\n}\n\nexport function tubeSpan(boxes: readonly Box[], start: number, end: number) {\n const a = tubeX(boxes, start)\n const b = tubeX(boxes, end, true)\n return { x0: Math.min(a, b), x1: Math.max(a, b) }\n}\n\n// The refName most of the cut's reference lies on, for the ruler\nexport function rulerBoxes(byRefName: ReferenceBoxes) {\n let best: Box[] | undefined\n for (const boxes of byRefName.values()) {\n if (!best || boxes.length > best.length) {\n best = boxes\n }\n }\n return best\n}\n\nconst TICK_PX = 5\nconst LABEL_GAP_PX = 10\nconst TARGET_TICK_PX = 110\n// narrower than this a box draws as one tick rather than a bracket\nconst MIN_BRACKET_PX = 3\n\nexport function rulerInk(darkMode?: boolean) {\n return darkMode ? '#b0b0b8' : '#55555c'\n}\n\ninterface Label {\n text: string\n at: number\n width: number\n}\n\nfunction niceStep(raw: number) {\n const pow = 10 ** Math.floor(Math.log10(raw))\n const m = raw / pow\n return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow\n}\n\n// In order, dropping any that would overlap one already placed\nfunction placeLabels(candidates: readonly Label[]) {\n const placed: Label[] = []\n for (const a of candidates) {\n if (\n placed.every(\n b => Math.abs(a.at - b.at) >= (a.width + b.width) / 2 + LABEL_GAP_PX,\n )\n ) {\n placed.push(a)\n }\n }\n return placed.map(({ text, at }) => ({ text, at }))\n}\n\nexport interface RulerMarks {\n // each box's span; the gaps between them stay blank, since the lane changes\n // drawn there cover no reference\n spans: { x0: number; x1: number }[]\n ticks: number[]\n labels: { text: string; at: number }[]\n}\n\n// On the reference axis the boxes sit on their bp, so one scale runs across\n// the ruler and its ticks fall on round positions.\nfunction scaleMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n): RulerMarks | undefined {\n const first = boxes[0]\n const last = boxes.at(-1)\n if (!first || !last) {\n return undefined\n }\n const from = x(first.x0)\n const to = x(last.x1)\n const bpSpan = last.bp1 - first.bp0\n if (to <= from || bpSpan <= 0) {\n return undefined\n }\n const step = niceStep((bpSpan / (to - from)) * TARGET_TICK_PX)\n const ticks: number[] = []\n const positions: Label[] = []\n for (\n let bp = Math.ceil(first.bp0 / step) * step;\n bp <= last.bp1;\n bp += step\n ) {\n const sx = Math.round(x(tubeX(boxes, bp))) + 0.5\n if (sx >= 0 && sx <= width) {\n ticks.push(sx)\n const text = bp.toLocaleString('en-US')\n positions.push({ text, at: sx, width: measure(text) })\n }\n }\n return {\n spans: [{ x0: from, x1: to }],\n ticks,\n labels: placeLabels(positions),\n }\n}\n\n// On the own axis every box is as wide as the log of its length (tubemap-core's\n// compressed widths: 2 bp is 8 px, 100 bp 56, 1 kb 84, 22 kb 121), so no box is\n// to scale and no one scale runs across them. The ruler treats every box alike:\n// a bracket under it, a tick at each end, its length where that fits inside it,\n// and positions at box boundaries. Labels are placed in that order after the\n// two ends of what is on screen.\nfunction boxMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n): RulerMarks | undefined {\n const shown = boxes\n .map(box => ({ box, s0: x(box.x0), s1: x(box.x1) }))\n .filter(({ s0, s1 }) => s1 >= 0 && s0 <= width)\n const first = shown[0]\n const last = shown.at(-1)\n if (!first || !last) {\n return undefined\n }\n const label = (text: string, at: number) => ({\n text,\n at,\n width: measure(text),\n })\n const position = (bp: number, at: number) =>\n label(bp.toLocaleString('en-US'), at)\n const spans: RulerMarks['spans'] = []\n const ticks: number[] = []\n const lengths: Label[] = []\n const boundaries: Label[] = []\n for (const { box, s0, s1 } of shown) {\n spans.push({ x0: s0, x1: s1 })\n ticks.push(Math.round(s0) + 0.5)\n if (s1 - s0 >= MIN_BRACKET_PX) {\n ticks.push(Math.round(s1) + 0.5)\n }\n const length = label(formatBp(box.bp1 - box.bp0), (s0 + s1) / 2)\n if (length.width <= s1 - s0) {\n lengths.push(length)\n }\n if (box !== first.box) {\n boundaries.push(position(box.bp0, s0))\n }\n }\n const ends = [\n position(first.box.bp0, first.s0),\n position(last.box.bp1, last.s1),\n ]\n return {\n spans,\n ticks,\n labels: placeLabels([...ends, ...lengths, ...boundaries]),\n }\n}\n\nexport function rulerMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n referenceAxis = false,\n) {\n return (referenceAxis ? scaleMarks : boxMarks)(boxes, x, width, measure)\n}\n\n// Reference bp along the tubes: round positions on the reference axis, a\n// bracket per box on the own axis\nexport function drawTubeMapRuler(\n ctx: CanvasRenderingContext2D,\n boxes: readonly Box[],\n frame: TubeMapFrame,\n top: number,\n referenceAxis = false,\n) {\n const { x, width, darkMode } = frame\n ctx.font = '10px sans-serif'\n const marks = rulerMarks(\n boxes,\n x,\n width,\n text => ctx.measureText(text).width,\n referenceAxis,\n )\n if (!marks) {\n return\n }\n const ink = rulerInk(darkMode)\n const y = top + 0.5\n ctx.strokeStyle = ink\n ctx.fillStyle = ink\n ctx.lineWidth = 1\n ctx.textAlign = 'center'\n ctx.textBaseline = 'top'\n ctx.beginPath()\n for (const { x0, x1 } of marks.spans) {\n ctx.moveTo(Math.max(0, x0), y)\n ctx.lineTo(Math.min(width, x1), y)\n }\n for (const sx of marks.ticks) {\n ctx.moveTo(sx, top)\n ctx.lineTo(sx, top + TICK_PX)\n }\n ctx.stroke()\n for (const { text, at } of marks.labels) {\n ctx.fillText(text, at, top + TICK_PX + 2)\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Graph } from '../types'\nimport type { TubeMapLayout } from '@gmod/tubemap-core'\n\n// On its own axis a tube map keeps none of the linear view's bp: a box is as\n// wide as log2 of its length, and the lane changes between boxes take px that\n// cover no reference at all. A band from each reference box up to the bp it\n// covers on the linear view's ruler ties the two back together, the way the\n// LD display ties its matrix columns to their variants.\n\nexport interface ReferenceNode {\n node: string\n bp0: number\n bp1: number\n x0: number\n x1: number\n}\n\n// A reference node's band, in screen px: its bp span along the top of the\n// zone, its box's span along the bottom\nexport interface Connector {\n node: string\n top0: number\n top1: number\n bottom0: number\n bottom1: number\n}\n\nexport function referenceNodes(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const out: ReferenceNode[] = []\n // sparse: an unreached node has no entry\n layout.nodes.forEach(node => {\n const graphNode = nodeById.get(node.name)\n if (node.order >= 0 && graphNode && isBackbone(graphNode)) {\n out.push({\n node: node.name,\n bp0: graphNode.stable.start,\n bp1: graphNode.stable.start + node.sequenceLength,\n x0: node.x,\n x1: node.x + node.pixelWidth,\n })\n }\n })\n return out\n}\n\n// The bands whose box is on screen. A band's top may still run off the side,\n// which is the linear view saying that box is outside its window.\nexport function tubeMapConnectors(\n nodes: readonly ReferenceNode[],\n bpToScreen: (bp: number) => number,\n tubeToScreen: (tx: number) => number,\n width: number,\n): Connector[] {\n const out: Connector[] = []\n for (const n of nodes) {\n const bottom0 = tubeToScreen(n.x0)\n const bottom1 = tubeToScreen(n.x1)\n if (bottom1 >= 0 && bottom0 <= width) {\n out.push({\n node: n.node,\n top0: bpToScreen(n.bp0),\n top1: bpToScreen(n.bp1),\n bottom0,\n bottom1,\n })\n }\n }\n return out\n}\n\n// A SNP's band is a sliver, so the pointer gets a few px either side of one\nconst HIT_SLOP_PX = 3\n\nexport function connectorAt(\n connectors: readonly Connector[],\n zoneBottom: number,\n sx: number,\n sy: number,\n) {\n if (zoneBottom <= 0 || sy < 0 || sy > zoneBottom) {\n return undefined\n }\n const t = sy / zoneBottom\n let hit: string | undefined\n let best = HIT_SLOP_PX\n for (const c of connectors) {\n const a = c.top0 + (c.bottom0 - c.top0) * t\n const b = c.top1 + (c.bottom1 - c.top1) * t\n const dist = Math.max(0, Math.min(a, b) - sx, sx - Math.max(a, b))\n if (dist < best) {\n best = dist\n hit = c.node\n }\n }\n return hit\n}\n\nfunction band(ctx: CanvasRenderingContext2D, c: Connector, bottom: number) {\n ctx.moveTo(c.top0, 0)\n ctx.lineTo(c.top1, 0)\n ctx.lineTo(c.bottom1, bottom)\n ctx.lineTo(c.bottom0, bottom)\n ctx.closePath()\n}\n\nfunction sides(ctx: CanvasRenderingContext2D, c: Connector, bottom: number) {\n ctx.moveTo(c.top0, 0)\n ctx.lineTo(c.bottom0, bottom)\n ctx.moveTo(c.top1, 0)\n ctx.lineTo(c.bottom1, bottom)\n}\n\n// One fill and one stroke for every band, so where hundreds of them converge\n// on the ruler the ink composites once rather than going black. The lit band\n// takes the tube map's own highlight.\nexport function drawTubeMapConnectors(\n ctx: CanvasRenderingContext2D,\n connectors: readonly Connector[],\n bottom: number,\n {\n highlightNode,\n darkMode,\n }: { highlightNode?: string | null; darkMode?: boolean },\n) {\n if (bottom <= 0 || connectors.length === 0) {\n return\n }\n const ink = darkMode ? '255,255,255' : '0,0,0'\n ctx.beginPath()\n for (const c of connectors) {\n band(ctx, c, bottom)\n }\n ctx.fillStyle = `rgba(${ink},0.07)`\n ctx.fill()\n ctx.beginPath()\n for (const c of connectors) {\n sides(ctx, c, bottom)\n }\n ctx.strokeStyle = `rgba(${ink},0.35)`\n ctx.lineWidth = 0.5\n ctx.stroke()\n const lit = connectors.find(c => c.node === highlightNode)\n if (lit) {\n ctx.beginPath()\n band(ctx, lit, bottom)\n ctx.fillStyle = 'rgba(255,192,203,0.5)'\n ctx.fill()\n ctx.beginPath()\n sides(ctx, lit, bottom)\n ctx.strokeStyle = '#ff0000'\n ctx.lineWidth = 1\n ctx.stroke()\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Deviation } from './coarsen'\nimport type { TubeMapFrame } from './draw'\nimport type { Graph } from '../types'\nimport type { LayoutNode, TubeMapLayout } from '@gmod/tubemap-core'\n\n// A folded variant on the tube of the walk that carries it, in tube\n// coordinates. Placed by bp through the box that holds it, since tubemap-core\n// may merge the coarsened nodes again and a box can stand for several.\nexport interface DeviationMark {\n x0: number\n x1: number\n y: number\n height: number\n}\n\ninterface Box {\n bp0: number\n bp1: number\n index: number\n node: LayoutNode\n}\n\nfunction boxesOf(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const boxes: Box[] = []\n layout.nodes.forEach((node, index) => {\n const g = nodeById.get(node.name)\n if (node.order >= 0 && g && isBackbone(g)) {\n const bp0 = g.stable.start\n boxes.push({ bp0, bp1: bp0 + node.sequenceLength, index, node })\n }\n })\n return boxes.sort((a, b) => a.bp0 - b.bp0)\n}\n\n// The box holding `bp` that this walk passes through; an insertion on a\n// boundary between two boxes sits on whichever the walk visits\nfunction boxAt(\n boxes: readonly Box[],\n bp: number,\n visited: Map<number, number>,\n) {\n let lo = 0\n let hi = boxes.length - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (boxes[mid]!.bp0 <= bp) {\n lo = mid\n } else {\n hi = mid - 1\n }\n }\n return [boxes[lo], boxes[lo - 1]].find(\n box => box && visited.has(box.index) && box.bp0 <= bp && bp <= box.bp1,\n )\n}\n\nexport function deviationMarks(\n graph: Graph,\n layout: TubeMapLayout,\n deviations: Map<string, Deviation[]>,\n) {\n const boxes = boxesOf(graph, layout)\n const marks: DeviationMark[] = []\n for (const track of layout.tracks) {\n const devs = track.name ? deviations.get(track.name) : undefined\n if (devs?.length) {\n // a walk that loops through a box twice marks its first pass\n const visited = new Map<number, number>()\n for (const segment of track.path) {\n if (\n segment.node !== null &&\n segment.y !== undefined &&\n !visited.has(segment.node)\n ) {\n visited.set(segment.node, segment.y)\n }\n }\n for (const d of devs) {\n const box = boxAt(boxes, d.start, visited)\n if (box) {\n const { node, bp0, bp1 } = box\n const at = (bp: number) =>\n node.x + ((Math.min(bp, bp1) - bp0) / (bp1 - bp0)) * node.pixelWidth\n marks.push({\n x0: at(d.start),\n x1: at(d.end),\n y: visited.get(box.index)!,\n height: track.width,\n })\n }\n }\n }\n }\n return marks\n}\n\n// Dark on light and light on dark, since the tubes themselves carry every hue\nexport function deviationInk(darkMode?: boolean) {\n return darkMode ? 'rgba(255,255,255,0.9)' : 'rgba(0,0,0,0.8)'\n}\n\n// A tick at least a px wide across the tube\nexport function drawDeviationMarks(\n ctx: CanvasRenderingContext2D,\n marks: readonly DeviationMark[],\n { x, y, width, darkMode }: TubeMapFrame,\n) {\n if (marks.length === 0) {\n return\n }\n ctx.fillStyle = deviationInk(darkMode)\n ctx.beginPath()\n for (const m of marks) {\n const left = x(m.x0)\n const right = Math.max(x(m.x1), left + 1)\n if (right >= 0 && left <= width) {\n const top = y(m.y)\n ctx.rect(left, top, right - left, y(m.y + m.height) - top)\n }\n }\n ctx.fill()\n}\n", "import { forward, getXCoordinateOfBaseWithinNode } from '@gmod/tubemap-core'\n\nimport type { Track, TubeMapLayout } from '@gmod/tubemap-core'\n\n// Where a read differs from the nodes it runs through, in tube coordinates:\n// the marks sequenceTubeMap's drawMismatches puts on a read's tube. Each mark\n// spans the bases it covers and sits on the tube of the read that carries it\n// (`y` is the tube's top, `height` its width), so a painter only maps them\n// through the frame. `nodeY` is the top of the node, which a hover draws a\n// leader up to.\ninterface MarkBase {\n readId: number\n y: number\n height: number\n nodeY: number\n}\n\nexport type TubeMapMismatch =\n | (MarkBase & {\n kind: 'insertion'\n x: number\n seq?: string\n // at the read's first or last base, where an aligner clips rather than\n // inserts; sequenceTubeMap hides these unless soft clips are shown\n softClip: boolean\n })\n | (MarkBase & { kind: 'deletion'; x0: number; x1: number })\n | (MarkBase & { kind: 'substitution'; x0: number; x1: number; seq: string })\n\nfunction readMismatches(read: Track, layout: TubeMapLayout) {\n const marks: TubeMapMismatch[] = []\n const entries = read.sequenceNew ?? []\n entries.forEach((entry, i) => {\n const nodeIndex = layout.nodeMap.get(forward(entry.nodeName))\n const node = nodeIndex === undefined ? undefined : layout.nodes[nodeIndex]\n // Node merging can drop a visit, so the segment for entry i is at or after\n // path[i], and the walk stops at the path's end.\n let pathIndex = i\n while (\n pathIndex < read.path.length &&\n read.path[pathIndex]!.node !== nodeIndex\n ) {\n pathIndex += 1\n }\n const y = read.path[pathIndex]?.y\n if (node && y !== undefined) {\n const base = { readId: read.id, y, height: read.width, nodeY: node.y }\n // A base past a merged node's end has no x.\n const at = (pos: number) => getXCoordinateOfBaseWithinNode(node, pos)\n for (const mm of entry.mismatches) {\n const x0 = at(mm.pos)\n if (x0 !== null) {\n if (mm.type === 'insertion') {\n const softClip =\n (i === 0 && mm.pos === read.firstNodeOffset) ||\n (i === entries.length - 1 && mm.pos === read.finalNodeCoverLength)\n marks.push({\n ...base,\n kind: 'insertion',\n x: x0,\n seq: mm.seq,\n softClip,\n })\n } else if (mm.type === 'deletion' && mm.length !== undefined) {\n const x1 = at(mm.pos + mm.length)\n if (x1 !== null) {\n marks.push({ ...base, kind: 'deletion', x0, x1 })\n }\n } else if (mm.type === 'substitution' && mm.seq !== undefined) {\n const x1 = at(mm.pos + mm.seq.length)\n if (x1 !== null) {\n marks.push({ ...base, kind: 'substitution', x0, x1, seq: mm.seq })\n }\n }\n }\n }\n }\n })\n return marks\n}\n\nexport function tubeMapMismatches(layout: TubeMapLayout) {\n return layout.reads.flatMap(read => readMismatches(read, layout))\n}\n", "import { curvePaths, nodeOutlinePath } from '@gmod/tubemap-core'\n\nimport { tubeMapMismatches } from './mismatches'\n\nimport type { TubeMapTransform } from './frame'\nimport type { TubeMapMismatch } from './mismatches'\nimport type { TrackType, TubeMapLayout } from '@gmod/tubemap-core'\n\n// Paints a tube map layout on a 2D canvas, in the order sequenceTubeMap's d3\n// drawing stacks it: haplotype tubes, their lane changes and turnarounds, then\n// reads the same way, then the node boxes over all of them. Everything goes\n// through `x` and `y`, tube coordinates to screen px, so the one painter serves\n// the tube map's own axis (an affine map) and the reference axis (warp.ts).\n//\n// The package states curves, corners and node outlines as SVG path data. They\n// are parsed once per layout into commands whose points can be mapped, since\n// a Path2D built from the string could only be transformed affinely.\n\ntype Command =\n | { op: 'M' | 'L'; x: number; y: number }\n | { op: 'Q'; x1: number; y1: number; x: number; y: number }\n | {\n op: 'C'\n x1: number\n y1: number\n x2: number\n y2: number\n x: number\n y: number\n }\n | { op: 'H'; x: number }\n | { op: 'V'; y: number }\n | { op: 'Z' }\n\n// The absolute M/L/H/V/Q/C/Z subset tubemap-core writes\nexport function parsePath(d: string): Command[] {\n const tokens = d.trim().split(/[\\s,]+/)\n const commands: Command[] = []\n let i = 0\n const num = () => Number(tokens[i++])\n while (i < tokens.length) {\n const op = tokens[i++]\n if (op === 'M' || op === 'L') {\n commands.push({ op, x: num(), y: num() })\n } else if (op === 'H') {\n commands.push({ op, x: num() })\n } else if (op === 'V') {\n commands.push({ op, y: num() })\n } else if (op === 'Q') {\n commands.push({ op, x1: num(), y1: num(), x: num(), y: num() })\n } else if (op === 'C') {\n commands.push({\n op,\n x1: num(),\n y1: num(),\n x2: num(),\n y2: num(),\n x: num(),\n y: num(),\n })\n } else if (op === 'Z') {\n commands.push({ op })\n }\n }\n return commands\n}\n\ninterface Filled {\n color: string\n // a haplotype tube's track id, which is its path's index in the graph\n track?: number\n alpha: number\n // tube x extent, for culling\n x0: number\n x1: number\n}\n\ninterface Rect extends Filled {\n y0: number\n y1: number\n}\n\ninterface Shape extends Filled {\n commands: Command[]\n}\n\ninterface Layer {\n rects: Rect[]\n shapes: Shape[]\n}\n\nexport interface TubeMapPicture {\n bounds: TubeMapLayout['bounds']\n layers: Layer[]\n nodes: { name: string; commands: Command[]; x0: number; x1: number }[]\n mismatches: TubeMapMismatch[]\n}\n\nfunction extentOf(commands: Command[]) {\n let x0 = Infinity\n let x1 = -Infinity\n for (const c of commands) {\n if ('x' in c) {\n x0 = Math.min(x0, c.x)\n x1 = Math.max(x1, c.x)\n }\n if ('x1' in c) {\n x0 = Math.min(x0, c.x1)\n x1 = Math.max(x1, c.x1)\n }\n }\n return { x0, x1 }\n}\n\nfunction shapeOf(\n d: string,\n color: string,\n alpha: number,\n track: number | undefined,\n): Shape {\n const commands = parsePath(d)\n return { commands, color, track, alpha, ...extentOf(commands) }\n}\n\nfunction layerOf(layout: TubeMapLayout, type: TrackType): Layer {\n const { shapes } = layout\n const trackOf = (s: { id: number }) =>\n type === 'haplotype' ? s.id : undefined\n const rects = [...shapes.rectangles, ...shapes.verticalRectangles]\n .filter(r => r.type === type)\n .map(r => ({\n color: r.color,\n track: trackOf(r),\n alpha: r.alpha ?? 1,\n x0: r.xStart,\n x1: r.xEnd + 1,\n y0: r.yStart,\n y1: r.yEnd + 1,\n }))\n const curves = curvePaths(shapes.curves, type).map(c =>\n shapeOf(c.path!, c.color, c.alpha ?? 1, trackOf(c)),\n )\n const corners = shapes.corners\n .filter(c => c.type === type)\n .map(c => shapeOf(c.path, c.color, 1, trackOf(c)))\n return { rects, shapes: [...curves, ...corners] }\n}\n\n// Everything a frame needs that does not depend on the transform\nexport function tubeMapPicture(layout: TubeMapLayout): TubeMapPicture {\n const nodes: TubeMapPicture['nodes'] = []\n layout.nodes.forEach(node => {\n if (node.order >= 0) {\n const commands = parsePath(nodeOutlinePath(node))\n nodes.push({ name: node.name, commands, ...extentOf(commands) })\n }\n })\n return {\n bounds: layout.bounds,\n layers: [layerOf(layout, 'haplotype'), layerOf(layout, 'read')],\n nodes,\n mismatches: tubeMapMismatches(layout).filter(\n m => m.kind !== 'insertion' || !m.softClip,\n ),\n }\n}\n\nfunction trace(\n ctx: CanvasRenderingContext2D,\n commands: Command[],\n x: (tx: number) => number,\n y: (ty: number) => number,\n) {\n let cx = 0\n let cy = 0\n for (const c of commands) {\n switch (c.op) {\n case 'M':\n cx = c.x\n cy = c.y\n ctx.moveTo(x(cx), y(cy))\n break\n case 'L':\n cx = c.x\n cy = c.y\n ctx.lineTo(x(cx), y(cy))\n break\n case 'H':\n cx = c.x\n ctx.lineTo(x(cx), y(cy))\n break\n case 'V':\n cy = c.y\n ctx.lineTo(x(cx), y(cy))\n break\n case 'Q':\n cx = c.x\n cy = c.y\n ctx.quadraticCurveTo(x(c.x1), y(c.y1), x(cx), y(cy))\n break\n case 'C':\n cx = c.x\n cy = c.y\n ctx.bezierCurveTo(x(c.x1), y(c.y1), x(c.x2), y(c.y2), x(cx), y(cy))\n break\n case 'Z':\n ctx.closePath()\n break\n }\n }\n}\n\nexport interface TubeMapFrame extends TubeMapTransform {\n width: number\n highlightNode?: string | null\n darkMode?: boolean\n // a haplotype tube's colour by track id, where it is not the layout's\n tubeColors?: readonly string[]\n // a box's tint by node name; a box with none is clear\n nodeColors?: ReadonlyMap<string, string>\n}\n\n// One fill per colour per layer: a layer's shapes do not overlap in a way the\n// order within it would decide, and a fill per shape costs a draw call each.\nfunction fillByColor<T extends Filled>(\n ctx: CanvasRenderingContext2D,\n items: T[],\n visible: (item: T) => boolean,\n colorOf: (item: T) => string,\n addTo: (item: T) => void,\n) {\n const byColor = new Map<string, T[]>()\n for (const item of items) {\n if (visible(item)) {\n const key = `${colorOf(item)}|${item.alpha}`\n const list = byColor.get(key)\n if (list) {\n list.push(item)\n } else {\n byColor.set(key, [item])\n }\n }\n }\n for (const list of byColor.values()) {\n ctx.fillStyle = colorOf(list[0]!)\n ctx.globalAlpha = list[0]!.alpha\n ctx.beginPath()\n for (const item of list) {\n addTo(item)\n }\n ctx.fill()\n }\n ctx.globalAlpha = 1\n}\n\n// Zoomed out, a box is a few px of outline and a cut's boxes ink over the\n// tubes between them, so an outline fades as its box narrows\nconst FULL_OUTLINE_PX = 12\nconst MIN_OUTLINE_ALPHA = 0.15\n\nfunction outlineAlpha(screenWidth: number) {\n return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX))\n}\n\n// A tinted box lets the tubes through it show, as the clear box's wash does\nconst NODE_TINT_ALPHA = 0.55\n\nexport function drawTubeMap(\n ctx: CanvasRenderingContext2D,\n picture: TubeMapPicture,\n frame: TubeMapFrame,\n) {\n const { x, y, width } = frame\n const visible = (item: { x0: number; x1: number }) =>\n x(item.x1) >= 0 && x(item.x0) <= width\n const colorOf = (item: Filled) =>\n (item.track !== undefined && frame.tubeColors?.[item.track]) || item.color\n for (const layer of picture.layers) {\n fillByColor(ctx, layer.rects, visible, colorOf, r => {\n const left = x(r.x0)\n const top = y(r.y0)\n ctx.rect(left, top, x(r.x1) - left, y(r.y1) - top)\n })\n fillByColor(ctx, layer.shapes, visible, colorOf, s => {\n trace(ctx, s.commands, x, y)\n })\n }\n const stroke = frame.darkMode ? '#d0d0d0' : '#000000'\n const fill = frame.darkMode ? 'rgba(40,40,40,0.4)' : 'rgba(255,255,255,0.4)'\n ctx.lineWidth = 2 * Math.max(0.25, Math.min(1, frame.yScale))\n for (const node of picture.nodes) {\n if (visible(node)) {\n const lit = node.name === frame.highlightNode\n ctx.beginPath()\n trace(ctx, node.commands, x, y)\n const tint = lit ? undefined : frame.nodeColors?.get(node.name)\n ctx.fillStyle = lit ? 'rgba(255,192,203,0.5)' : (tint ?? fill)\n ctx.strokeStyle = lit ? '#ff0000' : stroke\n ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1\n ctx.fill()\n ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0))\n ctx.stroke()\n ctx.globalAlpha = 1\n }\n }\n drawMismatches(ctx, picture.mismatches, frame)\n}\n\n// sequenceTubeMap's marks, which it draws at 12px and hides once zoomed out\n// below half size\nconst MISMATCH_FONT_PX = 12\nconst MIN_MISMATCH_FONT_PX = 6\n\nexport function mismatchesLegible(yScale: number) {\n return MISMATCH_FONT_PX * yScale >= MIN_MISMATCH_FONT_PX\n}\n\nexport function mismatchOnScreen(\n m: TubeMapMismatch,\n { x, width }: Pick<TubeMapFrame, 'x' | 'width'>,\n) {\n const [x0, x1] = m.kind === 'insertion' ? [m.x, m.x] : [m.x0, m.x1]\n return x(x1) >= 0 && x(x0) <= width\n}\n\nfunction drawMismatches(\n ctx: CanvasRenderingContext2D,\n marks: readonly TubeMapMismatch[],\n frame: TubeMapFrame,\n) {\n const { x, y, yScale, darkMode } = frame\n if (!mismatchesLegible(yScale) || marks.length === 0) {\n return\n }\n const fontPx = MISMATCH_FONT_PX * yScale\n ctx.fillStyle = 'grey'\n ctx.beginPath()\n for (const m of marks) {\n if (m.kind === 'deletion' && mismatchOnScreen(m, frame)) {\n const left = x(m.x0)\n const top = y(m.y)\n ctx.rect(left, top, x(m.x1) - left, y(m.y + m.height) - top)\n }\n }\n ctx.fill()\n ctx.font = `${fontPx}px monospace`\n ctx.textBaseline = 'alphabetic'\n ctx.fillStyle = darkMode ? '#ffffff' : '#000000'\n for (const m of marks) {\n if (!mismatchOnScreen(m, frame)) {\n continue\n }\n if (m.kind === 'substitution') {\n ctx.fillText(m.seq, x(m.x0) + 1, y(m.y + m.height))\n } else if (m.kind === 'insertion') {\n ctx.fillText('*', x(m.x) - 3, y(m.y + m.height))\n }\n }\n}\n", "import { tubeSpan } from './axis'\n\nimport type { ReferenceBoxes } from './axis'\nimport type { TubeMapFrame } from './draw'\nimport type { GeneModel } from '../genes/genePins'\n\n// A gene over the tube map, in tube x, mapped through the reference's boxes\n// (axis.ts)\nexport interface TubeMapGene {\n gene: GeneModel\n x0: number\n x1: number\n exons: { x0: number; x1: number }[]\n}\n\n// A gene takes the boxes of the refName it equals, as in genePins\nexport function tubeMapGenes(\n byRefName: ReferenceBoxes,\n genes: readonly GeneModel[],\n): TubeMapGene[] {\n const out: TubeMapGene[] = []\n for (const gene of genes) {\n const boxes = byRefName.get(gene.refName)\n const first = boxes?.[0]\n const last = boxes?.at(-1)\n if (!boxes || !first || !last) {\n continue\n }\n const start = Math.max(gene.start, first.bp0)\n const end = Math.min(gene.end, last.bp1)\n if (end <= start) {\n continue\n }\n out.push({\n gene,\n ...tubeSpan(boxes, start, end),\n exons: gene.exons\n .filter(e => e.end > start && e.start < end)\n .map(e =>\n tubeSpan(boxes, Math.max(e.start, start), Math.min(e.end, end)),\n ),\n })\n }\n return out\n}\n\nexport const GENE_ROW_PX = 22\nconst MAX_GENE_ROWS = 4\nconst EXON_PX = 8\nconst NAME_FONT = '11px sans-serif'\nconst NAME_GAP_PX = 8\n\n// The rows the genes need at least: as many as overlap at one point, which\n// holds at every zoom. A name wider than its gene can want more, and shares\n// the top row.\nexport function tubeMapGeneRows(genes: readonly TubeMapGene[]) {\n const edges = genes\n .flatMap(g => [\n { x: g.x0, step: 1 },\n { x: g.x1, step: -1 },\n ])\n .sort((a, b) => a.x - b.x || a.step - b.step)\n let depth = 0\n let most = 0\n for (const { step } of edges) {\n depth += step\n most = Math.max(most, depth)\n }\n return Math.min(MAX_GENE_ROWS, most)\n}\n\n// Rows of genes above the tubes, bottom row nearest them, packed so neither\n// glyphs nor names overlap. Packed per frame, since the reference axis warps x\n// by zoom. The rows go up to `top`; past that, genes share the top row.\nexport function drawTubeMapGenes(\n ctx: CanvasRenderingContext2D,\n genes: readonly TubeMapGene[],\n frame: TubeMapFrame,\n bottom: number,\n top = 0,\n) {\n const { x, width, darkMode } = frame\n const rows = Math.max(1, Math.floor((bottom - top) / GENE_ROW_PX))\n const rowEnds: number[] = []\n const ink = darkMode ? '#e0e0e6' : '#1c1c22'\n ctx.font = NAME_FONT\n ctx.textAlign = 'center'\n ctx.textBaseline = 'alphabetic'\n ctx.fillStyle = ink\n ctx.strokeStyle = ink\n ctx.lineWidth = 1\n const placed = genes\n .map(g => ({ g, s0: x(g.x0), s1: x(g.x1) }))\n .filter(({ s0, s1 }) => s1 >= 0 && s0 <= width)\n .sort((a, b) => a.s0 - b.s0)\n for (const { g, s0, s1 } of placed) {\n const arrow = g.gene.strand > 0 ? ' \u2192' : g.gene.strand < 0 ? '\u2190 ' : ''\n const label =\n g.gene.strand < 0 ? `${arrow}${g.gene.name}` : `${g.gene.name}${arrow}`\n const mid = (Math.max(s0, 0) + Math.min(s1, width)) / 2\n const half = ctx.measureText(label).width / 2\n const left = Math.min(s0, mid - half)\n const right = Math.max(s1, mid + half)\n let row = rowEnds.findIndex(end => end + NAME_GAP_PX <= left)\n if (row === -1) {\n row = Math.min(rowEnds.length, rows - 1)\n }\n rowEnds[row] = Math.max(rowEnds[row] ?? -Infinity, right)\n const glyphY = bottom - row * GENE_ROW_PX - EXON_PX / 2\n ctx.beginPath()\n ctx.moveTo(s0, glyphY + 0.5)\n ctx.lineTo(s1, glyphY + 0.5)\n ctx.stroke()\n for (const e of g.exons) {\n const e0 = x(e.x0)\n ctx.fillRect(e0, glyphY - EXON_PX / 2, Math.max(1, x(e.x1) - e0), EXON_PX)\n }\n ctx.fillText(label, mid, glyphY - EXON_PX / 2 - 2)\n }\n}\n", "import type { RepeatCall } from './repeatFeatures'\n\n// Which genotyped allele belongs to which haplotype walk. A VCF states a\n// sample's alleles in genotype order, which is the genotyper's own order and\n// carries no correspondence to the assembly's haplotype numbering \u2014 TRGTdb's\n// ABCA7 record, for one, lists them shortest first. So the walks and the\n// alleles are paired by length instead: the pairing whose total difference is\n// smallest, which for lengths on a line is the non-crossing one.\n//\n// A walk that does not reach both flanking reference nodes is measured whole\n// rather than across the array, and an allele no read spanned is a copy of the\n// one that was called. Neither states a length for this array, so they pair\n// last and take no verdict \u2014 pairing them first let a walk that stops inside\n// the array take the allele that fit one spanning it, and the spanning walk\n// then read as a disagreement.\n\nexport interface WalkCall extends RepeatCall {\n // undefined where the walk and the allele are not both measurements\n agrees?: boolean\n}\n\n// Every walk/call pair over the 94 HPRC samples TRGT genotyped at the ABCA7\n// VNTR is either within 8% of its allele or more than 14% from it, so any\n// threshold across that gap separates them the same way.\nexport const CALL_TOLERANCE = 0.1\n\nfunction agrees(walkBp: number, callBp: number) {\n return Math.abs(callBp - walkBp) <= CALL_TOLERANCE * Math.max(walkBp, callBp)\n}\n\n// Pair two ascending lists of lengths, leaving out the surplus of whichever is\n// longer. `out[i]` is the index in `calls` that `walks[i]` took.\nfunction nearest(walks: number[], calls: number[]) {\n const W = walks.length\n const C = calls.length\n const cost = Array.from({ length: W + 1 }, () => new Array(C + 1).fill(0))\n const paired = Array.from({ length: W + 1 }, () =>\n new Array<boolean>(C + 1).fill(false),\n )\n for (let i = W - 1; i >= 0; i--) {\n for (let j = C - 1; j >= 0; j--) {\n const together = Math.abs(walks[i]! - calls[j]!) + cost[i + 1]![j + 1]!\n const dropWalk = W - i > C - j ? cost[i + 1]![j]! : Infinity\n const dropCall = C - j > W - i ? cost[i]![j + 1]! : Infinity\n cost[i]![j] = Math.min(together, dropWalk, dropCall)\n paired[i]![j] = cost[i]![j] === together\n }\n }\n const out = new Array<number | undefined>(W).fill(undefined)\n for (let i = 0, j = 0; i < W && j < C;) {\n if (paired[i]![j]) {\n out[i++] = j++\n } else if (W - i > C - j) {\n i++\n } else {\n j++\n }\n }\n return out\n}\n\nfunction byLength<T>(items: T[], bp: (item: T) => number) {\n return items\n .map((item, i) => ({ item, i }))\n .sort((a, b) => bp(a.item) - bp(b.item))\n}\n\nconst backed = (call: RepeatCall) => call.spanningReads !== 0\n\nfunction scored(row: { bp: number; complete: boolean }, call: RepeatCall) {\n return {\n ...call,\n agrees: row.complete && backed(call) ? agrees(row.bp, call.bp) : undefined,\n }\n}\n\n// A phased genotype names its walk's haplotype outright. The rest go by\n// length: the walks that span the array against the alleles reads spanned,\n// then everything else against what those two left. That decides the\n// ambiguous cases on merit: which walk carries the one allele a lone spanning\n// read supports, and which allele a walk that stops inside the array is drawn\n// against.\nfunction pairSample<\n R extends { bp: number; complete: boolean; haplotype?: number },\n>(rows: R[], calls: RepeatCall[]) {\n const out = new Map<number, WalkCall>()\n const taken = new Set<RepeatCall>()\n rows.forEach((row, i) => {\n const call = calls.find(\n c => c.haplotype !== undefined && c.haplotype === row.haplotype,\n )\n if (call) {\n taken.add(call)\n out.set(i, scored(row, call))\n }\n })\n const unphased = calls.filter(call => call.haplotype === undefined)\n const ranked = byLength(rows, r => r.bp)\n const stages = [\n [ranked.filter(r => r.item.complete), unphased.filter(backed)],\n [ranked, unphased],\n ] as const\n for (const [pool, group] of stages) {\n const free = pool.filter(r => !out.has(r.i))\n const open = byLength(\n group.filter(call => !taken.has(call)),\n call => call.bp,\n )\n nearest(\n free.map(r => r.item.bp),\n open.map(c => c.item.bp),\n ).forEach((pick, k) => {\n if (pick !== undefined) {\n const row = free[k]!\n const call = open[pick]!.item\n taken.add(call)\n out.set(row.i, scored(row.item, call))\n }\n })\n }\n return out\n}\n\nexport function withCalls<\n R extends {\n sample: string\n bp: number\n complete: boolean\n haplotype?: number\n },\n>(rows: R[], calls: Record<string, RepeatCall[]> | undefined) {\n if (!calls) {\n return rows as (R & { call?: WalkCall })[]\n }\n const paired = new Map<R, WalkCall>()\n const bySample = new Map<string, R[]>()\n for (const row of rows) {\n bySample.set(row.sample, [...(bySample.get(row.sample) ?? []), row])\n }\n for (const [sample, group] of bySample) {\n const called = calls[sample]\n if (called) {\n for (const [i, call] of pairSample(group, called)) {\n paired.set(group[i]!, call)\n }\n }\n }\n return rows.map(row => ({ ...row, call: paired.get(row) }))\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6\"\n}), 'Settings');"],
5
- "mappings": "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",
6
- "names": ["require_SvgIcon", "__commonJSMin", "exports", "module", "require_colorBits", "__commonJSMin", "exports", "module", "require_mobx", "__commonJSMin", "exports", "module", "require_canvas2dUtils", "__commonJSMin", "exports", "module", "require_renderingBackendBase", "__commonJSMin", "exports", "module", "import_SvgIcon", "HALO_FACTOR", "LEGEND_INSET_PX", "ROUTE_STACK", "GENE_PIN_DROP_PX", "GENE_STACK", "TOPMOST_BASELINE", "geneCoverageNote", "pin", "length", "per", "unit", "amount", "bp", "geneLabelCandidates", "pins", "screen", "contigThickness", "byExtent", "x", "y", "note", "items", "extent", "a", "b", "layoutLabels", "m", "width", "height", "translateX", "translateY", "scaleX", "scaleY", "frame", "p", "reserved", "label", "rowLabelBox", "stripPx", "take", "occupancy", "onRows", "rowsTop", "r", "halo", "byBubble", "h", "bubbles", "placeLabels", "baseline", "genes", "sizeCandidates", "sizeLabelCandidates", "deletionLabels", "placeSizeLabels", "routes", "route", "nodeLabels", "nodeLabelBudget", "import_colorBits", "import_colorBits", "fadeAbgr", "c", "alpha", "brightenAbgr", "factor", "MAX_BACKBONE_HOPS", "buildNeighbors", "graph", "neighbors", "add", "from", "to", "list", "edge", "flankingSpan", "nodeId", "nodeById", "seen", "frontier", "minEnd", "maxStart", "hop", "next", "id", "adjacent", "neighborId", "neighbor", "isBackbone", "nodeReferenceSpan", "node", "referenceMidpoints", "midpoints", "span", "cache", "baseEdgeCurves", "nodePositions", "graph", "axis", "deletions", "version", "routes", "scaleX", "scaleY", "cached", "curves", "ei", "edge", "from", "to", "bypassed", "route", "routedEdgeCurves", "computeEdgeCurves", "selfLinkOf", "bypassedPoints", "EDGE_DEFAULT_COLOR", "EDGE_PATH_FALLBACK_COLOR", "EDGE_DELETION_COLOR", "DELETION_THICKNESS_FACTOR", "LIFT_BACKDROP_COLOR", "FADED_ALPHA", "LIFT_BACKDROP_CSS", "fadeAbgr", "DELETION_DASH_PX", "MIN_PATH_STRIPE_PX", "MIN_WALK_LANE_PX", "MIN_SCALE_FOR_OFFSET", "ARROW_LENGTH_BASE_PX", "ARROW_LENGTH_PER_EDGE_PX", "ARROW_HALF_WIDTH_BASE_PX", "ARROW_HALF_WIDTH_PER_EDGE_PX", "MIN_ARROW_FIT", "MIN_ARROW_SCALE", "ARROW_CURVE_STEPS", "ABUTTING_PX", "walkX", "walkY", "walkPx", "backFromEnd", "curves", "scale", "yToX", "untilPx", "most", "last", "n", "px", "ci", "k", "t", "u", "a", "b", "d", "e", "x", "y", "walkPointAt", "i", "from", "to", "f", "arrowheadFor", "edgeHalfWidth", "nodeHalfWidth", "color", "fromSegments", "toSegments", "length", "lengthPx", "inset", "room", "polylinePx", "fit", "tip", "base", "dx", "dy", "endTangent", "intoDirection", "segments", "first", "final", "pointNormalsOf", "rawPoints", "points", "p", "pointNormals", "nx", "ny", "len", "dx1", "dy1", "len1", "dx2", "dy2", "len2", "nx1", "ny1", "nx2", "ny2", "dot", "offsetPolyline", "normals", "distance", "packNorm", "r", "g", "hslToRgb", "h", "s", "l", "c", "m", "DEPTH_GRADIENT", "STABLE_RANK_GRADIENT", "NODE_LENGTH_GRADIENT", "sampleGradient", "stops", "pos", "hashColor", "str", "alpha", "nameHue", "PATH_SATURATION", "PATH_LIGHTNESS", "REFERENCE_RAMP_MAX_HUE", "REFERENCE_RAMP_SATURATION", "REFERENCE_RAMP_LIGHTNESS", "REFERENCE_RAMP_ALT_RGB", "REFERENCE_RAMP_ALT_COLOR", "REFERENCE_RAMP_ALT_CSS", "computeReferenceRamp", "graph", "domain", "midpoints", "referenceMidpoints", "min", "max", "mid", "computeColorSchemeRange", "minDepth", "maxDepth", "minLength", "maxLength", "maxRank", "getNodeColor", "node", "nodeIndex", "colorScheme", "range", "ramp", "frac", "hue", "into", "candidates", "isPolylineInBounds", "bounds", "hit", "isBezierInBounds", "cMinX", "cMaxX", "cMinY", "cMaxY", "paletteCache", "pathPaletteOf", "palette", "pathColorByIndex", "pathIndexByName", "paths", "pathHueAt", "slotsCache", "NO_SLOTS", "pathSlotsOf", "slots", "pathIdx", "path", "nodeId", "visits", "buildGeometry", "options", "nodePositions", "nodeById", "contigThickness", "connectorThickness", "drawPaths", "nodeWidth", "highlight", "axis", "linearLayout", "viewportBounds", "referenceRamp", "deletions", "deletionRoutes", "hiddenEdges", "version", "depthNorm", "meanDepth", "yToXOf", "sharedCurves", "baseEdgeCurves", "nodeStrokes", "nodeStrokeRuns", "arrows", "arrowRuns", "edgeCurves", "edgeCurveRuns", "colorRange", "nodeIndexMap", "pathCount", "pathColorByName", "name", "nodePathSlots", "showArrows", "ei", "edge", "baseCurves", "numPaths", "isDeletion", "onWalk", "edgeThickness", "ribbons", "plainEdgeColor", "edgeColor", "toNode", "intoHalfWidth", "nodeWidthPx", "buildSingleEdge", "offsetX", "offsetY", "arrowColor", "translateCurves", "dash", "dashCurves", "head", "edgeCurveStart", "arrowStart", "laneWalks", "fromNode", "width", "laneWidth", "worldPerScreenPx", "walk", "lane", "offset", "offsets", "pathRibbonOffsets", "arrowRibbon", "ribbonColor", "edgeCurveCount", "arrowCount", "faded", "own", "nodeThickness", "start", "slotWidth", "drawable", "lanes", "laneColor", "slot", "stripe", "count", "rampHueCss", "hue", "rampHue", "bp", "domain", "frac", "REFERENCE_RAMP_MAX_HUE", "HUE_STOP_DEG", "rampStops", "start", "reversed", "low", "high", "n", "_", "RAMP_GRADIENT_CSS", "i", "nodeOwnLocation", "node", "isAnchored", "anchoredNodeLocation", "panSNSample", "panSNHaplotype", "panSNContig", "widestCluster", "spans", "maxGap", "sorted", "a", "b", "best", "current", "span", "groupKey", "loc", "contributingAssemblies", "graph", "groups", "key", "group", "bySample", "candidate", "existing", "resolveLocationAssembly", "resolve", "resolveContributors", "contributors", "c", "sample", "locString", "locLabel", "import_jsx_runtime", "CompareArrows_default", "_jsx", "import_jsx_runtime", "HighlightAlt_default", "_jsx", "import_jsx_runtime", "LineStyle_default", "_jsx", "LINEAR_LABEL", "SYNTENY_LABEL", "NO_SYNTENY_TRACK", "oneOrMany", "label", "icon", "entries", "entryLabel", "onSelect", "disabledHelpText", "nameTheOnlyEntry", "first", "empty", "entry", "graphLaunchMenuItems", "contributors", "syntenyTracks", "onShowLinear", "onShowSynteny", "widest", "LineStyle_default", "c", "locLabel", "CompareArrows_default", "track", "nodeLaunchMenuItems", "own", "reference", "highlight", "onHighlight", "HighlightAlt_default", "target", "canonicalStrand", "node", "stepName", "strand", "stepStarts", "lengths", "total", "starts", "head", "i", "tail", "j", "mismatchesByStep", "record", "byStep", "stepAt", "pos", "atEnd", "s", "start", "length", "add", "mismatch", "list", "last", "op", "parseCs", "at", "left", "take", "tubeMapReads", "graph", "records", "firstId", "nodeByName", "n", "reads", "nodes", "step", "kept", "first", "mismatches", "names", "lastLength", "k", "FORWARD_READ_COLORS", "REVERSE_READ_COLORS", "readColor", "read", "colors", "parsePanSN", "refName", "parts", "haplotype", "collectRun", "seed", "byId", "adj", "claimed", "members", "anchors", "queue", "id", "node", "neighborId", "neighbor", "anchorSpan", "a", "sorted", "b", "first", "last", "start", "end", "runDistances", "entry", "m", "dist", "member", "reach", "known", "runOffsets", "ordered", "rank", "i", "offsets", "offset", "projectAlleles", "graph", "isAnchored", "n", "buildNeighbors", "alleles", "span", "max", "MIN_DELETION_BP", "MIN_ALLELE_SPAN_FRACTION", "placeOffReference", "graph", "span", "rowY", "positions", "minSpan", "alleles", "projectAlleles", "byId", "n", "alleleDeletions", "allele", "skipped", "drawn", "scale", "nodeId", "i", "node", "offset", "even", "at", "bp", "y", "neighbors", "buildNeighbors", "queue", "fromId", "fromEnd", "nextId", "next", "rankRows", "graph", "present", "node", "a", "b", "r", "i", "anchoredLayout", "region", "backbone", "backboneNodes", "span", "referenceSpan", "rows", "nodePositions", "backbonePositions", "alleleDeletions", "placeOffReference", "rowLabels", "rank", "row", "GUTTER", "LANE_PX", "nodeWidth", "node", "compare", "a", "b", "compareKeys", "UNREACHED", "referenceOrder", "graph", "byId", "n", "key", "orderKeys", "placeByWalks", "backbone", "isNode", "startOf", "next", "prev", "i", "head", "insertAfter", "at", "id", "after", "insertBefore", "before", "path", "rising", "falling", "last", "start", "ids", "from", "leading", "lead", "placed", "placeBySearch", "adjacent", "queue", "ka", "kb", "nb", "searched", "sided", "seed", "run", "farHalf", "hops", "deadEnd", "touchesLeft", "other", "k", "e", "isBackbone", "rank", "layerGraph", "order", "isRef", "preds", "succs", "link", "layerOf", "l", "p", "s", "minSucc", "t", "lo", "hi", "layers", "orderedLayout", "layer", "layerWidth", "w", "layerX", "x", "y", "free", "ideal", "taken", "lane", "d", "nodePositions", "cx", "ny", "contributingSamples", "graph", "keep", "carried", "node", "isOffReference", "sample", "parsePanSN", "held", "row", "a", "b", "sampleRowLayout", "region", "backbone", "backboneNodes", "samples", "span", "referenceSpan", "rowOf", "s", "i", "nodePositions", "backbonePositions", "spareRow", "alleleDeletions", "placeOffReference", "stable", "rowLabels", "emptyTrackShapes", "DEBUG", "DEFAULT_TRACK_COLORS", "configFrom", "options", "track", "config", "bed", "nodes", "tracks", "reads", "nodeMap", "nodesPerOrder", "nodeOrders", "assignments", "extraLeft", "extraRight", "maxOrder", "shapes", "emptyTrackShapes", "imageBounds", "trackForRuler", "coarsenedEdgeMeta", "layoutTubeMap", "inputNodes", "inputTracks", "inputReads", "copies", "deepCopy", "node", "i", "hadInputReads", "filterReads", "t", "straightenTrack", "generateNodeMap", "generateTrackIndexSequences", "generateNodeWidth", "generateNodeSuccessors", "generateNodeOrder", "reverseReversedReads", "mergeNodes", "generateNodeDegree", "getMaxOrder", "switchNodeOrientation", "coarsenHaplotypes", "rulerIndex", "refIndex", "altHaplotypes", "_", "bands", "buildCoarsenedSyntheticBands", "calculateTrackWidth", "generateLaneAssignment", "addTrackFeatures", "drawCoarsenedReads", "generateReadOnlyNodeAttributes", "placeReads", "generateNodeXCoords", "generateSVGShapesFromPath", "getImageDimensions", "generateTrackColor", "highlight", "generateTrackAlpha", "val", "isReverse", "nodeName", "s", "forward", "reverse", "flip", "isForwardIndex", "n", "index", "nodesToInvert", "currentSequence", "cur", "firstForwardIndex", "j", "orderY", "setMapToMax", "step", "map", "key", "value", "existing", "READ_WIDTH", "assignReadsToNodes", "read", "idx", "firstNode", "element", "pathIdx", "elemNode", "generateBasicPathsForReads", "sortedNodes", "compareNodesByOrder", "readsBySource", "source", "bucket", "allSources", "b", "topMargin", "placeReadSet", "bottomY", "calculateBottomY", "elementsWithoutNode", "len", "prevSegment", "lastValidPrev", "seg", "nextSegment", "firstValidNext", "previousVisitToNode", "nextVisitToNode", "betweenCycleReverseTraversal", "compareNoNodeReads", "segment", "readIDs", "toPlace", "incomingReads", "readID", "outgoingReads", "internalReads", "startY", "incomingKeys", "entry", "path", "decisionStep", "foundY", "k", "y", "compareIncomingReadKeys", "currentY", "occupiedUntil", "readElement", "setOccupiedUntil", "maxY", "compareReadOutgoingSegmentsByGoingTo", "occupiedFrom", "firstNodeOffset", "occUntil", "incReadElementIndices", "incRead", "incReadPathElement", "compareInternalReads", "readIdx", "currentRead", "finalNodeCoverLength", "heightIncrease", "adjustVertically3", "a", "pathIndex", "readA", "readB", "segmentA", "segmentB", "prevNodeA", "prevNodeB", "aPrev", "bPrev", "readIndexA", "pathIndexA", "readIndexB", "pathIndexB", "previousValidYA", "previousValidYB", "lastPathIndexA", "lastPathIndexB", "initialNodeA", "initialNodeB", "nodeA", "nodeB", "nextNodeIdx", "beginDiff", "compareTrackByInitialOrdering", "trackA", "trackB", "pathA", "pathB", "AIndex", "BIndex", "trackAOrder", "trackBOrder", "idxA", "idxB", "aFirst", "bFirst", "aLast", "bLast", "currentNodeIndex", "currentNodeIsForward", "currentNode", "previousNode", "previousNodeIsForward", "pos", "sequenceNew", "reverseMismatches", "nodeByName", "temp", "firstLen", "lastLen", "mismatches", "sequenceLength", "mm", "getReverseComplement", "COMPLEMENT", "result", "tracksOrReads", "rawNodeName", "fwdIdx", "switched", "nodeIndex", "NODE_HORIZONTAL_SLACK", "bounds", "successorSets", "predecessorSets", "addEdges", "current", "follower", "generateNodeOrderOfSingleTrack", "sequence", "forwardOrder", "backwardOrder", "minOrder", "order", "increaseOrderForAllNodes", "generateNodeOrderTrackBeginning", "anchorIndex", "anchorSeqVal", "currentOrder", "increment", "UNREACHABLE_ORDER", "fillUnassignedOrders", "orders", "filled", "modifiedSequence", "rightIndex", "leftIndex", "tracksAndReads", "reachability", "uninvert", "increaseOrderForSuccessors", "isSuccessor", "finalOrders", "first", "second", "scratch", "stamp", "generation", "stack", "childIndex", "max", "v", "amount", "startingNode", "tabuNode", "newOrder", "increasedOrders", "queue", "head", "currentNodeOrder", "successor", "predecessor", "pivotPath", "countPaths", "switchNodeOrientationForPaths", "paths", "toSwitch", "pivotNames", "prevOrder", "nextOrder", "addToSwitchScore", "score", "scores", "delta", "currentX", "nextX", "extra", "calculateExtraSpace", "leftSideEdges", "rightSideEdges", "fallAngleAdjustment", "prevSeg", "yDifference", "segmentNumber", "prevSegmentPerOrderPerTrack", "assignmentByOrderAndNode", "trackNo", "addToAssignment", "generateSingleLaneAssignment", "segmentID", "assignment", "getIdealLanesAndCoords", "currentLane", "potentialAdjustmentValues", "prevNameIsNull", "prevTrack", "compareByIdealLane", "adjustVertically", "verticalAdjustment", "minAdjustmentCost", "moveBy", "getVerticalAdjustmentCost", "adjustBy", "assignmentNode", "aNode", "nodeStart", "nodeEnd", "feature", "line", "allAreFour", "NARROW_WIDTH", "WIDE_WIDTH", "getReadXStart", "offset", "clampedXCoordinateOfBaseWithinNode", "getReadXEnd", "cover", "getXCoordinateOfBaseWithinNode", "base", "nodeLeftX", "nodeRightX", "nodePixelCoordinatesInX", "clamped", "xStart", "xEnd", "yStart", "yEnd", "trackColor", "trackAlpha", "dummy", "reversalFlag", "orderStartX", "orderEndX", "colorHighlight", "colorForCurrentHighlight", "createFeatureRectangle", "generateTurnaround", "COARSENED_ID_BASE", "buildCoarsenedSyntheticBands", "source", "unit", "coarsenedEdgeMeta", "edges", "ignoreStrand", "config", "item", "seq", "i", "sSigned", "dSigned", "sIdx", "dIdx", "srcNode", "nodes", "dstNode", "key", "a", "b", "existing", "maxEdgeCount", "e", "BAND_MIN_WIDTH", "READ_WIDTH", "BAND_MAX_WIDTH", "widthForCount", "c", "synthetic", "edge", "id", "COARSENED_ID_BASE", "label", "srcLen", "tallestName", "tallestH", "n", "DEBUG", "createFeatureRectangle", "node", "nodeXStart", "nodeXEnd", "highlight", "track", "rectXStart", "yStart", "trackColor", "reversalFlag", "currentHighlight", "co", "featureXStart", "featureXEnd", "nodeWidth", "feature", "generateTrackColor", "shapes", "MIN_BEND_WIDTH", "generateTurnaround", "dir", "x", "yEnd", "trackWidth", "trackID", "order", "type", "trackName", "extra", "extraRight", "extraLeft", "offset", "apex", "radius", "stem", "yTop", "yBottom", "stubNear", "stubFar", "horizontal", "segTop", "inner", "outer", "d", "nodePixelCoordinatesInX", "nodeLeftX", "nodeRightX", "generateNodeWidth", "charWidth", "len", "mergeNodes", "nodeName", "nodeName2", "predSets", "succSets", "tracksAndReads", "reads", "tracks", "isReverse", "forward", "nodeMap", "reverse", "pred", "s", "succ", "sortedNodes", "compareNodesByOrder", "mergeOffset", "mergeOrigin", "offsetOf", "originOf", "origin", "predecessor", "mergeableWithPred", "nodeByName", "read", "sequenceNew", "mergeableWithSucc", "predLength", "mismatch", "firstEntry", "firstOffset", "donor", "nodeIndex", "index", "predecessorIndex", "successor", "successorIndex", "filterReads", "readList", "focusNames", "groupBy", "items", "key", "groups", "item", "k", "group", "compareCurvesByXYStartValue", "a", "b", "curvePaths", "curves", "type", "groupedCurves", "curve", "curveGroup", "adjustValue", "adjustIncrement", "xAdjusted", "xNextAdjusted", "d", "groupKeys", "aParts", "bParts", "a0", "a1", "b0", "b1", "flattenedGroups", "nodeOutlinePath", "node", "x", "y", "visitStrands", "graph", "strands", "segment", "visits", "visit", "key", "list", "tubeMapTracks", "nodeById", "n", "paths", "path", "index", "reference", "p", "pathOrigin", "visitName", "seen", "sequence", "id", "node", "k", "strand", "canonicalStrand", "stepName", "tubeMapNodes", "hasTubeMapPaths", "STACK_PX", "MIN_TUBE_PX", "MAX_TUBE_PX", "tubeWidth", "trackCount", "runTubeMap", "tracks", "reads", "tubeMapReads", "tubeColor", "pathGreyCssColor", "pathCssColor", "pathColors", "_", "layout", "layoutTubeMap", "track", "readColor", "drawnNodes", "nodes", "centreline", "yOffset", "x0", "x1", "y", "extentOf", "tubeMapLayout", "run", "nodePositions", "minY", "maxY", "tubeMapColumns", "byOrder", "columns", "end", "order", "a", "b", "bp0", "bp1", "graphNode", "isBackbone", "first", "c", "column", "pathsReachBackbone", "tubeMapReferenceLayout", "sampleOf", "path", "panSNSample", "labelOf", "stepsOf", "pieces", "lengthOf", "steps", "end", "piece", "bp", "sum", "id", "stepSpans", "spans", "pos", "len", "sliceBetween", "span", "region", "flanked", "i0", "i1", "bestEnd", "bestStart", "i", "s", "ids", "walkRows", "graph", "unit", "paths", "isReference", "p", "pathOrigin", "referencePieces", "a", "first", "others", "walks", "n", "referenceStart", "cut", "rowOf", "ordered", "b", "complete", "from", "to", "contig", "panSNContig", "axis", "runs", "offReferenceBp", "gapBp", "step", "last", "at", "forward", "backward", "READOUT_ROOM", "walkRowsExtent", "walks", "longest", "row", "walkRowLayout", "graph", "region", "backbone", "backboneNodes", "walkRows", "overWindow", "n", "overlapsWindow", "nodePositions", "backbonePositions", "rowLabels", "FORCE_LAYOUT_LABEL", "hasBackbone", "graph", "isBackbone", "hasWalks", "hasTubeMapBackbone", "hasTubeMapPaths", "hasAlleles", "isOffReference", "LAYOUT_MODES", "anchoredLayout", "sampleRowLayout", "walkRowLayout", "orderedLayout", "tubeMapLayout", "tubeMapReferenceLayout", "LAYOUT_MODE_VALUES", "m", "modeUsesLayoutEngine", "value", "layoutModeByValue", "REPEAT_GROWTH", "coveringRepeat", "b", "repeats", "span", "growth", "r", "SUPERBUBBLE_SEGMENTS", "formatBp", "bp", "formatBpRange", "shortest", "longest", "low", "high", "unit", "routes", "count", "SATURATED_PATH_COUNT", "classifyBubble", "c", "classifyShape", "refSpan", "pathCount", "segmentCount", "inversion", "range", "repeat", "name", "alleles", "BUBBLE_KIND_COLORS", "BUBBLE_KIND_NAMES", "bubbleSegmentIds", "facetGrid", "count", "bounds", "pixelRows", "width", "room", "columns", "tile", "across", "rows", "panelWidth", "panelRoom", "fitX", "fitY", "scale", "drawn", "height", "best", "grid", "facetSettingOf", "facet", "raw", "domainFirst", "names", "domain", "rank", "name", "i", "listed", "a", "b", "facetCells", "by", "wrapOrder", "wrap", "places", "sample", "haplotype", "contig", "n", "p", "cells", "el", "tag", "attrs", "children", "c", "escapeText", "s", "escapeAttr", "serializeEl", "node", "v", "k", "ON_REFERENCE", "OFF_REFERENCE", "OUTSIDE_CUT", "GENE_INK", "BAR_PX", "GAP_PX", "MIN_TILE_PX", "kb", "bp", "units", "unit", "walkRowReadout", "row", "reference", "call", "gapBp", "complete", "delta", "against", "outside", "called", "readoutPlacement", "text", "endX", "width", "x", "unitTicks", "pxPerBp", "ticks", "k", "runPaint", "run", "ramp", "REFERENCE_RAMP_ALT_CSS", "stops", "rampStops", "rampHueCss", "alongRow", "axis", "start", "end", "rowSpan", "placeRowGenes", "rows", "byRow", "placed", "genes", "clip", "g", "e", "a", "b", "GENE_FONT_PX", "GENE_CHAR_PX", "rowGeneBoxes", "X", "y", "boxes", "taken", "named", "w", "half", "at", "box", "rowsText", "n", "walkRowsKey", "bars", "o", "entries", "CALL_TICK", "UNBACKED_TICK", "DISAGREES", "walkRowsKeyTree", "parts", "swatch", "el", "coalesceRuns", "runs", "minBp", "out", "bucket", "flush", "on", "off", "r", "onReference", "lead", "held", "geneTree", "walkRowsTree", "frame", "rowPx", "barPx", "Y", "origin", "along", "offset", "prefix", "rowTree", "i", "paint", "h", "id", "rect", "color", "readouts", "import_colorBits", "import_canvas2dUtils", "import_renderingBackendBase", "Canvas2DRenderer", "width", "height", "batch", "factors", "edgeIndex", "factor", "transform", "clearColor", "ctx", "t", "hl", "color", "items", "groupByPaint", "e", "i", "inRun", "brightenAbgr", "first", "c", "lifted", "nodeId", "run", "s", "p0", "l", "p", "a", "tip", "left", "notch", "right", "arrowheadOutline", "ARROW_NOTCH", "ux", "uy", "tipX", "tipY", "length", "halfWidth", "backX", "backY", "notchBack", "paintOf", "groups", "deferred", "item", "weight", "last", "into", "key", "group", "import_colorBits", "WALK_FIELDS", "WALK_SCHEMES", "FAMILIES", "isField", "v", "f", "isScheme", "s", "resolveEncoding", "layer", "reference", "picked", "alone", "askedField", "askedScheme", "field", "family", "scheme", "BASE_T", "toLinear", "c", "toGamma", "oklab", "hex", "r", "g", "b", "i", "l", "m", "oklabToRgb", "L", "a", "schemeRgb", "x", "hslToRgb", "REFERENCE_RAMP_MAX_HUE", "from", "to", "schemeCss", "schemeColor", "encodedColor", "t", "NO_VALUE_COLOR", "REFERENCE_RAMP_ALT_COLOR", "encodingStops", "_", "encodingSwatchCss", "encoding", "stops", "rangeText", "contig", "start", "end", "s", "e", "groupDigits", "formatBp", "walkKey", "walk", "reference", "field", "own", "scale", "spanned", "outside", "walkPosition", "nodeId", "length", "progress", "range", "PAD_PX", "MIN_ROW_PX", "LABELLED_ROW_PX", "READOUT_ROOM", "MARK_INK", "TICK_PX", "stripMarks", "graph", "rows", "node", "visits", "marks", "row", "axis", "path", "pathOrigin", "v", "start", "end", "alongRow", "spanCache", "spansOf", "name", "byName", "lengthOf", "n", "pieces", "p", "spans", "piece", "a", "b", "pos", "id", "len", "segmentAt", "offset", "pxPerBp", "at", "tol", "bar", "from", "to", "lo", "hi", "mid", "best", "bestLen", "bestHolds", "i", "holds", "walkStripFrame", "bars", "o", "count", "rowPx", "labelled", "readouts", "barPx", "longest", "r", "left", "scaleX", "stripRowAt", "frame", "x", "y", "walkMarksTree", "index", "X", "el", "m", "w", "tick", "top", "import_colorBits", "REFERENCE_STRIP_PX", "REFERENCE_STRIP_ZONE_PX", "OVERHANG_PX", "OVERHANG_CAP_PX", "MIN_STAGGER_PX", "referenceStripBlocks", "graph", "colorScheme", "referenceRamp", "walks", "range", "computeColorSchemeRange", "out", "node", "index", "isBackbone", "colors", "w", "lane", "LIFT_BACKDROP_CSS", "getNodeColor", "b", "span", "frame", "bp0", "bp1", "a", "HIT_SLOP_PX", "stripOverhang", "blocks", "width", "lo", "hi", "below", "above", "block", "stripBlockAt", "sx", "sy", "hit", "best", "x0", "x1", "dist", "nodeAnchor", "segments", "toScreen", "p", "stripPixels", "dpr", "placed", "px0", "claimed", "tierEnds", "px1", "start", "tier", "_", "drawOverhang", "ctx", "edgeX", "inward", "ink", "capX", "tipX", "baseX", "rowEdge", "row", "rows", "drawReferenceStrip", "lit", "darkMode", "staggered", "color", "i", "y0", "overhang", "left", "referenceBoxes", "graph", "layout", "nodeById", "n", "byRefName", "node", "graphNode", "isBackbone", "refName", "start", "boxes", "a", "b", "tubeX", "bp", "isEnd", "lo", "hi", "mid", "bp1", "box", "span", "tubeSpan", "end", "rulerBoxes", "best", "TICK_PX", "LABEL_GAP_PX", "TARGET_TICK_PX", "MIN_BRACKET_PX", "rulerInk", "darkMode", "niceStep", "raw", "pow", "m", "placeLabels", "candidates", "placed", "text", "at", "scaleMarks", "x", "width", "measure", "first", "last", "from", "to", "bpSpan", "step", "ticks", "positions", "sx", "boxMarks", "shown", "s0", "s1", "label", "position", "spans", "lengths", "boundaries", "length", "formatBp", "ends", "rulerMarks", "referenceAxis", "drawTubeMapRuler", "ctx", "frame", "top", "marks", "ink", "y", "x0", "x1", "referenceNodes", "graph", "layout", "nodeById", "n", "out", "node", "graphNode", "isBackbone", "tubeMapConnectors", "nodes", "bpToScreen", "tubeToScreen", "width", "bottom0", "bottom1", "HIT_SLOP_PX", "connectorAt", "connectors", "zoneBottom", "sx", "sy", "t", "hit", "best", "c", "a", "b", "dist", "band", "ctx", "bottom", "sides", "drawTubeMapConnectors", "highlightNode", "darkMode", "ink", "lit", "boxesOf", "graph", "layout", "nodeById", "n", "boxes", "node", "index", "g", "isBackbone", "bp0", "a", "b", "boxAt", "bp", "visited", "lo", "hi", "mid", "box", "deviationMarks", "deviations", "marks", "track", "devs", "segment", "d", "bp1", "at", "deviationInk", "darkMode", "drawDeviationMarks", "ctx", "x", "y", "width", "m", "left", "right", "top", "readMismatches", "read", "layout", "marks", "entries", "entry", "i", "nodeIndex", "forward", "node", "pathIndex", "y", "base", "at", "pos", "getXCoordinateOfBaseWithinNode", "mm", "x0", "softClip", "x1", "tubeMapMismatches", "parsePath", "d", "tokens", "commands", "i", "num", "op", "extentOf", "x0", "x1", "c", "shapeOf", "color", "alpha", "track", "layerOf", "layout", "type", "shapes", "trackOf", "s", "rects", "r", "curves", "curvePaths", "corners", "tubeMapPicture", "nodes", "node", "nodeOutlinePath", "tubeMapMismatches", "m", "trace", "ctx", "x", "y", "cx", "cy", "fillByColor", "items", "visible", "colorOf", "addTo", "byColor", "item", "key", "list", "FULL_OUTLINE_PX", "MIN_OUTLINE_ALPHA", "outlineAlpha", "screenWidth", "NODE_TINT_ALPHA", "drawTubeMap", "picture", "frame", "width", "layer", "left", "top", "stroke", "fill", "lit", "tint", "drawMismatches", "MISMATCH_FONT_PX", "MIN_MISMATCH_FONT_PX", "mismatchesLegible", "yScale", "mismatchOnScreen", "marks", "darkMode", "fontPx", "tubeMapGenes", "byRefName", "genes", "out", "gene", "boxes", "first", "last", "start", "end", "tubeSpan", "e", "GENE_ROW_PX", "MAX_GENE_ROWS", "EXON_PX", "NAME_FONT", "NAME_GAP_PX", "tubeMapGeneRows", "edges", "g", "a", "b", "depth", "most", "step", "drawTubeMapGenes", "ctx", "frame", "bottom", "top", "x", "width", "darkMode", "rows", "rowEnds", "ink", "placed", "s0", "s1", "arrow", "label", "mid", "half", "left", "right", "row", "glyphY", "e0", "agrees", "walkBp", "callBp", "nearest", "walks", "calls", "W", "C", "cost", "paired", "i", "j", "together", "dropWalk", "dropCall", "out", "byLength", "items", "bp", "item", "a", "b", "backed", "call", "scored", "row", "pairSample", "rows", "taken", "c", "unphased", "ranked", "r", "stages", "pool", "group", "free", "open", "pick", "k", "withCalls", "bySample", "sample", "called", "import_jsx_runtime", "Settings_default", "_jsx"]
3
+ "sources": ["global-externals:@mui/material/SvgIcon", "global-externals:@jbrowse/core/util/colorBits", "global-externals:mobx", "global-externals:@jbrowse/render-core/canvas2dUtils", "global-externals:@jbrowse/render-core/renderingBackendBase", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/utils/createSvgIcon.mjs", "../../packages/core/src/labelLayout.ts", "../../packages/core/src/renderer/GeometryBuilder.ts", "../../packages/core/src/renderer/colorBits.ts", "../../packages/core/src/referenceSpan.ts", "../../packages/core/src/util/edgeCurves.ts", "../../packages/core/src/referenceRampCss.ts", "../../src/launchFromGraph/contributors.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/CompareArrows.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/HighlightAlt.mjs", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/LineStyle.mjs", "../../src/launchFromGraph/graphMenuItems.ts", "../../packages/core/src/tubeMap/reads.ts", "../../packages/core/src/alleleProjection/projectAlleles.ts", "../../packages/core/src/layout/placeOffReference.ts", "../../packages/core/src/layout/anchoredLayout.ts", "../../packages/core/src/layout/orderedLayout.ts", "../../packages/core/src/layout/sampleRowLayout.ts", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/types.js", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/layout.js", "../../node_modules/.pnpm/@gmod+tubemap-core@0.1.0/node_modules/@gmod/tubemap-core/dist/geometry.js", "../../packages/core/src/layout/tubeMapLayout.ts", "../../packages/core/src/layout/walkRows.ts", "../../packages/core/src/layout/walkRowLayout.ts", "../../packages/core/src/layoutModes.ts", "../../packages/core/src/bubbles/classifyBubble.ts", "../../packages/core/src/facetGrid.ts", "../../packages/core/src/el.ts", "../../packages/core/src/layout/walkRowDraw.ts", "../../packages/core/src/layout/walkStrip.ts", "../../packages/core/src/renderer/Canvas2DRenderer.ts", "../../packages/core/src/walkEncoding.ts", "../../packages/core/src/walkKey.ts", "../../packages/core/src/referenceStrip.ts", "../../packages/core/src/tubeMap/axis.ts", "../../packages/core/src/tubeMap/connectors.ts", "../../packages/core/src/tubeMap/deviations.ts", "../../packages/core/src/tubeMap/mismatches.ts", "../../packages/core/src/tubeMap/draw.ts", "../../packages/core/src/tubeMap/genes.ts", "../../src/GraphGenomeView/repeats/walkCalls.ts", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/Settings.mjs"],
4
+ "sourcesContent": ["module.exports = JBrowseExports[\"@mui/material/SvgIcon\"];", "module.exports = JBrowseExports[\"@jbrowse/core/util/colorBits\"];", "module.exports = JBrowseExports[\"mobx\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/canvas2dUtils\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/renderingBackendBase\"];", "'use client';\n\nexport { createSvgIcon as default } from '@mui/material/SvgIcon';", "import {\n nodeLabelBudget,\n placeSizeLabels,\n rowLabelBox,\n sizeLabelCandidates,\n} from './graphLabels'\nimport {\n LABEL_CHAR_PX,\n LABEL_PAD,\n LABEL_PX,\n occupancy,\n placeLabels,\n} from './overlayLabels'\n\nimport type { BubbleHalo, RouteLabel } from './bubbles/bubbleHalos'\nimport type { DeletionEdge } from './deletionEdges'\nimport type { GenePin } from './genes/genePins'\nimport type { GraphLabel } from './graphLabels'\nimport type { DeletionRoutes } from './layout/deletionRoutes'\nimport type { Box, PlacedLabel } from './overlayLabels'\nimport type { AlleleDeletion, NodeSegment } from './types'\nimport type { AxisScale } from './util/geometry'\n\nexport const HALO_FACTOR = 3.4\nexport const LEGEND_INSET_PX = 6\n// routes whose own stretches are drawn on top of each other stack their chips\nconst ROUTE_STACK = 8\nconst GENE_PIN_DROP_PX = 18\n// a gene's name drops a row or two rather than vanish under a bubble's\nconst GENE_STACK = 2\n// a chip's baseline this far down is the highest it sits whole in the pane\nconst TOPMOST_BASELINE = LABEL_PX + LABEL_PAD\n\nexport interface LabelLayoutSource {\n paneWidth: number\n canvasHeight: number\n axisScale: AxisScale\n translateX: number\n translateY: number\n contigThickness: number\n legendSize: { width: number; height: number }\n // the band along the top a reference strip draws in\n referenceStripZonePx?: number\n drawnRowLabels: { label: string; y: number }[]\n bubbleHalos: BubbleHalo[]\n genePins: GenePin[]\n poppedFrom?: { label: string }\n nodePositions?: Record<string, NodeSegment[]>\n // false where the drawing is not the canvas's nodes (the tube map), so a\n // length label would sit on a box's tubes rather than beside a node\n labelsNodeSizes: boolean\n nodeLengths: Map<string, number>\n showDeletionEdges: boolean\n deletions: DeletionEdge[]\n deletionRoutes?: DeletionRoutes\n alleleDeletions: AlleleDeletion[]\n positionsVersion: number\n}\n\nexport interface LabelLayout {\n bubbles: PlacedLabel<BubbleHalo>[]\n genes: PlacedLabel<GenePin>[]\n routes: PlacedLabel<{ halo: BubbleHalo; route: RouteLabel }>[]\n sizes: GraphLabel[]\n}\n\n// How much of a gene the cut's backbone carries, when that is not all of it,\n// in the unit of the gene's length: `35 of 132.8 kb`\nexport function geneCoverageNote(pin: GenePin) {\n const length = pin.gene.end - pin.gene.start\n if (pin.covered >= 0.98) {\n return undefined\n }\n const [per, unit] =\n length >= 1_000_000\n ? [1_000_000, 'Mb']\n : length >= 1000\n ? [1000, 'kb']\n : [1, 'bp']\n const amount = (bp: number) => +(bp / per).toFixed(per === 1 ? 0 : 1)\n return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`\n}\n\n// Each gene's name under its pin, the longest gene first, with how much of\n// it the cut carries where that is not all of it\nexport function geneLabelCandidates(\n pins: GenePin[],\n screen: (p: { x: number; y: number }) => { x: number; y: number },\n contigThickness: number,\n) {\n return byExtent(pins, pin => pin.gene.end - pin.gene.start).map(pin => {\n const { x, y } = screen(pin.at)\n const note = geneCoverageNote(pin)\n return {\n item: pin,\n x,\n y: y + contigThickness + GENE_PIN_DROP_PX,\n text: note ? `${pin.gene.name} \u00B7 ${note}` : pin.gene.name,\n fallback: note ? `${pin.gene.name} \u2026` : undefined,\n stack: GENE_STACK,\n }\n })\n}\n\nfunction byExtent<T>(items: T[], extent: (item: T) => number) {\n return [...items].sort((a, b) => extent(b) - extent(a))\n}\n\n// The order is what survives a crowd: the bubble's name, then the gene's, then\n// what a deletion skips, then who takes each route, and a node's length last.\nexport function layoutLabels(m: LabelLayoutSource): LabelLayout {\n const { paneWidth: width, canvasHeight: height, translateX, translateY } = m\n const { scaleX, scaleY } = m.axisScale\n const frame = { width, height }\n const screen = (p: { x: number; y: number }) => ({\n x: p.x * scaleX + translateX,\n y: p.y * scaleY + translateY,\n })\n const reserved: Box[] = m.drawnRowLabels.map(({ label, y }) =>\n rowLabelBox(label, y * scaleY + translateY),\n )\n const stripPx = m.referenceStripZonePx ?? 0\n if (stripPx > 0) {\n reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx })\n }\n if (m.legendSize.width > 0) {\n reserved.push({\n x0: width - LEGEND_INSET_PX - m.legendSize.width,\n x1: width,\n y0: 0,\n y1: stripPx + LEGEND_INSET_PX + m.legendSize.height,\n })\n }\n if (m.poppedFrom) {\n reserved.push({\n x0: 0,\n x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,\n y0: 0,\n y1: 44,\n })\n }\n const take = occupancy(frame, reserved)\n\n // On a row layout a bubble's name goes in the band above the top row, which\n // is the reference it varies, and stays at the pane's top edge once that row\n // scrolls out. Above its own nodes, a lower row's allele would put it on the\n // reference line.\n const onRows = m.drawnRowLabels.length > 0\n const rowsTop = onRows\n ? Math.min(...m.drawnRowLabels.map(r => r.y))\n : Infinity\n const halo = m.contigThickness * HALO_FACTOR\n const byBubble = byExtent(m.bubbleHalos, h => h.members)\n const bubbles = placeLabels(\n byBubble.map(h => {\n const { x, y } = screen({\n x: h.labelAt.x,\n y: Math.min(h.labelAt.y, rowsTop),\n })\n const baseline = y - halo / 2 - 6\n return {\n item: h,\n x,\n y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,\n text: h.label,\n }\n }),\n frame,\n take,\n )\n\n const genes = placeLabels(\n geneLabelCandidates(m.genePins, screen, m.contigThickness),\n frame,\n take,\n )\n const sizeCandidates =\n m.nodePositions && m.labelsNodeSizes\n ? sizeLabelCandidates({\n nodePositions: m.nodePositions,\n nodeLengths: m.nodeLengths,\n deletions: m.showDeletionEdges ? m.deletions : [],\n deletionRoutes: m.deletionRoutes,\n alleleDeletions: m.alleleDeletions,\n axis: m.axisScale,\n translateX,\n translateY,\n width,\n height,\n version: m.positionsVersion,\n })\n : { deletions: [], nodes: [] }\n const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take)\n const routes = placeLabels(\n byBubble.flatMap(h =>\n h.routes.map(route => {\n const { x, y } = screen(route.at)\n return {\n item: { halo: h, route },\n x,\n y: y + 4,\n text: route.text,\n stack: ROUTE_STACK,\n }\n }),\n ),\n frame,\n take,\n )\n const nodeLabels = placeSizeLabels(\n sizeCandidates.nodes,\n take,\n nodeLabelBudget(width, height),\n )\n return {\n bubbles,\n genes,\n routes,\n sizes: [...deletionLabels, ...nodeLabels],\n }\n}\n", "import { abgrToCssRgba, 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 { DeletionRoutes } from '../layout/deletionRoutes'\nimport type { NodeWidth } from '../nodeWidths'\nimport type { Graph, GraphNode, NodeSegment } from '../types'\nimport type { WalkLift } from '../walkHighlight'\nimport type {\n Arrowhead,\n EdgeCurveBatch,\n NodeStroke,\n RenderBatch,\n Run,\n} from './types'\nimport type { AxisScale, BezierCurve } from '../util/geometry'\n\n// Colors flow through the geometry builder as ABGR-in-u32 (see colorBits.ts),\n// so a brighten or fade is integer arithmetic and a batch of a hundred thousand\n// strokes carries its colours in one number each.\nconst EDGE_DEFAULT_COLOR = packAbgr(119, 119, 119, 217) // rgb(119,119,119) ~ 0.467, alpha 0.85\nconst EDGE_PATH_FALLBACK_COLOR = packAbgr(136, 136, 136, 217) // ~0.533, alpha 0.85\n// An edge that skips reference sequence: the graph's own statement that some\n// haplotype does not carry what the backbone does. Drawn thicker than a plain\n// link, and unconditionally rather than under a colour scheme, because a\n// deletion has no node to colour \u2014 nothing else in the drawing can carry it, so\n// there is no scheme it could disagree with. See deletionEdges.ts.\n//\n// NEAR-BLACK, not red. The reference-position ramp runs hue 0 to 300 at 70%/50%,\n// and its hue-0 end is rgb(217,38,38) \u2014 the old rgb(214,39,40) was that colour\n// to within three points, so at the start of any region the deletion arc and the\n// backbone under it were the same red (review: \"the 'red' coloring is too close\n// to the rainbow\"). Nothing else in the drawing is near-black: nodes are ramp\n// hues or the off-reference charcoal, plain links are mid grey. Weight and\n// darkness carry it instead of a hue, which is what keeps hue meaning reference\n// position and only that.\nconst EDGE_DELETION_COLOR = packAbgr(24, 24, 28, 240)\nconst DELETION_THICKNESS_FACTOR = 2.2\n// A lifted walk's links keep their plain ink, since the walk's lanes in the\n// nodes carry it, and the rest fades. The alpha they fade to keeps every colour\n// scheme's hues, only dimmer, on any background.\nexport const LIFT_BACKDROP_COLOR = packAbgr(160, 160, 160, 255)\nexport const FADED_ALPHA = 0.18\n// The same fade as css, for what paints outside the batch: the strip, the\n// keys, a tube map's tubes\nexport const LIFT_BACKDROP_CSS = abgrToCssRgba(\n fadeAbgr(LIFT_BACKDROP_COLOR, FADED_ALPHA),\n)\n// Dash period in screen px, so a dashed arc looks the same at any zoom. Dashes\n// are geometry rather than a stroke style, because only one of the two backends\n// has one; see dashCurves.\nconst DELETION_DASH_PX = 11\n\n// A node drawn under `drawPaths` is split into one lengthwise stripe per path in\n// the graph, in the legend's own order, and a path that does not visit the node\n// leaves its slot empty. The slot is what makes carriage readable: an absence\n// lands in the same place on every node, so \"which sample skips this segment\"\n// is a gap at a fixed height rather than something to be counted.\n//\n// Only the edges used to carry this, and how many of a graph's paths resolved\n// there was a function of how long the edge happened to be DRAWN: a kilobase\n// deletion is one long edge and separates all five strokes, while a 1 bp SNP\n// bubble is an edge a few px long and shows a speck (review: \"only deletion\n// really shows paths clearly, the edges between nodes are too small to see the\n// paths ... even coloring the length of the nodes using the per-sample colors\").\n// A node is drawn at its own length, which is the one thing in the drawing that\n// does not shrink to a joint.\n//\n// How many paths is too many to colour at all is not decided here \u2014 it is one\n// rule over the stripes, the ribbons and the key, so the model resolves it and\n// hands down the answer as `drawPaths`. See MAX_PATH_COLORS.\n//\n// A stripe below this many screen px is not a color, it is aliasing between\n// neighbouring stripes. Under it the node keeps its own scheme colour.\n// `contigThickness` is itself in screen px, so this bites on the path COUNT\n// rather than on the zoom.\nconst MIN_PATH_STRIPE_PX = 1.2\n// A lifted walk's lane is never thinner than this, so a node carrying three\n// lifted walks widens rather than splitting into hairlines\nconst MIN_WALK_LANE_PX = 4\n// Guards the screen-px-to-world division below against a degenerate transform.\nconst MIN_SCALE_FOR_OFFSET = 1e-6\n\n// An arrowhead's size in SCREEN px, grown from the half-width of the edge it\n// ends: the renderer expands it after the transform (see TransformUniform), so\n// a head is the same size at every zoom. A fixed 12 px head was twice the width\n// of a default 6 px node and hid the 1 bp nodes it pointed at; this one is about\n// a node wide on a default edge and grows with a deletion's or a walk's weight.\nconst ARROW_LENGTH_BASE_PX = 7\nconst ARROW_LENGTH_PER_EDGE_PX = 3\nconst ARROW_HALF_WIDTH_BASE_PX = 3\nconst ARROW_HALF_WIDTH_PER_EDGE_PX = 1.5\n// A head on an edge shorter than itself would overhang the node it leaves, so\n// it shrinks to fit, and below this fraction of its size it is dropped until a\n// zoom lengthens the edge. In a tight bubble the old heads, laid over every\n// 1 bp allele, were most of the ink.\nconst MIN_ARROW_FIT = 0.5\nconst MIN_ARROW_SCALE = 0.45\n\n// Steps per cubic when walking a curve back from its end, spaced by the square\n// of the step so they bunch where the head sits: the first is 1/144 of the\n// cubic, a few px on even a long edge.\nconst ARROW_CURVE_STEPS = 12\n// Below this an edge has no length, only the rounding of sampling one.\nconst ABUTTING_PX = 1e-6\n\n// Scratch for backFromEnd, reused across edges: this runs once per head per\n// rebuild, and objects per sample doubled the cost of a 16k-edge build.\nlet walkX = new Float64Array(64)\nlet walkY = new Float64Array(64)\nlet walkPx = new Float64Array(64)\n\n// A run of curves as points walked back from its end into the walk arrays,\n// each with its distance from the end in screen px, as far as `untilPx`.\n// Returns the point count.\nfunction backFromEnd(\n curves: BezierCurve[],\n scale: number,\n yToX: number,\n untilPx: number,\n) {\n const most = curves.length * ARROW_CURVE_STEPS + 1\n if (walkX.length < most) {\n walkX = new Float64Array(most)\n walkY = new Float64Array(most)\n walkPx = new Float64Array(most)\n }\n const last = curves[curves.length - 1]!\n walkX[0] = last.x1\n walkY[0] = last.y1\n walkPx[0] = 0\n let n = 1\n let px = 0\n for (let ci = curves.length - 1; ci >= 0 && px < untilPx; ci--) {\n const c = curves[ci]!\n for (let k = 1; k <= ARROW_CURVE_STEPS && px < untilPx; k++) {\n const t = 1 - (k / ARROW_CURVE_STEPS) ** 2\n const u = 1 - t\n const a = u * u * u\n const b = 3 * u * u * t\n const d = 3 * u * t * t\n const e = t * t * t\n const x = a * c.x0 + b * c.cx0 + d * c.cx1 + e * c.x1\n const y = a * c.y0 + b * c.cy0 + d * c.cy1 + e * c.y1\n px += Math.hypot(x - walkX[n - 1]!, (y - walkY[n - 1]!) * yToX) * scale\n walkX[n] = x\n walkY[n] = y\n walkPx[n] = px\n n++\n }\n }\n return n\n}\n\nfunction walkPointAt(n: number, px: number) {\n let i = 1\n while (i < n - 1 && walkPx[i]! < px) {\n i++\n }\n const from = walkPx[i - 1]!\n const to = walkPx[i]!\n const f = to > from ? Math.min(1, Math.max(0, (px - from) / (to - from))) : 0\n return {\n x: walkX[i - 1]! + (walkX[i]! - walkX[i - 1]!) * f,\n y: walkY[i - 1]! + (walkY[i]! - walkY[i - 1]!) * f,\n }\n}\n\n// Bandage's rule, from GraphicsItemNode::shape: a node's arrow is sized by the\n// node's own width. Here the head belongs to the edge, so the edge sets its\n// size, and the node it enters sets where the tip stops. The tip lands on that\n// node's outline, which a round cap puts `nodeHalfWidth` from the endpoint in\n// every direction; on the centreline it sank half a node deep and covered the\n// node's first few px. Only as far as the edge is long, so an abutting joint\n// keeps its head on the joint.\n//\n// A joint's head lies over the node the edge leaves, so that node's drawn\n// length is the room it fits to. A run of 1 bp nodes on a walk row otherwise\n// drew a head at every joint, a serration along the row.\n//\n// The head lies along the curve it ends, from the curve point a head-length\n// back to the tip. The end tangent alone runs along the entered node, and an\n// edge that turns in its last few px then drew a head pointing across its own\n// stroke.\nfunction arrowheadFor({\n curves,\n edgeHalfWidth,\n nodeHalfWidth,\n scale,\n yToX,\n color,\n fromSegments,\n toSegments,\n}: {\n curves: BezierCurve[]\n edgeHalfWidth: number\n nodeHalfWidth: number\n scale: number\n yToX: number\n color: number\n fromSegments: NodeSegment[]\n toSegments: NodeSegment[]\n}): Arrowhead | undefined {\n const length = ARROW_LENGTH_BASE_PX + ARROW_LENGTH_PER_EDGE_PX * edgeHalfWidth\n const n = backFromEnd(curves, scale, yToX, nodeHalfWidth + length)\n const lengthPx = walkPx[n - 1]!\n const inset = Math.min(nodeHalfWidth, lengthPx)\n const room =\n lengthPx < ABUTTING_PX\n ? polylinePx(fromSegments, scale, yToX)\n : lengthPx - inset\n const fit = Math.min(1, room / length)\n if (fit < MIN_ARROW_FIT) {\n return undefined\n }\n const tip = walkPointAt(n, inset)\n const base = walkPointAt(n, inset + length * fit)\n const dx = tip.x - base.x\n const dy = (tip.y - base.y) * yToX\n const last = curves[curves.length - 1]!\n return {\n x: tip.x,\n y: tip.y,\n angle:\n Math.hypot(dx, dy) > 0\n ? Math.atan2(dy, dx)\n : endTangent(last, yToX, intoDirection(last, toSegments)),\n length: length * fit,\n halfWidth:\n (ARROW_HALF_WIDTH_BASE_PX +\n ARROW_HALF_WIDTH_PER_EDGE_PX * edgeHalfWidth) *\n fit,\n color,\n }\n}\n\nfunction polylinePx(segments: NodeSegment[], scale: number, yToX: number) {\n let length = 0\n for (let i = 1; i < segments.length; i++) {\n length += Math.hypot(\n segments[i]!.x - segments[i - 1]!.x,\n (segments[i]!.y - segments[i - 1]!.y) * yToX,\n )\n }\n return length * scale\n}\n\n// The way into a node from the end an edge reaches it at: the direction an\n// abutting edge, which has no length of its own, points.\nfunction intoDirection(last: BezierCurve, toSegments: NodeSegment[]) {\n if (toSegments.length < 2) {\n return undefined\n }\n const first = toSegments[0]!\n const final = toSegments[toSegments.length - 1]!\n const atStart =\n Math.hypot(last.x1 - first.x, last.y1 - first.y) <=\n Math.hypot(last.x1 - final.x, last.y1 - final.y)\n return atStart\n ? { from: first, to: toSegments[1]! }\n : { from: final, to: toSegments[toSegments.length - 2]! }\n}\n\n// Per-point unit normals of a polyline, mitred at the interior joints so a\n// stripe slid along them keeps a constant distance from the node's own outline\n// where it bends.\n//\n// `yToX` puts the two axes in comparable units first (see computeEdgeCurves), so\n// the normal is perpendicular to the polyline AS DRAWN rather than to its\n// coordinates. It is 1 on an isotropic layout, where the two are the same thing.\nfunction pointNormalsOf(rawPoints: { x: number; y: number }[], yToX = 1) {\n const points =\n yToX === 1 ? rawPoints : rawPoints.map(p => ({ x: p.x, y: p.y * yToX }))\n const pointNormals: { nx: number; ny: number }[] = []\n for (let i = 0; i < points.length; i++) {\n let nx = 0\n let ny = 0\n\n if (i === 0) {\n const dx = points[1]!.x - points[0]!.x\n const dy = points[1]!.y - points[0]!.y\n const len = Math.hypot(dx, dy)\n if (len > 0) {\n nx = -dy / len\n ny = dx / len\n }\n } else if (i === points.length - 1) {\n const dx = points[i]!.x - points[i - 1]!.x\n const dy = points[i]!.y - points[i - 1]!.y\n const len = Math.hypot(dx, dy)\n if (len > 0) {\n nx = -dy / len\n ny = dx / len\n }\n } else {\n const dx1 = points[i]!.x - points[i - 1]!.x\n const dy1 = points[i]!.y - points[i - 1]!.y\n const len1 = Math.hypot(dx1, dy1)\n const dx2 = points[i + 1]!.x - points[i]!.x\n const dy2 = points[i + 1]!.y - points[i]!.y\n const len2 = Math.hypot(dx2, dy2)\n\n if (len1 > 0 && len2 > 0) {\n const nx1 = -dy1 / len1\n const ny1 = dx1 / len1\n const nx2 = -dy2 / len2\n const ny2 = dx2 / len2\n nx = (nx1 + nx2) / 2\n ny = (ny1 + ny2) / 2\n const dot = nx1 * nx + ny1 * ny\n if (dot > 0.1) {\n nx /= dot\n ny /= dot\n }\n } else if (len1 > 0) {\n nx = -dy1 / len1\n ny = dx1 / len1\n } else if (len2 > 0) {\n nx = -dy2 / len2\n ny = dx2 / len2\n }\n }\n pointNormals.push({ nx, ny })\n }\n return pointNormals\n}\n\n// The polyline shifted `distance` world units along its own normals, which is\n// the node equivalent of the constant perpendicular offset the edge path\n// strokes already take. `distance` is in x units and the normal is the drawn\n// one (pointNormalsOf), so the y component comes back out of x units here.\nfunction offsetPolyline(\n points: { x: number; y: number }[],\n normals: { nx: number; ny: number }[],\n distance: number,\n yToX = 1,\n) {\n return points.map((p, i) => ({\n x: p.x + normals[i]!.nx * distance,\n y: p.y + (normals[i]!.ny * distance) / yToX,\n }))\n}\n\nfunction packNorm(r: number, g: number, b: number, a: number) {\n return packAbgr(\n Math.round(r * 255),\n Math.round(g * 255),\n Math.round(b * 255),\n Math.round(a * 255),\n )\n}\n\nexport interface BuildOptions {\n nodePositions: Record<string, NodeSegment[]>\n graph: Graph\n nodeById: Map<string, GraphNode>\n colorScheme: ResolvedColorScheme\n contigThickness: number\n connectorThickness: number\n // Whether to paint per path \u2014 the RESOLVED answer, not the raw view prop.\n // Past MAX_PATH_COLORS a graph's paths are not drawable as colours at all and\n // the model hands over `false`, which is the one place that decides it for\n // the stripes, the ribbons and the key together. Same contract as\n // `colorScheme` above, which arrives with 'auto' already resolved.\n drawPaths: boolean\n // thicker by depth, or every node at `contigThickness`; see nodeWidths.ts\n nodeWidth?: NodeWidth\n // walks lifted out: each draws a lane of its own along the nodes it visits,\n // their links draw dark and heavy, and everything else fades to a fraction\n // of its alpha\n highlight?: WalkLift\n // Both scales together, and required. Every screen-metric constant here (dash\n // period, stripe width, arrowhead angle) divides by scaleX, and everything\n // that mixes the axes needs their ratio; taking them as one value is what\n // keeps a caller from supplying an x scale and defaulting y, which draws a\n // wrong picture and reports nothing. See AxisScale.\n axis: AxisScale\n linearLayout?: boolean\n viewportBounds?: { minX: number; minY: number; maxX: number; maxY: number }\n // What the 'reference-position' scheme paints from: each node's midpoint on\n // the reference, and the interval the hue spans. Unused by every other\n // scheme, and passed in rather than derived here for the reason `deletions`\n // below is \u2014 it costs a neighbour walk per node, it is a fact about the\n // graph, and this function runs on every pan. `computeReferenceRamp` is what\n // builds one.\n referenceRamp?: ReferenceRamp\n // links that skip reference sequence (deletionEdges()), keyed by their index\n // into graph.edges and carrying the backbone node ids they bypass. Passed in\n // rather than derived here because the model can hold it against the graph,\n // and because the same set names the hover text.\n deletions?: Map<number, string[]>\n // LayoutResult.deletionRoutes\n deletionRoutes?: DeletionRoutes\n hiddenEdges?: ReadonlySet<number>\n // Bumped when a drag moves the positions in place, which their identity\n // cannot report. Only the shared curve cache reads it; see baseEdgeCurves.\n version?: number\n}\n\nexport function hslToRgb(\n h: number,\n s: number,\n l: number,\n): [number, number, number] {\n const c = (1 - Math.abs(2 * l - 1)) * s\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = l - c / 2\n let r = 0\n let g = 0\n let b = 0\n if (h < 60) {\n r = c\n g = x\n } else if (h < 120) {\n r = x\n g = c\n } else if (h < 180) {\n g = c\n b = x\n } else if (h < 240) {\n g = x\n b = c\n } else if (h < 300) {\n r = x\n b = c\n } else {\n r = c\n b = x\n }\n return [r + m, g + m, b + m]\n}\n\n// Evenly-spaced RGB gradient stops (0-255 per channel).\nconst DEPTH_GRADIENT = [\n [68, 1, 84],\n [59, 82, 139],\n [33, 145, 140],\n [94, 201, 98],\n [253, 231, 37],\n] as const\n\n// ranks >= 1, i.e. everything off the reference backbone\nconst STABLE_RANK_GRADIENT = [\n [237, 137, 44],\n [158, 42, 122],\n] as const\n\nconst NODE_LENGTH_GRADIENT = [\n [220, 50, 50],\n [50, 120, 220],\n] as const\n\n// Sample a piecewise-linear RGB gradient; t is clamped to [0,1].\nfunction sampleGradient(\n stops: readonly (readonly [number, number, number])[],\n t: number,\n) {\n const pos = Math.max(0, Math.min(1, t)) * (stops.length - 1)\n const i = Math.min(stops.length - 2, Math.floor(pos))\n const f = pos - i\n const a = stops[i]!\n const b = stops[i + 1]!\n return packAbgr(\n Math.round(a[0] + (b[0] - a[0]) * f),\n Math.round(a[1] + (b[1] - a[1]) * f),\n Math.round(a[2] + (b[2] - a[2]) * f),\n 255,\n )\n}\n\n// Deterministic color from a string (djb2-style hash \u2192 HSL hue), for the\n// `random` node scheme. The path ribbons do NOT use this \u2014 see pathHueAt.\nfunction hashColor(str: string, alpha: number) {\n const [r, g, b] = hslToRgb(nameHue(str), PATH_SATURATION, PATH_LIGHTNESS)\n return packNorm(r, g, b, alpha)\n}\n\n// A hue ramp over reference coordinates, which is the one colouring a linear\n// track can reproduce exactly: it is a function of two stated numbers and a\n// node's reference midpoint, so `hsl(min(300,max(0,(mid-start)/span*300)),70%,50%)`\n// in a track's `color` slot paints a segment the colour the graph paints its\n// node. Stops at 300 rather than wrapping to 360, so the two ends of the window\n// are red and magenta rather than both red.\nexport const REFERENCE_RAMP_MAX_HUE = 300\nconst REFERENCE_RAMP_SATURATION = 0.7\nconst REFERENCE_RAMP_LIGHTNESS = 0.5\n// Off-reference segments come off the ramp entirely and paint one flat charcoal.\n//\n// They used to keep the hue of the reference they replace, paler and softer, on\n// the argument that the correspondence is the whole scheme. Two rounds of docs\n// review say otherwise: a pale hue is still a hue on the ramp, so an alternative\n// allele reads as reference sequence at that position rather than as sequence\n// the reference does not have \u2014 \"non-backbone parts of the bandage graph could\n// be coloured in a non-spectrum colouring\". Nothing is lost by dropping the hue,\n// because in both anchored layouts an allele's x already states which reference\n// position it attaches to, and in the force layout its position states nothing\n// to begin with.\n//\n// Distinct from the light grey a node with no reference coordinates at all gets:\n// this one is anchored and off-reference, that one is unplaceable.\nconst REFERENCE_RAMP_ALT_RGB = [60, 65, 72] as const\nexport const REFERENCE_RAMP_ALT_COLOR = packAbgr(...REFERENCE_RAMP_ALT_RGB, 255)\nexport const REFERENCE_RAMP_ALT_CSS = `rgb(${REFERENCE_RAMP_ALT_RGB.join(', ')})`\n\nexport interface ReferenceRamp {\n start: number\n span: number\n midpoints: Map<string, number>\n}\n\nexport interface ColorSchemeRange {\n minDepth: number\n maxDepth: number\n minLength: number\n maxLength: number\n maxRank: number\n nodeCount: number\n referenceRamp?: ReferenceRamp\n}\n\n// The domain is stated by the caller \u2014 the region the subgraph was cut from \u2014\n// rather than measured off the nodes, because a linear view has no way to know\n// what the cut reached: segments overrun the region at both edges, so a\n// measured domain would shift the hue of every node by an amount the other\n// panel cannot compute. With no region (a whole-file import) the drawn extent\n// is all there is, and the ramp is then only comparable to itself.\nexport function computeReferenceRamp(\n graph: Graph,\n domain: { start: number; end: number } | undefined,\n): ReferenceRamp {\n const midpoints = referenceMidpoints(graph)\n if (domain && domain.end > domain.start) {\n return {\n start: domain.start,\n span: domain.end - domain.start,\n midpoints,\n }\n }\n let min = Infinity\n let max = -Infinity\n for (const mid of midpoints.values()) {\n min = Math.min(min, mid)\n max = Math.max(max, mid)\n }\n return {\n start: min === Infinity ? 0 : min,\n span: max > min ? max - min : 1,\n midpoints,\n }\n}\n\nexport function computeColorSchemeRange(graph: Graph) {\n let minDepth = Infinity\n let maxDepth = -Infinity\n let minLength = Infinity\n let maxLength = -Infinity\n let maxRank = 0\n for (const n of graph.nodes) {\n if (n.stable !== undefined && n.stable.rank > maxRank) {\n maxRank = n.stable.rank\n }\n if (n.depth < minDepth) {\n minDepth = n.depth\n }\n if (n.depth > maxDepth) {\n maxDepth = n.depth\n }\n if (n.length < minLength) {\n minLength = n.length\n }\n if (n.length > maxLength) {\n maxLength = n.length\n }\n }\n return {\n minDepth,\n maxDepth,\n minLength,\n maxLength,\n maxRank,\n nodeCount: graph.nodes.length,\n } satisfies ColorSchemeRange\n}\n\nexport function getNodeColor(\n node: GraphNode,\n nodeIndex: number,\n colorScheme: ResolvedColorScheme,\n range: ColorSchemeRange,\n) {\n switch (colorScheme) {\n case 'random':\n return hashColor(node.id, 1)\n\n case 'depth':\n return sampleGradient(\n DEPTH_GRADIENT,\n range.maxDepth > range.minDepth\n ? (node.depth - range.minDepth) / (range.maxDepth - range.minDepth)\n : 0.5,\n )\n\n case 'node-length':\n return sampleGradient(\n NODE_LENGTH_GRADIENT,\n range.maxLength > range.minLength\n ? (node.length - range.minLength) /\n (range.maxLength - range.minLength)\n : 0.5,\n )\n\n // rGFA states the backbone rather than leaving it to be inferred: rank 0 is\n // the reference, higher ranks are the sequence that diverges from it. A\n // graph with no SR tags draws every node as unranked.\n case 'stable-rank':\n return node.stable === undefined\n ? packAbgr(160, 160, 160, 255)\n : node.stable.rank === 0\n ? packAbgr(52, 152, 219, 255)\n : sampleGradient(\n STABLE_RANK_GRADIENT,\n range.maxRank > 1\n ? (node.stable.rank - 1) / (range.maxRank - 1)\n : 0,\n )\n\n // Position on the reference, the one quantity both panels of a\n // graph-over-linear figure can state. A node with no reference position at\n // all \u2014 nothing anchored, or an allele whose flanks fell outside the cut \u2014\n // is grey rather than a hue it has not earned.\n case 'reference-position': {\n const ramp = range.referenceRamp\n const mid = ramp?.midpoints.get(node.id)\n if (ramp === undefined || mid === undefined) {\n return packAbgr(160, 160, 160, 255)\n }\n if (node.stable !== undefined && node.stable.rank > 0) {\n return REFERENCE_RAMP_ALT_COLOR\n }\n const frac = Math.max(0, Math.min(1, (mid - ramp.start) / ramp.span))\n const [r, g, b] = hslToRgb(\n frac * REFERENCE_RAMP_MAX_HUE,\n REFERENCE_RAMP_SATURATION,\n REFERENCE_RAMP_LIGHTNESS,\n )\n return packNorm(r, g, b, 1)\n }\n\n case 'grey':\n return packAbgr(160, 160, 160, 255)\n\n case 'rainbow': {\n const hue = range.nodeCount > 1 ? (nodeIndex / range.nodeCount) * 360 : 0\n const [r, g, b] = hslToRgb(hue, 0.75, 0.5)\n return packNorm(r, g, b, 1)\n }\n\n default:\n return packAbgr(52, 152, 219, 255)\n }\n}\n\n// The SCREEN angle an arrowhead points: a cubic's tangent at t=1, which runs\n// from its last control point to its endpoint, so no tessellation is needed.\n// The head is expanded from a screen-px size after the transform, so its\n// direction has to be the drawn one too, and `yToX` puts the y difference in x\n// units first.\n//\n// A control point on the endpoint states no direction there. The tangent's\n// limit as t -> 1 then runs from the FIRST control point, and failing that from\n// the start. An edge with no length at all, the abutting joint of an anchored\n// layout, has none of those, and `into` (the way into the node it enters) is\n// what is left; without it such a head pointed along +x whatever the node did.\nexport function endTangent(\n c: BezierCurve,\n yToX = 1,\n into?: { from: NodeSegment; to: NodeSegment },\n) {\n const candidates: [number, number][] = [\n [c.cx1, c.cy1],\n [c.cx0, c.cy0],\n [c.x0, c.y0],\n ]\n for (const [x, y] of candidates) {\n const dx = c.x1 - x\n const dy = (c.y1 - y) * yToX\n if (Math.hypot(dx, dy) > 0) {\n return Math.atan2(dy, dx)\n }\n }\n return into\n ? Math.atan2((into.to.y - into.from.y) * yToX, into.to.x - into.from.x)\n : 0\n}\n\n// Whether any part of a node's polyline falls inside the viewport. Testing the\n// *segments* rather than their endpoints is what makes a node wider than the\n// window visible: in the reference-anchored layouts x is in bp, so a backbone\n// segment routinely spans the whole viewport with both ends outside it, and\n// point containment culled exactly the segment the view exists to show.\n//\n// Segment bounding boxes, not exact line-rect intersection: culling only has to\n// avoid dropping something visible, and a diagonal kept by its bbox costs one\n// polyline.\nfunction isPolylineInBounds(\n segments: NodeSegment[],\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n) {\n let hit = false\n for (let i = 0; i < segments.length - 1 && !hit; i++) {\n const a = segments[i]!\n const b = segments[i + 1]!\n hit =\n Math.max(a.x, b.x) >= bounds.minX &&\n Math.min(a.x, b.x) <= bounds.maxX &&\n Math.max(a.y, b.y) >= bounds.minY &&\n Math.min(a.y, b.y) <= bounds.maxY\n }\n return hit\n}\n\nfunction isBezierInBounds(\n curves: BezierCurve[],\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n) {\n for (const c of curves) {\n const cMinX = Math.min(c.x0, c.cx0, c.cx1, c.x1)\n const cMaxX = Math.max(c.x0, c.cx0, c.cx1, c.x1)\n const cMinY = Math.min(c.y0, c.cy0, c.cy1, c.y1)\n const cMaxY = Math.max(c.y0, c.cy0, c.cy1, c.y1)\n if (\n cMaxX >= bounds.minX &&\n cMinX <= bounds.maxX &&\n cMaxY >= bounds.minY &&\n cMinY <= bounds.maxY\n ) {\n return true\n }\n }\n return false\n}\n\n// Per-graph derivations, cached on the graph: this function runs on every\n// debounced pan and zoom, and a walk over every path's every step is a fact\n// about the graph rather than about the frame.\n\n// By position in the path list, matching pathLegend's swatches exactly, so the\n// key beside the drawing names the strokes in it.\n//\n// One array plus a name lookup, rather than an array and a Map of the same\n// colours: two paths that share a name then draw the same colour on their\n// edges and on their nodes, instead of the Map keeping the last one written\n// while the array kept the first. `gfaConverter` no longer produces such a\n// pair, and this is what makes the drawing merely wrong rather than\n// inconsistent if anything ever does again.\nconst paletteCache = new WeakMap<\n Graph,\n { pathColorByIndex: number[]; pathIndexByName: Map<string, number> }\n>()\n\nfunction pathPaletteOf(graph: Graph) {\n let palette = paletteCache.get(graph)\n if (!palette) {\n const pathColorByIndex: number[] = []\n const pathIndexByName = new Map<string, number>()\n const paths = graph.paths ?? []\n for (let i = 0; i < paths.length; i++) {\n const [r, g, b] = hslToRgb(\n pathHueAt(i, paths.length),\n PATH_SATURATION,\n PATH_LIGHTNESS,\n )\n pathColorByIndex.push(packNorm(r, g, b, 0.85))\n if (!pathIndexByName.has(paths[i]!.name)) {\n pathIndexByName.set(paths[i]!.name, i)\n }\n }\n palette = { pathColorByIndex, pathIndexByName }\n paletteCache.set(graph, palette)\n }\n return palette\n}\n\n// nodeId -> the indices of the paths that visit it, i.e. which stripe slots\n// this node fills. Built from the paths themselves rather than read off the\n// node, because `GraphNode.samples` collapses a sample's haplotypes together\n// and the legend does not.\nconst slotsCache = new WeakMap<Graph, Map<string, number[]>>()\nconst NO_SLOTS: ReadonlyMap<string, number[]> = new Map()\n\nfunction pathSlotsOf(graph: Graph): ReadonlyMap<string, number[]> {\n let slots = slotsCache.get(graph)\n if (!slots) {\n slots = new Map()\n for (const [pathIdx, path] of (graph.paths ?? []).entries()) {\n for (const nodeId of new Set(path.nodeIds)) {\n const visits = slots.get(nodeId)\n if (visits) {\n visits.push(pathIdx)\n } else {\n slots.set(nodeId, [pathIdx])\n }\n }\n }\n slotsCache.set(graph, slots)\n }\n return slots\n}\n\nexport function buildGeometry(options: BuildOptions): RenderBatch {\n const {\n nodePositions,\n graph,\n nodeById,\n colorScheme,\n contigThickness,\n connectorThickness,\n drawPaths,\n nodeWidth = 'uniform',\n highlight,\n axis,\n linearLayout,\n viewportBounds,\n referenceRamp,\n deletions,\n deletionRoutes,\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 deletionRoutes,\n )\n\n const nodeStrokes: NodeStroke[] = []\n const nodeStrokeRuns = new Map<string, Run>()\n const arrows: Arrowhead[] = []\n const arrowRuns = new Map<number, Run>()\n const edgeCurves: EdgeCurveBatch[] = []\n const edgeCurveRuns = new Map<number, Run>()\n\n const colorRange = { ...computeColorSchemeRange(graph), referenceRamp }\n\n const nodeIndexMap = new Map<string, number>()\n if (colorScheme === 'rainbow') {\n for (let i = 0; i < graph.nodes.length; i++) {\n nodeIndexMap.set(graph.nodes[i]!.id, i)\n }\n }\n\n const pathCount = graph.paths?.length ?? 0\n const { pathColorByIndex, pathIndexByName } = pathPaletteOf(graph)\n const pathColorByName = (name: string) =>\n pathColorByIndex[pathIndexByName.get(name) ?? -1] ??\n EDGE_PATH_FALLBACK_COLOR\n const nodePathSlots =\n drawPaths && pathCount > 1 ? pathSlotsOf(graph) : NO_SLOTS\n\n // An arrowhead at every joint of a few hundred short nodes is a serration\n // along the whole drawing, so heads wait for a zoom where a node is longer\n // than its head.\n const showArrows = scale > (linearLayout ? 1 : MIN_ARROW_SCALE)\n\n for (let ei = 0; ei < graph.edges.length; ei++) {\n const edge = graph.edges[ei]!\n const fromSegments = nodePositions[edge.from]\n const toSegments = nodePositions[edge.to]\n const baseCurves = sharedCurves.get(ei)\n if (\n !fromSegments?.length ||\n !toSegments?.length ||\n !baseCurves ||\n hiddenEdges?.has(ei)\n ) {\n continue\n }\n\n const numPaths = edge.pathIds?.length ?? 0\n const bypassed = deletions?.get(ei)\n const isDeletion = bypassed !== undefined\n const onWalk = highlight?.edgeIndexes.has(ei) ?? false\n const edgeThickness =\n (connectorThickness / 2) * (isDeletion ? DELETION_THICKNESS_FACTOR : 1)\n // One stroke per path crossing the edge, fanned off it: the shared curve\n // slid sideways, which is also what the hit index tests against. Rebuilding\n // the curve at each offset was 40% of a striped build and differed from a\n // translation only in a deletion's bow, by the offset's few px.\n const ribbons = drawPaths && numPaths > 0\n\n // An arrowhead states which way a LINK is read, which is a property of the\n // edge and not of each path crossing it. Drawn per ribbon it says one thing\n // once per haplotype: five stacked heads at a joint, in five colours, for a\n // single fact about a single link. That is the \"coloured confetti at each\n // joint\" that made `drawPaths` unusable on a force layout, and it reached\n // the anchored layouts too the moment their abutting edges started drawing\n // again. Measured on `ecoli_pggb_subgraph.gfa`: 72 arrowheads plain, 155\n // with ribbons, over 72 links.\n //\n // So one head per edge, in the edge's own colour rather than a ribbon's,\n // since choosing a ribbon's would privilege one haplotype for a fact that\n // belongs to none of them. `undefined` means this call draws no head.\n const plainEdgeColor = isDeletion ? EDGE_DELETION_COLOR : EDGE_DEFAULT_COLOR\n const edgeColor =\n !highlight || onWalk\n ? plainEdgeColor\n : fadeAbgr(plainEdgeColor, FADED_ALPHA)\n const toNode = nodeById.get(edge.to)\n const intoHalfWidth =\n (toNode\n ? nodeWidthPx(toNode, contigThickness, nodeWidth, depthNorm)\n : contigThickness) / 2\n const buildSingleEdge = (\n offsetX: number,\n offsetY: number,\n color: number,\n arrowColor: number | undefined,\n ) => {\n const curves =\n offsetX === 0 && offsetY === 0\n ? baseCurves\n : translateCurves(baseCurves, offsetX, offsetY)\n\n if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {\n return\n }\n\n // A deletion is drawn dashed, and that is the only thing in the drawing\n // that is. Weight and darkness were carrying it alone, and they could not:\n // an off-reference node is charcoal rgb(60,65,72) and the arc is near-black\n // rgb(24,24,28), which is ~25 points a channel apart, so in a figure the\n // two dark strokes read as the same ink and the arc is the rarer of them\n // (measured on `pangenome/hprc_lpa_kiv2`: 7,411 px of charcoal node against\n // ~5,000 px of arc). Hue cannot separate them either, since hue means\n // reference position and charcoal means having none. A broken line is\n // available, unused, and says what it draws: sequence that is not there.\n if (isDeletion) {\n for (const dash of dashCurves(curves, DELETION_DASH_PX / scale)) {\n edgeCurves.push({ curves: dash, thickness: edgeThickness, color })\n }\n } else {\n edgeCurves.push({ curves, thickness: edgeThickness, color })\n }\n\n const head =\n showArrows && arrowColor !== undefined\n ? arrowheadFor({\n curves,\n edgeHalfWidth: edgeThickness,\n nodeHalfWidth: intoHalfWidth,\n scale,\n yToX,\n color: arrowColor,\n fromSegments,\n toSegments,\n })\n : undefined\n if (head) {\n arrows.push(head)\n }\n }\n\n const edgeCurveStart = edgeCurves.length\n const arrowStart = arrows.length\n\n // A lifted walk's link carries on its lane from the node it leaves, so a\n // lane runs unbroken through the joints rather than beside a grey link\n const laneWalks =\n highlight && onWalk && !ribbons ? highlight.walks : undefined\n if (laneWalks) {\n const fromNode = nodeById.get(edge.from)\n const width = fromNode\n ? nodeWidthPx(fromNode, contigThickness, nodeWidth, depthNorm)\n : contigThickness\n const laneWidth = Math.max(width / laneWalks.length, MIN_WALK_LANE_PX)\n // the lanes' sideways direction where the link leaves its node, which\n // is the end a '-' link leaves from the start of\n const normals = pointNormalsOf(fromSegments, yToX)\n const { nx, ny } = (edge.fromStrand === '-'\n ? normals[0]\n : normals.at(-1)) ?? {\n nx: 0,\n ny: 0,\n }\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n laneWalks.forEach((walk, lane) => {\n if (!walk.edgeIndexes.has(ei)) {\n return\n }\n const offset =\n (lane - (laneWalks.length - 1) / 2) * laneWidth * worldPerScreenPx\n const curves =\n offset === 0\n ? baseCurves\n : translateCurves(baseCurves, nx * offset, (ny * offset) / yToX)\n if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {\n return\n }\n edgeCurves.push({\n curves,\n thickness: laneWidth / 2,\n color:\n walk.colors.get(edge.from) ??\n walk.colors.get(edge.to) ??\n plainEdgeColor,\n })\n })\n } else if (!ribbons) {\n buildSingleEdge(0, 0, edgeColor, edgeColor)\n } else {\n const offsets = pathRibbonOffsets(\n fromSegments,\n toSegments,\n numPaths,\n axis,\n )\n // The middle ribbon carries the head, which at an odd path count is the\n // one drawn at offset 0, i.e. on the edge's own line. At an even count it\n // is half a spacing off, which beats a sixth `computeEdgeCurves` call to\n // rebuild a curve the fan is already centred on.\n const arrowRibbon = Math.floor((numPaths - 1) / 2)\n for (let pathIdx = 0; pathIdx < numPaths; pathIdx++) {\n const offset = offsets[pathIdx]!\n const ribbonColor = pathColorByName(edge.pathIds![pathIdx]!)\n buildSingleEdge(\n offset.x,\n offset.y,\n highlight && !highlight.names.has(edge.pathIds![pathIdx]!)\n ? fadeAbgr(ribbonColor, FADED_ALPHA)\n : ribbonColor,\n pathIdx === arrowRibbon ? edgeColor : undefined,\n )\n }\n }\n\n const edgeCurveCount = edgeCurves.length - edgeCurveStart\n if (edgeCurveCount > 0) {\n edgeCurveRuns.set(ei, { start: edgeCurveStart, count: edgeCurveCount })\n }\n const arrowCount = arrows.length - arrowStart\n if (arrowCount > 0) {\n arrowRuns.set(ei, { start: arrowStart, count: arrowCount })\n }\n }\n\n for (const [nodeId, segments] of Object.entries(nodePositions)) {\n const node = nodeById.get(nodeId)\n if (!node) {\n continue\n }\n\n if (viewportBounds && !isPolylineInBounds(segments, viewportBounds)) {\n continue\n }\n\n const faded = highlight !== undefined && !highlight.nodeIds.has(nodeId)\n // Under lifted walks the lanes carry every colour, so the rest fades in\n // grey rather than in a scheme no lane is drawn in\n const own = faded\n ? LIFT_BACKDROP_COLOR\n : getNodeColor(\n node,\n nodeIndexMap.get(nodeId) ?? 0,\n colorScheme,\n colorRange,\n )\n const color = faded ? fadeAbgr(own, FADED_ALPHA) : own\n const width = nodeWidthPx(node, contigThickness, nodeWidth, depthNorm)\n const nodeThickness = width / 2\n\n const start = nodeStrokes.length\n // The node's drawn width is the same either way: the stripes divide it\n // rather than inflate it, so turning paths on does not re-weight the\n // drawing against the edges and the layout underneath it.\n //\n // A thickness is SCREEN px and a point is world units. So the slot width is\n // screen px and the sideways shift that places a stripe has to be taken\n // back into world units, or the stripes fan apart as you zoom in and\n // collapse into one as you zoom out.\n const slots = nodePathSlots.get(nodeId)\n const slotWidth = width / pathCount\n const drawable = segments.length >= 2\n // Each lifted walk keeps one lane across the whole drawing, so a lane\n // missing from a node is a walk that does not visit it\n const lanes =\n highlight && !faded && !slots?.length ? highlight.walks : undefined\n if (drawable && lanes) {\n const laneWidth = Math.max(width / lanes.length, MIN_WALK_LANE_PX)\n const normals = pointNormalsOf(segments, yToX)\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n lanes.forEach((walk, lane) => {\n const laneColor = walk.colors.get(nodeId)\n if (laneColor === undefined) {\n return\n }\n const offset =\n (lane - (lanes.length - 1) / 2) * laneWidth * worldPerScreenPx\n nodeStrokes.push({\n points:\n lanes.length === 1\n ? segments\n : offsetPolyline(segments, normals, offset, yToX),\n thickness: laneWidth / 2,\n color: laneColor,\n })\n })\n } else if (drawable && slots?.length && slotWidth >= MIN_PATH_STRIPE_PX) {\n const normals = pointNormalsOf(segments, yToX)\n const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET)\n for (const slot of slots) {\n const offset =\n (slot - (pathCount - 1) / 2) * slotWidth * worldPerScreenPx\n const stripe = pathColorByIndex[slot] ?? EDGE_PATH_FALLBACK_COLOR\n nodeStrokes.push({\n points: offsetPolyline(segments, normals, offset, yToX),\n thickness: slotWidth / 2,\n // a node off the lifted walk fades whichever way it is painted\n color: faded ? fadeAbgr(stripe, FADED_ALPHA) : stripe,\n })\n }\n } else if (drawable) {\n nodeStrokes.push({ points: segments, thickness: nodeThickness, color })\n }\n const count = nodeStrokes.length - start\n if (count > 0) {\n nodeStrokeRuns.set(nodeId, { start, count })\n }\n }\n\n return {\n nodeStrokes,\n nodeStrokeRuns,\n arrows,\n arrowRuns,\n edgeCurves,\n edgeCurveRuns,\n }\n}\n", "import {\n abgrAlpha,\n abgrBlue,\n abgrGreen,\n abgrRed,\n packAbgr,\n} from '@jbrowse/core/util/colorBits'\n\n// The one ABGR helper core does not have. Everything else the renderer needs \u2014\n// the packing, the channel accessors, both css formatters \u2014 comes straight from\n// `@jbrowse/core/util/colorBits`, which ReExports/list.ts now names, so the\n// plugin bundle binds it to the host's copy. The plugin targets the latest\n// JBrowse beta; 5.0.0-beta.4 serves that module without `packAbgr`, and the\n// plugin fails to install there.\n\n// Scale a packed color's channels, clamped at full brightness and leaving alpha\n// alone. factor === 1 returns the color unchanged.\n// The same colour at a fraction of its alpha, so a faded node reads as the\n// same ink through it on any background.\nexport function fadeAbgr(c: number, alpha: number) {\n return packAbgr(\n abgrRed(c),\n abgrGreen(c),\n abgrBlue(c),\n Math.round(abgrAlpha(c) * alpha),\n )\n}\n\nexport function brightenAbgr(c: number, factor: number) {\n return packAbgr(\n Math.min(255, Math.round(abgrRed(c) * factor)),\n Math.min(255, Math.round(abgrGreen(c) * factor)),\n Math.min(255, Math.round(abgrBlue(c) * factor)),\n abgrAlpha(c),\n )\n}\n", "import { isBackbone } from './anchoredNodes'\n\nimport type { Graph, GraphNode } from './types'\n\n// How long an allele chain can be and still be located from both ends. An\n// allele is a run of off-reference segments between two backbone flanks, so the\n// two flanks sit at *different* hop depths from any segment in the middle and\n// the walk has to keep going after it finds the first one. Minigraph alleles are\n// short runs, so this reaches both flanks throughout the fixtures; a longer run\n// degrades to the flanks it did reach rather than to nothing.\nconst MAX_BACKBONE_HOPS = 4\n\nexport interface ReferenceSpan {\n start: number\n end: number\n}\n\n// Undirected adjacency over node ids. Edges carry the topology in both\n// directions for this purpose: finding the backbone a node hangs off doesn't\n// care which way the L line was written.\nexport function buildNeighbors(graph: Graph) {\n const neighbors = new Map<string, string[]>()\n function add(from: string, to: string) {\n const list = neighbors.get(from)\n if (list) {\n list.push(to)\n } else {\n neighbors.set(from, [to])\n }\n }\n for (const edge of graph.edges) {\n add(edge.from, edge.to)\n add(edge.to, edge.from)\n }\n return neighbors\n}\n\n// The reference interval an allele branches into: between the end of the\n// backbone segment upstream of it and the start of the one downstream.\n// `[min(ends), max(starts)]` picks those two out of the reached flanks without\n// needing to know which is which, and it degrades usefully in both directions \u2014\n// a pure insertion, whose flanks abut, collapses to the single reference point\n// it is inserted at, and a bubble with only one flank inside the window yields\n// that flank's own span.\nfunction flankingSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}) {\n const seen = new Set([nodeId])\n let frontier = [nodeId]\n let minEnd = Infinity\n let maxStart = -Infinity\n\n // Backbone nodes are terminal \u2014 they are collected, never queued \u2014 so the walk\n // cannot cross the reference into a neighbouring bubble. It stays inside the\n // allele it started in and stops on its own at the flanks.\n for (let hop = 0; hop < MAX_BACKBONE_HOPS && frontier.length > 0; hop++) {\n const next: string[] = []\n for (const id of frontier) {\n const adjacent = neighbors.get(id)\n if (adjacent) {\n for (const neighborId of adjacent) {\n if (!seen.has(neighborId)) {\n seen.add(neighborId)\n const neighbor = nodeById.get(neighborId)\n if (neighbor && isBackbone(neighbor)) {\n minEnd = Math.min(minEnd, neighbor.stable.start + neighbor.length)\n maxStart = Math.max(maxStart, neighbor.stable.start)\n } else {\n next.push(neighborId)\n }\n }\n }\n }\n }\n frontier = next\n }\n\n return minEnd === Infinity\n ? undefined\n : {\n start: Math.min(minEnd, maxStart),\n end: Math.max(minEnd, maxStart),\n }\n}\n\n// The reference interval to highlight for a node, in the coordinates of the\n// stable sequence the subgraph was cut from.\n//\n// A rank-0 node states its own, so the interval is exact. A rank>0 allele has\n// none: its SO is an offset on a *different* stable sequence and is not\n// comparable to the reference axis (see the axis note in\n// layout/anchoredLayout.ts), so it is located by the backbone it branches from\n// instead.\nexport function nodeReferenceSpan({\n nodeId,\n nodeById,\n neighbors,\n}: {\n nodeId: string\n nodeById: Map<string, GraphNode>\n neighbors: Map<string, string[]>\n}): ReferenceSpan | undefined {\n const node = nodeById.get(nodeId)\n return node === undefined\n ? undefined\n : isBackbone(node)\n ? { start: node.stable.start, end: node.stable.start + node.length }\n : flankingSpan({ nodeId, nodeById, neighbors })\n}\n\n// Every node's midpoint on the reference, keyed by id: what a position-based\n// colour scheme ramps over. Alleles come through the same flanking walk hover\n// sync uses, so a bubble takes the colour of the reference interval it branches\n// from instead of dropping out of the ramp \u2014 which is the whole point, since an\n// allele is exactly what a linear view cannot draw at a position of its own.\nexport function referenceMidpoints(graph: Graph) {\n const nodeById = new Map(graph.nodes.map(node => [node.id, node]))\n const neighbors = buildNeighbors(graph)\n const midpoints = new Map<string, number>()\n for (const node of graph.nodes) {\n const span = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors })\n if (span) {\n midpoints.set(node.id, (span.start + span.end) / 2)\n }\n }\n return midpoints\n}\n", "import { bypassedPoints } from '../deletionEdges'\nimport { computeEdgeCurves, routedEdgeCurves, selfLinkOf } from './geometry'\n\nimport type { DeletionRoutes } from '../layout/deletionRoutes'\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 routes: DeletionRoutes | 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 // where a force layout ran each deletion; such an edge follows its route\n routes?: DeletionRoutes,\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.routes === routes &&\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 const route = bypassed && routes?.[ei]\n curves.set(\n ei,\n route\n ? routedEdgeCurves(from, to, route)\n : computeEdgeCurves(\n from,\n to,\n selfLinkOf(edge),\n 0,\n 0,\n axis,\n bypassed ? bypassedPoints(nodePositions, bypassed) : [],\n ),\n )\n }\n }\n cache.set(nodePositions, {\n graph,\n scaleX,\n scaleY,\n deletions,\n routes,\n version,\n curves,\n })\n return curves\n}\n", "import { REFERENCE_RAMP_MAX_HUE } from './renderer/GeometryBuilder'\n\n// The reference-position ramp in CSS, from the same three numbers\n// `getNodeColor` paints with (hue 0 to REFERENCE_RAMP_MAX_HUE at 70%/50%), so\n// a key or an overlay cannot come to describe a ramp the canvas stopped using.\n\nexport function rampHueCss(hue: number) {\n return `hsl(${hue}, 70%, 50%)`\n}\n\nexport function rampHue(bp: number, domain: { start: number; end: number }) {\n const frac = (bp - domain.start) / (domain.end - domain.start)\n return Math.max(0, Math.min(1, frac)) * REFERENCE_RAMP_MAX_HUE\n}\n\n// hue degrees one gradient stop spans, since SVG interpolates stops in RGB\nconst HUE_STOP_DEG = 30\n\n// The hues a gradient across `bp` of reference paints, evenly spaced along it\n// and walked from `to`'s end when reversed. One hue means the span reads flat.\nexport function rampStops(\n { start, bp, reversed }: { start: number; bp: number; reversed?: boolean },\n domain: { start: number; end: number },\n) {\n const low = rampHue(start, domain)\n const high = rampHue(start + bp, domain)\n if (high - low < 1) {\n return [(low + high) / 2]\n }\n const n = Math.ceil((high - low) / HUE_STOP_DEG)\n return Array.from({ length: n + 1 }, (_, i) =>\n rampHue(start + (bp * (reversed ? n - i : i)) / n, domain),\n )\n}\n\nexport const RAMP_GRADIENT_CSS = `linear-gradient(to right, ${Array.from(\n { length: 7 },\n (_, i) => `${rampHueCss((i / 6) * REFERENCE_RAMP_MAX_HUE)} ${(i / 6) * 100}%`,\n).join(', ')})`\n", "import { isAnchored } from '@jbrowse/bandage-core/anchoredNodes'\nimport {\n panSNContig,\n panSNHaplotype,\n panSNSample,\n} from '@jbrowse/bandage-core/pansn'\n\nimport type { AnchoredNode } from '@jbrowse/bandage-core/anchoredNodes'\nimport type { Graph, GraphNode } from '@jbrowse/bandage-core/types'\n\nexport interface GraphLocation {\n // PanSN sample name off the node's own stable sequence. The assembly that\n // contributed the sequence, which is a *name*, not a resolved assembly \u2014 see\n // resolveContributors.\n sample: string\n // `sample#hap` where the stable name states a haplotype (HPRC release 2.1\n // names every node `NA20809#2#CM094351.1`), so a sample loaded as two\n // assemblies opens the right one; undefined where it states none\n haplotype: string | undefined\n refName: string\n start: number\n end: number\n}\n\n// Where a node's own sequence lives, in the coordinates of the assembly that\n// contributed it. rGFA states this on every segment: SN names the stable\n// sequence and SO the offset on it, so this is exact at every rank.\n//\n// It is the other question from nodeReferenceSpan, which asks where a node sits\n// on the *reference* axis and can only approximate that off the backbone. A\n// rank>0 node has an exact coordinate of its own; what it has no coordinate for\n// is the reference.\nexport function nodeOwnLocation(node: GraphNode): GraphLocation | undefined {\n return isAnchored(node) ? anchoredNodeLocation(node) : undefined\n}\n\nfunction anchoredNodeLocation(node: AnchoredNode): GraphLocation {\n return {\n sample: panSNSample(node.stable.refName),\n haplotype: panSNHaplotype(node.stable.refName),\n refName: panSNContig(node.stable.refName),\n start: node.stable.start,\n end: node.stable.start + node.length,\n }\n}\n\nexport interface Contributor extends GraphLocation {\n // lowest rank this assembly holds here: 0 means it is the reference backbone\n rank: number\n nodeCount: number\n}\n\ninterface Span {\n start: number\n end: number\n}\n\n// The widest run of spans with no gap larger than `maxGap`, which is the locus\n// this assembly contributes *here*. A bare union of every span would be wrong\n// on a graph where a sample also contributes sequence from a distant\n// duplication: the union then reads as a multi-megabase locus, and a linear view\n// launched at it shows neither end.\nfunction widestCluster(spans: Span[], maxGap: number) {\n const sorted = [...spans].sort((a, b) => a.start - b.start)\n let best: Span | undefined\n let current: Span | undefined\n for (const span of sorted) {\n current =\n current && span.start - current.end <= maxGap\n ? { start: current.start, end: Math.max(current.end, span.end) }\n : { start: span.start, end: span.end }\n if (!best || current.end - current.start > best.end - best.start) {\n best = current\n }\n }\n return best\n}\n\ninterface Group {\n sample: string\n haplotype: string | undefined\n refName: string\n rank: number\n spans: Span[]\n}\n\nfunction groupKey(loc: GraphLocation) {\n return `${loc.haplotype ?? loc.sample}\u0000${loc.refName}`\n}\n\n// Every assembly with a segment in this graph, and the locus each one covers on\n// its own coordinates.\n//\n// This is what makes a launch out of the graph possible at all: the graph names\n// its contributors in the data (rGFA's SN tag), so a node, a row and the graph\n// as a whole can each say which assembly they belong to without consulting an\n// alignment.\n//\n// Ordered reference-first, then by name, so the row order matches\n// sampleRowLayout's and neither reshuffles as the window moves.\nexport function contributingAssemblies(\n graph: Graph,\n { maxGap = Infinity }: { maxGap?: number } = {},\n): Contributor[] {\n const groups = new Map<string, Group>()\n for (const node of graph.nodes) {\n if (isAnchored(node)) {\n const loc = anchoredNodeLocation(node)\n const key = groupKey(loc)\n const group = groups.get(key)\n if (group) {\n group.spans.push({ start: loc.start, end: loc.end })\n group.rank = Math.min(group.rank, node.stable.rank)\n } else {\n groups.set(key, {\n sample: loc.sample,\n haplotype: loc.haplotype,\n refName: loc.refName,\n rank: node.stable.rank,\n spans: [{ start: loc.start, end: loc.end }],\n })\n }\n }\n }\n\n // One entry per assembly (a haplotype where the names state one, else the\n // sample): an assembly contributing on several contigs is launched at the\n // contig it contributes most of, since a linear view opens on one region.\n const bySample = new Map<string, Contributor>()\n for (const group of groups.values()) {\n const span = widestCluster(group.spans, maxGap)\n if (span) {\n const candidate = {\n sample: group.sample,\n haplotype: group.haplotype,\n refName: group.refName,\n start: span.start,\n end: span.end,\n rank: group.rank,\n nodeCount: group.spans.length,\n }\n const key = group.haplotype ?? group.sample\n const existing = bySample.get(key)\n if (!existing || candidate.nodeCount > existing.nodeCount) {\n bySample.set(key, candidate)\n }\n }\n }\n\n // Reference first, then by name: the same order sampleRowLayout gives its\n // rows, so the menu lists the assemblies in the order they are stacked on\n // screen. Sorting by rank instead would order them by minigraph's build\n // order, which is not what the labels beside the drawing say.\n return [...bySample.values()].sort(\n (a, b) =>\n Number(a.rank !== 0) - Number(b.rank !== 0) ||\n a.sample.localeCompare(b.sample) ||\n (a.haplotype ?? '').localeCompare(b.haplotype ?? ''),\n )\n}\n\n// The assembly a location opens on: its haplotype's where the name states one\n// and the session holds it, else its sample's. A session holding both\n// haplotypes of a sample opens the right one; one holding a haplotype under\n// the bare sample name still opens it.\nexport function resolveLocationAssembly(\n resolve: (sample: string) => string | undefined,\n loc: Pick<GraphLocation, 'sample' | 'haplotype'>,\n) {\n const byHaplotype =\n loc.haplotype === undefined ? undefined : resolve(loc.haplotype)\n return byHaplotype ?? resolve(loc.sample)\n}\n\n// Contributors that name an assembly this session has loaded, which are the\n// only ones a linear or synteny view can open on.\n//\n// The two cases this filters apart are both normal. An E. coli pangenome demo\n// loads every strain as its own assembly, so every contributor resolves and the\n// whole graph is navigable. An HPRC graph names hundreds of haplotypes that no\n// session would ever load as assemblies, so only the reference resolves \u2014\n// there the reference projection (nodeReferenceSpan) is the way back to a\n// coordinate, not this.\n//\n// `resolve` is a name lookup rather than a list of names, because a PanSN sample\n// is the *graph's* spelling and need not be the assembly's. HPRC's graph writes\n// `CHM13`, and the assembly holding that sequence is UCSC's `hs1`, with CHM13\n// among its aliases; matching on names alone leaves that donor unopenable unless\n// the reader renames their assembly after the graph. The resolved (canonical)\n// name replaces the sample, so a launch opens the assembly the session knows.\nexport function resolveContributors(\n contributors: Contributor[],\n resolve: (sample: string) => string | undefined,\n) {\n return contributors.flatMap(c => {\n const sample = resolveLocationAssembly(resolve, c)\n return sample === undefined ? [] : [{ ...c, sample }]\n })\n}\n\n// 1-based inclusive, which is what a locstring means. Unformatted digits: this\n// is parsed, not read. locLabel is the one people read.\nexport function locString(loc: GraphLocation) {\n return `${loc.refName}:${loc.start + 1}-${loc.end}`\n}\n\nexport function locLabel(loc: { refName: string; start: number; end: number }) {\n return `${loc.refName}:${(loc.start + 1).toLocaleString()}-${loc.end.toLocaleString()}`\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M9.01 14H2v2h7.01v3L13 15l-3.99-4zm5.98-1v-3H22V8h-7.01V5L11 9z\"\n}), 'CompareArrows');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M17 5h-2V3h2zm-2 16h2v-2.59L19.59 21 21 19.59 18.41 17H21v-2h-6zm4-12h2V7h-2zm0 4h2v-2h-2zm-8 8h2v-2h-2zM7 5h2V3H7zM3 17h2v-2H3zm2 4v-2H3c0 1.1.9 2 2 2M19 3v2h2c0-1.1-.9-2-2-2m-8 2h2V3h-2zM3 9h2V7H3zm4 12h2v-2H7zm-4-8h2v-2H3zm0-8h2V3c-1.1 0-2 .9-2 2\"\n}), 'HighlightAlt');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M3 16h5v-2H3zm6.5 0h5v-2h-5zm6.5 0h5v-2h-5zM3 20h2v-2H3zm4 0h2v-2H7zm4 0h2v-2h-2zm4 0h2v-2h-2zm4 0h2v-2h-2zM3 12h8v-2H3zm10 0h8v-2h-8zM3 4v4h18V4z\"\n}), 'LineStyle');", "import CompareArrowsIcon from '@mui/icons-material/CompareArrows'\nimport HighlightAltIcon from '@mui/icons-material/HighlightAlt'\nimport LineStyleIcon from '@mui/icons-material/LineStyle'\n\nimport { locLabel } from './contributors'\n\nimport type { Contributor, GraphLocation } from './contributors'\nimport type { LaunchableSyntenyTrack } from './syntenyTracks'\nimport type { MenuItem } from '@jbrowse/core/ui'\n\nconst LINEAR_LABEL = 'Linear genome view'\nconst SYNTENY_LABEL = 'Linear synteny view'\nconst NO_SYNTENY_TRACK =\n 'No synteny track in this session aligns these assemblies'\n\nexport interface LinearTarget {\n location: GraphLocation\n assembly: string\n}\n\n// One flat item when there is one thing to open, a submenu when there is a\n// choice. Same shape as subgraphMenuItems uses in the other direction, so the\n// two ends of the graph <-> linear trip read the same way.\nfunction oneOrMany<T>({\n label,\n icon,\n entries,\n entryLabel,\n onSelect,\n disabledHelpText,\n nameTheOnlyEntry = true,\n}: {\n label: string\n icon: React.ElementType\n entries: T[]\n entryLabel: (entry: T) => string\n onSelect: (entry: T) => () => void\n disabledHelpText?: string\n // Whether a lone entry's name is worth appending to the item. It is when the\n // entry is where you land (`GRCh38 chr6:31,980,000-32,050,000`) and not when\n // it is the ingredient used to get there \u2014 a synteny track's name says\n // nothing the item does not already say, and reading it costs the width of\n // `pggb graph: all-vs-all synteny (wfmash)`.\n nameTheOnlyEntry?: boolean\n}): MenuItem[] {\n const [first] = entries\n // Nothing to open: a *disabled* item with the reason in its tooltip, when\n // there is a reason worth reading. An item that vanishes teaches nobody that\n // the session is one synteny track short of a multi-genome view.\n const empty: MenuItem[] = disabledHelpText\n ? [{ label, icon, disabled: true, disabledHelpText, onClick: () => {} }]\n : []\n return entries.length === 0\n ? empty\n : entries.length === 1 && first\n ? [\n {\n label: nameTheOnlyEntry ? `${label} \u2014 ${entryLabel(first)}` : label,\n icon,\n onClick: onSelect(first),\n },\n ]\n : [\n {\n label,\n icon,\n type: 'subMenu',\n subMenu: entries.map(entry => ({\n label: entryLabel(entry),\n onClick: onSelect(entry),\n })),\n },\n ]\n}\n\n// The graph's way out: a linear view of any assembly the graph names here, and a\n// synteny view of all of them at once. Until this existed the triangle had two\n// edges \u2014 a linear view could open a graph or a synteny view of a locus, and the\n// graph could open nothing at all.\n//\n// `contributors` are already resolved against the session's assemblies. On an\n// E. coli pangenome every strain is its own assembly, so all of them appear and\n// the synteny item is the interesting one; on an HPRC graph the 400-odd\n// contributing haplotypes are not loaded assemblies and only the reference\n// appears, which is the honest answer rather than a missing feature.\nexport function graphLaunchMenuItems({\n contributors,\n syntenyTracks,\n onShowLinear,\n onShowSynteny,\n}: {\n contributors: Contributor[]\n syntenyTracks: LaunchableSyntenyTrack[]\n onShowLinear: (target: LinearTarget) => void\n onShowSynteny: (trackId: string) => void\n}): MenuItem[] {\n // sorted widest first\n const widest = syntenyTracks[0]?.coverage ?? contributors.length\n return [\n ...oneOrMany({\n label: LINEAR_LABEL,\n icon: LineStyleIcon,\n entries: contributors,\n entryLabel: c => `${c.sample} ${locLabel(c)}`,\n onSelect: c => () => {\n onShowLinear({ location: c, assembly: c.sample })\n },\n }),\n ...(contributors.length >= 2\n ? oneOrMany({\n // the count the launch opens, which is what the widest track aligns\n // and not always every contributor; see launchSyntenyView\n label: `${SYNTENY_LABEL} (${widest} assemblies)`,\n icon: CompareArrowsIcon,\n entries: syntenyTracks,\n entryLabel: track =>\n track.coverage < widest\n ? `${track.name} (${track.coverage} of ${widest})`\n : track.name,\n nameTheOnlyEntry: false,\n disabledHelpText: NO_SYNTENY_TRACK,\n onSelect: track => () => {\n onShowSynteny(track.trackId)\n },\n })\n : []),\n ]\n}\n\n// A single node's way out, for the node context menu. Two different questions,\n// both worth offering:\n//\n// - the node's own coordinates, on the assembly that contributed it. Exact,\n// from the node's SN/SO, and available at every rank \u2014 but only openable\n// when that assembly is loaded, which for an HPRC haplotype it is not.\n// - where the node sits on the reference the graph was cut against. Always\n// openable, and off the backbone it is the flanking projection rather than\n// an exact span (see nodeReferenceSpan), so it is labelled as the region\n// around the node rather than as the node.\n//\n// Labels are verbs. Review: \"the lineargenomeview menu item should say launch\n// if that is what it does\" \u2014 a bare `Linear genome view \u2014 hg38 chr6:\u2026` names a\n// place and leaves the reader to guess whether clicking scrolls the view beside\n// the graph or opens a new one. It does the first when there is one to scroll\n// (see showInLinearView), so \"Open in\" covers both without claiming a launch.\n//\n// Verb, assembly, and where \u2014 nothing else. Every word here is read inside a\n// menu opened on a node, so \"this node in the hg38 view\" and \"in linear view\"\n// restate the context they are already in; what a coordinate opens in is not in\n// question, and the assembly name is.\n//\n// Flat, not under a \"Launch view\" submenu: this menu is a handful of items and\n// every one is contextual to the node just clicked.\nexport function nodeLaunchMenuItems({\n own,\n reference,\n highlight,\n onShowLinear,\n onHighlight,\n}: {\n own: LinearTarget | undefined\n reference: LinearTarget | undefined\n // the node's exact reference span, where `reference` is padded to open on\n highlight: LinearTarget | undefined\n onShowLinear: (target: LinearTarget) => void\n // absent when no linear view in the session is this graph's to mark\n onHighlight?: (target: LinearTarget) => void\n}): MenuItem[] {\n const entries = [\n own\n ? {\n label: `Open in ${own.assembly} \u2014 ${locLabel(own.location)}`,\n target: own,\n }\n : undefined,\n // Only when it says something the item above doesn't. A backbone segment on\n // a loaded reference answers both questions with the same assembly and\n // nearly the same span, and offering that twice reads as two different\n // places to go.\n reference && reference.assembly !== own?.assembly\n ? {\n label: `Open in ${reference.assembly} \u2014 around this node`,\n target: reference,\n }\n : undefined,\n ].filter(entry => entry !== undefined)\n\n // The reference projection, not the node's own coordinates: a highlight is\n // drawn in the linear view already on screen, and that view is on the\n // reference the subgraph was cut from.\n return [\n ...(onHighlight && highlight\n ? [\n {\n label: `Highlight in ${highlight.assembly}`,\n icon: HighlightAltIcon,\n onClick: () => {\n onHighlight(highlight)\n },\n },\n ]\n : []),\n ...entries.map(({ label, target }) => ({\n label,\n icon: LineStyleIcon,\n onClick: () => {\n onShowLinear(target)\n },\n })),\n ]\n}\n", "import { parseCs } from '../gaf/parseGaf'\n\nimport type { GafRecord } from '../gaf/parseGaf'\nimport type { Graph, GraphNode } from '../types'\nimport type { InputTrack, Mismatch } from '@gmod/tubemap-core'\n\n// GAF reads as tube map reads over a cut. A read keeps the steps whose\n// segments the cut holds, and its cs edits land on the node they fall in, at\n// a position in the direction the read walks it, as vg's GAM mappings state\n// theirs.\n\nexport function canonicalStrand(node: GraphNode) {\n return node.id.endsWith('-') ? '-' : '+'\n}\n\n// A visit to a node as tubemap-core names it: `-` where the walk runs against\n// the strand the node is drawn in\nexport function stepName(node: GraphNode, strand: '+' | '-') {\n return strand === canonicalStrand(node) ? node.id : `-${node.id}`\n}\n\n// Where each step starts on the walk, where that can be known: counted from\n// the walk's start up to the first segment outside the cut, and back from its\n// end down to the last. A step between two such segments has no start.\nfunction stepStarts(lengths: readonly (number | undefined)[], total: number) {\n const starts: (number | undefined)[] = lengths.map(() => undefined)\n let head = 0\n let i = 0\n for (; i < lengths.length && lengths[i] !== undefined; i++) {\n starts[i] = head\n head += lengths[i]!\n }\n let tail = total\n for (let j = lengths.length - 1; j > i && lengths[j] !== undefined; j--) {\n tail -= lengths[j]!\n starts[j] = tail\n }\n return starts\n}\n\nfunction mismatchesByStep(\n record: GafRecord,\n lengths: readonly (number | undefined)[],\n) {\n const byStep: Mismatch[][] = record.path.map(() => [])\n if (record.cs === undefined) {\n return byStep\n }\n const starts = stepStarts(lengths, record.pathLength)\n // the step holding walk position `pos`, or for `atEnd` the one ending there\n const stepAt = (pos: number, atEnd = false) => {\n for (let s = 0; s < starts.length; s++) {\n const start = starts[s]\n const length = lengths[s]\n if (\n start !== undefined &&\n length !== undefined &&\n start <= pos &&\n (pos < start + length || (atEnd && pos === start + length))\n ) {\n return { s, local: pos - start, room: start + length - pos }\n }\n }\n return undefined\n }\n const add = (s: number, mismatch: Mismatch) => {\n const list = byStep[s]!\n const last = list.at(-1)\n if (\n mismatch.type === 'substitution' &&\n last?.type === 'substitution' &&\n last.pos + last.seq!.length === mismatch.pos\n ) {\n last.seq += mismatch.seq!\n } else {\n list.push(mismatch)\n }\n }\n let pos = record.pathStart\n for (const op of parseCs(record.cs)) {\n if (op.op === 'match') {\n pos += op.length\n } else if (op.op === 'substitution') {\n const at = stepAt(pos)\n if (at) {\n add(at.s, { type: 'substitution', pos: at.local, seq: op.seq })\n }\n pos += 1\n } else if (op.op === 'insertion') {\n const at = stepAt(pos, pos === record.pathEnd)\n if (at) {\n add(at.s, { type: 'insertion', pos: at.local, seq: op.seq })\n }\n } else {\n let left = op.length\n while (left > 0) {\n const at = stepAt(pos)\n const take = at ? Math.min(left, at.room) : left\n if (at) {\n add(at.s, { type: 'deletion', pos: at.local, length: take })\n }\n pos += take\n left -= take\n }\n }\n }\n return byStep\n}\n\n// `firstId` follows the haplotype tracks' ids, which tubemap-core shares\n// between tracks and reads.\nexport function tubeMapReads(\n graph: Graph,\n records: readonly GafRecord[],\n firstId: number,\n): InputTrack[] {\n const nodeByName = new Map(graph.nodes.map(n => [n.name, n]))\n const reads: InputTrack[] = []\n for (const record of records) {\n const nodes = record.path.map(step => nodeByName.get(step.name))\n const kept = nodes.flatMap((node, i) => (node ? [i] : []))\n const first = kept[0]\n const last = kept.at(-1)\n if (first !== undefined && last !== undefined) {\n const mismatches = mismatchesByStep(\n record,\n nodes.map(n => n?.length),\n )\n const names = kept.map(i => stepName(nodes[i]!, record.path[i]!.strand))\n const lastLength = nodes[last]!.length\n reads.push({\n id: firstId + reads.length,\n name: record.name,\n type: 'read',\n sourceTrackID: 1,\n sequence: names,\n sequenceNew: kept.map((i, k) => ({\n nodeName: names[k]!,\n mismatches: mismatches[i]!,\n })),\n firstNodeOffset: first === 0 ? record.pathStart : 0,\n finalNodeCoverLength:\n last === record.path.length - 1\n ? lastLength - (record.pathLength - record.pathEnd)\n : lastLength,\n mapping_quality:\n record.mappingQuality === 255 ? 0 : record.mappingQuality,\n is_secondary: record.secondary,\n is_reverse: record.strand === '-',\n })\n }\n }\n return reads\n}\n\n// sequenceTubeMap's read palettes: blues forward, reds reverse, staggered by\n// read so neighbours differ\nexport const FORWARD_READ_COLORS = ['#6baed6', '#4292c6', '#2171b5', '#08519c']\nexport const REVERSE_READ_COLORS = ['#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15']\n\nexport function readColor(read: { id: number; is_reverse?: boolean }) {\n const colors = read.is_reverse ? REVERSE_READ_COLORS : FORWARD_READ_COLORS\n return colors[read.id % colors.length]!\n}\n", "import { REFERENCE_RANK, isAnchored } from '../anchoredNodes'\nimport { buildNeighbors } from '../referenceSpan'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph } from '../types'\n\n// An rGFA subgraph's alleles on the reference axis, from the stable\n// coordinates and the L lines alone: an allele is a run of off-reference\n// segments, and the rank-0 segments it detaches from and reattaches to give it\n// a reference span. rGFA carries no W or P lines, so nothing here reads a walk.\n\nexport interface ProjectedAllele {\n // reference bp the allele replaces, from the flanking rank-0 segments\n start: number\n refSpan: number\n // the run's strand-suffixed node ids, ordered by `nodeOffsets`\n nodeIds: string[]\n // bp from the allele's entry to the start of each node, along the run's own\n // edges, so a layout puts a node where it sits within the allele\n nodeOffsets: number[]\n // bp of the longest path through the run, the denominator for nodeOffsets:\n // a bubble's branches are alternatives, so their sum is no assembly's length\n pathLength: number\n}\n\n// PanSN is `sample#haplotype#contig`, but minigraph accepts a bare contig name\n// and the E. coli fixture is `K12#1#chr`, so this parses what is there.\nexport function parsePanSN(refName: string) {\n const parts = refName.split('#')\n const haplotype = parts.length >= 2 ? Number(parts[1]) : Number.NaN\n return {\n sample: parts[0] ?? refName,\n haplotype: Number.isFinite(haplotype) ? haplotype : undefined,\n }\n}\n\n// One connected run of off-reference segments and the rank-0 segments on its\n// boundary. Walked through anchored nodes only: everything reported about a\n// run is read off a stable coordinate.\nfunction collectRun(\n seed: string,\n byId: Map<string, AnchoredNode>,\n adj: Map<string, string[]>,\n claimed: Set<string>,\n) {\n const members: AnchoredNode[] = []\n const anchors: AnchoredNode[] = []\n const queue = [seed]\n claimed.add(seed)\n\n while (queue.length > 0) {\n const id = queue.pop()!\n const node = byId.get(id)\n if (node) {\n members.push(node)\n for (const neighborId of adj.get(id) ?? []) {\n const neighbor = byId.get(neighborId)\n if (neighbor) {\n if (neighbor.stable.rank === REFERENCE_RANK) {\n anchors.push(neighbor)\n } else if (!claimed.has(neighborId)) {\n claimed.add(neighborId)\n queue.push(neighborId)\n }\n }\n }\n }\n }\n return { members, anchors }\n}\n\n// The reference interval an allele replaces: from the end of the last backbone\n// segment before it to the start of the first one after it. Anchors on two\n// stable sequences, or in an order that runs backwards (an inverted or\n// translocated attachment), state no span rather than a negative one.\nfunction anchorSpan(anchors: AnchoredNode[]) {\n const refNames = new Set(anchors.map(a => a.stable.refName))\n if (refNames.size !== 1 || anchors.length < 2) {\n return undefined\n }\n const sorted = [...anchors].sort((a, b) => a.stable.start - b.stable.start)\n const first = sorted[0]!\n const last = sorted.at(-1)!\n const start = first.stable.start + first.length\n const end = last.stable.start\n return end >= start ? { start, end, entry: first } : undefined\n}\n\n// Shortest-path bp from the upstream anchor to each member over the run's own\n// edges, which only orders the run. The edges are undirected (a GFA states a\n// reverse-complement link backwards and single-node mode collapses both\n// orientations), so there is no topological order and a longest-path\n// relaxation would not converge.\nfunction runDistances(\n members: AnchoredNode[],\n entry: AnchoredNode,\n adj: Map<string, string[]>,\n) {\n const byId = new Map(members.map(m => [m.id, m]))\n const dist = new Map<string, number>()\n const queue: string[] = []\n for (const neighborId of adj.get(entry.id) ?? []) {\n if (byId.has(neighborId)) {\n dist.set(neighborId, 0)\n queue.push(neighborId)\n }\n }\n if (queue.length === 0) {\n for (const member of members) {\n dist.set(member.id, 0)\n queue.push(member.id)\n }\n }\n for (const id of queue) {\n const reach = dist.get(id)! + byId.get(id)!.length\n for (const neighborId of adj.get(id) ?? []) {\n if (byId.has(neighborId)) {\n const known = dist.get(neighborId)\n if (known === undefined || reach < known) {\n dist.set(neighborId, reach)\n queue.push(neighborId)\n }\n }\n }\n }\n return dist\n}\n\n// Where each member sits within the allele: ordered by distance from the entry\n// and swept forward, so a node starts where the last of its already-placed\n// neighbours ends. Offsets are then monotone along every edge in the run, which\n// is what keeps a bubble's entry and exit curves from crossing; where the two\n// branches differ in length the short one gets slack rather than the long one a\n// backwards edge.\nfunction runOffsets(\n members: AnchoredNode[],\n entry: AnchoredNode,\n adj: Map<string, string[]>,\n) {\n const dist = runDistances(members, entry, adj)\n const ordered = [...members].sort((a, b) => dist.get(a.id)! - dist.get(b.id)!)\n const rank = new Map(ordered.map((m, i) => [m.id, i]))\n const byId = new Map(members.map(m => [m.id, m]))\n const offsets = new Map<string, number>()\n for (const [i, member] of ordered.entries()) {\n let offset = 0\n for (const neighborId of adj.get(member.id) ?? []) {\n const neighbor = byId.get(neighborId)\n if (neighbor && rank.get(neighborId)! < i) {\n offset = Math.max(offset, offsets.get(neighborId)! + neighbor.length)\n }\n }\n offsets.set(member.id, offset)\n }\n return { offsets, ordered }\n}\n\n// Every allele with a reference span, by start. A run that reaches the edge of\n// the cut has one anchor and no span, which is the ordinary case at a window\n// boundary; placeOffReference chains those off wherever they branch.\nexport function projectAlleles(graph: Graph): ProjectedAllele[] {\n const byId = new Map(\n graph.nodes.filter(isAnchored).map(n => [n.id, n] as const),\n )\n const adj = buildNeighbors(graph)\n const claimed = new Set<string>()\n const alleles: ProjectedAllele[] = []\n\n for (const node of byId.values()) {\n if (node.stable.rank !== REFERENCE_RANK && !claimed.has(node.id)) {\n const { members, anchors } = collectRun(node.id, byId, adj, claimed)\n const span = anchorSpan(anchors)\n if (span) {\n const { offsets, ordered } = runOffsets(members, span.entry, adj)\n alleles.push({\n start: span.start,\n refSpan: span.end - span.start,\n nodeIds: ordered.map(m => m.id),\n nodeOffsets: ordered.map(m => offsets.get(m.id)!),\n pathLength: ordered.reduce(\n (max, m) => Math.max(max, offsets.get(m.id)! + m.length),\n 0,\n ),\n })\n }\n }\n }\n\n return alleles.sort((a, b) => a.start - b.start)\n}\n", "import { projectAlleles } from '../alleleProjection/projectAlleles'\nimport { buildNeighbors } from '../referenceSpan'\n\nimport type { AlleleDeletion, Graph, NodeSegment } from '../types'\n\n// Where off-reference segments go on a reference x axis. Shared by the two\n// row-structured layouts, which differ only in what a row means (stable rank\n// vs contributing assembly); how far along the reference a segment reaches is\n// the same question in both, and answering it twice is how they drifted.\n//\n// The rule: a segment occupies the reference its allele replaces, never its own\n// sequence length. x is reference bp \u2014 the axis the backbone declares and the\n// linear view beside it is drawn on \u2014 so an insertion, which consumes no\n// reference, must not advance along it. Both layouts used to advance by\n// sequence length, which made every off-reference x a false statement about\n// reference position and, on a 1 kb window holding a 100 kb allele, drew 101x\n// the window so that zoom-to-fit rendered the reference at 1% of the frame.\n//\n// Magnitude is the cost. An axis of reference coordinates has no way to state\n// how much sequence an insertion adds, so it is not stated here: it is in the\n// node tooltip, and drawn to scale in the allele-inventory track, where a CIGAR\n// gives the alignments display a channel for length that is not the x axis.\n\n// Reference bp an allele has to skip before the drawing calls it a deletion:\n// the 1 bp floor `deletionEdges` uses, a guard against abutting anchors' zero\n// gap rather than a biological threshold. Whether a small one is drawn is\n// decided by its label's fit in graphLabels.\nconst MIN_DELETION_BP = 1\n\n// Floor on an off-reference node's drawn span, as a fraction of the window.\n// Node thickness is a constant number of screen pixels, so a segment on a\n// sub-percent slice of the window draws wider than it is long and reads as a\n// dot on a stalk, and a pure insertion occupies no reference at all. Rank-0\n// nodes keep their exact offsets, so the reference axis is untouched.\nconst MIN_ALLELE_SPAN_FRACTION = 0.015\n\ninterface PlaceArgs {\n graph: Graph\n // the reference interval the drawing is scaled against (referenceSpan)\n span: number\n // y for a segment, which is the whole of what the two layouts disagree about\n rowY: (node: Graph['nodes'][number]) => number\n // the backbone, already placed at its declared offsets. Mutated in place, and\n // read for the branch points the unanchored walk below starts from.\n positions: Record<string, NodeSegment[]>\n}\n\nexport function placeOffReference({\n graph,\n span,\n rowY,\n positions,\n}: PlaceArgs): AlleleDeletion[] {\n const minSpan = span * MIN_ALLELE_SPAN_FRACTION\n const alleles = projectAlleles(graph)\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n // Alleles standing in for more reference than they carry. Collected here\n // rather than derived by the label pass, because the reason they need saying\n // is the placement done in this function: a run drawn over the reference it\n // replaces has a width that is not its length, and nothing downstream can\n // tell that from a node drawn at its own scale. Same measure the deletion\n // arcs use \u2014 reference bp a haplotype does not carry \u2014 so the two kinds of\n // deletion in a drawing are stated in the same units and the same words.\n const alleleDeletions: AlleleDeletion[] = []\n\n for (const allele of alleles) {\n const skipped = allele.refSpan - allele.pathLength\n if (skipped >= MIN_DELETION_BP) {\n alleleDeletions.push({ nodeIds: allele.nodeIds, bp: skipped })\n }\n const drawn = Math.max(allele.refSpan, minSpan)\n // A node sits where it sits *within* the allele: its bp offset from the\n // entry, scaled so the run's longest path fills the reference the allele\n // replaces. So a bubble path occupies the reference exactly once however\n // many segments it is built from, and two parallel branches share the x\n // range rather than queue up after each other \u2014 they are alternatives over\n // the same interval, not consecutive sequence.\n //\n // Offsets are measured over the graph rather than over nodeIds' order (see\n // runOffsets). Concatenating in traversal order put graph neighbours 26 kb\n // apart on a 27 kb allele, and the edges spanning those gaps crossed as a\n // long X. A run of all zero-length segments has no distances to scale by;\n // splitting evenly beats 0/0, which wrote NaN into every position after it.\n //\n // Where the floor widens a run past the reference it replaces, the overhang\n // hangs off the right, so the run can end past the rank-0 segment it\n // rejoins and its exit edge runs backwards. That is handled where it shows,\n // in computeEdgeCurves, which sizes each control point by how far the other\n // endpoint lies along its own tangent \u2014 a backwards edge gets a straight\n // leader rather than the loop that read as a crossed bowtie.\n const scale = allele.pathLength > 0 ? drawn / allele.pathLength : 0\n allele.nodeIds.forEach((nodeId, i) => {\n const node = byId.get(nodeId)\n if (node) {\n const offset = allele.nodeOffsets[i]!\n const even = drawn / allele.nodeIds.length\n // both ends from the offset rather than start-plus-width, so two nodes\n // the sweep placed end to end land on exactly the same x instead of on\n // two roundings of it\n const at = (bp: number) => allele.start + bp * scale\n const y = rowY(node)\n positions[nodeId] = scale\n ? [\n { x: at(offset), y },\n { x: at(offset + node.length), y },\n ]\n : [\n { x: allele.start + i * even, y },\n { x: allele.start + (i + 1) * even, y },\n ]\n }\n })\n }\n\n // A run that reaches the edge of the fetched subgraph has only one backbone\n // anchor, so no reference span can be stated for it. That is the normal case\n // at a window boundary, not an error, and dropping it is indistinguishable\n // from the layout being broken \u2014 the sample-rows layout did drop them.\n //\n // They get the floor each, chained from wherever they branch: the only honest\n // claim available is \"attaches here and continues past the edge\", and a floor\n // keeps a chain of them bounded by the window instead of by their sequence.\n // The same undirected adjacency the reference walk builds, from the one\n // function that builds it. The copy here rebuilt each list with a spread per\n // edge, which is quadratic in a node's degree and is the shape a collapsed\n // repeat's hub node is worst at.\n const neighbors = buildNeighbors(graph)\n // Read forward while appending, so this is a BFS with an O(1) dequeue rather\n // than a quadratic shift() over what ends up being every node in the run.\n const queue = Object.keys(positions)\n for (const fromId of queue) {\n const fromEnd = positions[fromId]!.at(-1)!.x\n for (const nextId of neighbors.get(fromId) ?? []) {\n const next = byId.get(nextId)\n if (next && !(nextId in positions)) {\n const y = rowY(next)\n positions[nextId] = [\n { x: fromEnd, y },\n { x: fromEnd + minSpan, y },\n ]\n queue.push(nextId)\n }\n }\n }\n\n return alleleDeletions\n}\n", "import { placeOffReference } from './placeOffReference'\nimport { ROW_HEIGHT_PX } from './rowSpacing'\nimport {\n backboneNodes,\n backbonePositions,\n referenceSpan,\n} from '../anchoredNodes'\n\nimport type { Graph, LayoutResult, RowLabel } from '../types'\n\n// Layout for rGFA, where the graph states its own backbone instead of leaving a\n// force simulation to find one. Every segment carries SN/SO/SR (gfatools\n// doc/rGFA.md), so both axes come from the file:\n//\n// x reference bp, for every segment. Rank-0 segments sit at the offset they\n// declare; an off-reference segment cannot use its own SO, which is an\n// offset on a different stable sequence, so it takes the slice of the\n// reference its allele replaces (placeOffReference).\n// y one row per stable rank *present in this subgraph*, the way lh3's own\n// rGFA viewer (VRPG) does it: rank 0 is the reference line, higher ranks\n// below it in order. In SCREEN PIXELS, not bp \u2014 see rowSpacing.ts, shared\n// with the sample-row layout.\n\n// Rank is a property of the whole graph, not of the window being drawn: HPRC's\n// minigraph graph ranks up to 89, but an MHC window holds only ranks\n// 0/1/3/6/14/23. Indexing rows by raw rank would leave 17 of 24 rows empty and\n// zoom-to-fit would shrink the drawing to fit that void (measured: 0.3% scale).\n// So rows are the ranks actually present, in rank order \u2014 identical to the raw\n// rank whenever the window happens to hold a contiguous run from 0.\nfunction rankRows(graph: Graph) {\n const present = new Set<number>()\n for (const node of graph.nodes) {\n if (node.stable) {\n present.add(node.stable.rank)\n }\n }\n return new Map([...present].sort((a, b) => a - b).map((r, i) => [r, i]))\n}\n\nexport function anchoredLayout(\n graph: Graph,\n region?: { start: number; end: number },\n): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n if (backbone.length === 0) {\n return undefined\n }\n\n const span = referenceSpan(backbone, region)\n const rows = rankRows(graph)\n\n // Row 0 by construction: a backbone exists, so rank 0 is present, and\n // `rankRows` numbers the present ranks in ascending order.\n const nodePositions = backbonePositions(backbone)\n\n const alleleDeletions = placeOffReference({\n graph,\n span,\n rowY: node =>\n (node.stable ? (rows.get(node.stable.rank) ?? rows.size) : rows.size) *\n ROW_HEIGHT_PX,\n positions: nodePositions,\n })\n\n // Named from the same `rows` map that placed every node, so the axis and the\n // drawing cannot disagree. Rank 0 is spelled out: \"the reference\" is what a\n // reader needs, and \"Rank 0\" alone reads as an ordinal with no meaning.\n const rowLabels: RowLabel[] = [...rows].map(([rank, row]) => ({\n label: rank === 0 ? 'Reference (rank 0)' : `Rank ${rank}`,\n y: row * ROW_HEIGHT_PX,\n }))\n\n return {\n nodePositions,\n rowLabels,\n referenceAxis: true,\n pixelRows: true,\n alleleDeletions,\n }\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { isBackbone } from '../anchoredNodes'\n\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph, GraphNode, LayoutResult, NodeSegment } from '../types'\n\n// Reference-ordered layered layout: x is reference ORDER, not bp. Backbone\n// segments take increasing layers by stable offset, every other node the layer\n// between the anchors it hangs off, and a node is as wide as the log of its bp,\n// so a SNP allele gets the same room as a 10 kb segment and a bubble reads as a\n// lens rather than a bar under a line. y is a lane in screen px with the\n// reference pinned at lane 0 (ADR-042).\n\nconst GUTTER = 12\nconst LANE_PX = ROW_HEIGHT_PX\n\nfunction nodeWidth(node: GraphNode) {\n return 10 + 8 * Math.log2(1 + node.length)\n}\n\nfunction compare<T extends string | number>(a: T, b: T) {\n return a < b ? -1 : a > b ? 1 : 0\n}\n\n// Where a node sorts: `at` is the rank of a placed node, the backbone and what\n// the walks visit, and a node the search claims sorts before or after the one\n// that claimed it, by its steps from it. A tuple rather than an offset nudged\n// per step: on a base-level graph the backbone nodes are a bp apart, and a\n// chain a few nodes long nudged its keys past the ones that follow.\ninterface OrderKey {\n at: number\n side: -1 | 0 | 1\n hops: number\n}\n\nfunction compareKeys(a: OrderKey, b: OrderKey) {\n return (\n compare(a.at, b.at) || compare(a.side, b.side) || compare(a.hops, b.hops)\n )\n}\n\nconst UNREACHED: OrderKey = { at: Infinity, side: 0, hops: 0 }\n\n// Every node id in reference order: backbone by stable offset, every other node\n// beside a backbone node (orderKeys), nodes nothing reaches last. Name breaks\n// ties, so the order is a strict total order and the same graph always gets\n// the same one.\nexport function referenceOrder(graph: Graph) {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const key = orderKeys(graph)\n return graph.nodes\n .map(n => n.id)\n .sort(\n (a, b) =>\n compareKeys(key.get(a) ?? UNREACHED, key.get(b) ?? UNREACHED) ||\n compare(byId.get(a)!.name, byId.get(b)!.name) ||\n compare(a, b),\n )\n}\n\n// A walk states where its nodes lie, which the links alone do not. Starting\n// from the backbone in offset order, each node a walk visits for the first time\n// goes into the order immediately after the node that walk came from, and the\n// nodes a walk visits before it meets a placed one go immediately before it.\n//\n// That keeps every true boundary. A walk's step joins two nodes on the same\n// side of a node every haplotype crosses once, unless one of them is that\n// node, so by induction the nodes between two such boundaries stay one\n// unbroken stretch of the order and no link among them reaches across. On the\n// eight-haplotype KIV-2 cut (15,808 nodes) the walks imply 28 bubbles and this\n// derives those 28, with 5 of 65,776 walk steps against the order. Placing\n// every node by the links alone derived 21, with 23,308 steps against it.\n//\n// A walk on the reverse strand is read end-first.\nfunction placeByWalks(graph: Graph, backbone: AnchoredNode[]) {\n const isNode = new Set(graph.nodes.map(n => n.id))\n const startOf = new Map(backbone.map(n => [n.id, n.stable.start]))\n const next = new Map<string, string | undefined>()\n const prev = new Map<string, string | undefined>()\n backbone.forEach((n, i) => {\n prev.set(n.id, backbone[i - 1]?.id)\n next.set(n.id, backbone[i + 1]?.id)\n })\n let head = backbone[0]?.id\n const insertAfter = (at: string, id: string) => {\n const after = next.get(at)\n next.set(at, id)\n prev.set(id, at)\n next.set(id, after)\n if (after !== undefined) {\n prev.set(after, id)\n }\n }\n const insertBefore = (at: string, id: string) => {\n const before = prev.get(at)\n if (before === undefined) {\n head = id\n prev.set(id, undefined)\n next.set(id, at)\n prev.set(at, id)\n } else {\n insertAfter(before, id)\n }\n }\n\n for (const path of graph.paths ?? []) {\n let rising = 0\n let falling = 0\n let last: number | undefined\n for (const id of path.nodeIds) {\n const start = startOf.get(id)\n if (start !== undefined) {\n if (last !== undefined && start !== last) {\n if (start > last) {\n rising++\n } else {\n falling++\n }\n }\n last = start\n }\n }\n const ids = falling > rising ? [...path.nodeIds].reverse() : path.nodeIds\n let from: string | undefined\n const leading = new Set<string>()\n for (const id of ids) {\n if (next.has(id)) {\n if (from === undefined) {\n for (const lead of leading) {\n insertBefore(id, lead)\n }\n }\n from = id\n } else if (isNode.has(id)) {\n if (from === undefined) {\n leading.add(id)\n } else {\n insertAfter(from, id)\n from = id\n }\n }\n }\n }\n\n const placed: string[] = []\n for (let id = head; id !== undefined; id = next.get(id)) {\n placed.push(id)\n }\n return placed\n}\n\n// The nodes no walk visits, which is every off-reference node of a graph with\n// no walks: each goes beside the nearest placed node that reaches it (BFS over\n// the links as undirected, seeded in order so a tie goes left).\n//\n// That search meets itself in the middle of an allele, so the far half of a\n// chain A>X1>X2>B is claimed from B, its RIGHT anchor. Placed after B, as every\n// claimed node once was, X2 sorted past the node it leads into, its link was\n// turned round, and the insertion stopped being a bubble. So a run of nodes\n// claimed from one placed node lies BEFORE it, farthest node first, when it is\n// such a far half: it touches nothing right of that node, and every dead end of\n// the search inside it is a place the search from the left arrived. Steps are\n// a position only along a chain. In a tangle the farthest node is a dead end\n// with nothing before it, and sorted first its links to its real neighbours\n// sweep across every boundary between, so any other run stays after.\nfunction placeBySearch(\n adjacent: Map<string, string[]>,\n key: Map<string, OrderKey>,\n) {\n const queue = [...key.entries()]\n .sort(([a, ka], [b, kb]) => compareKeys(ka, kb) || compare(a, b))\n .map(([id]) => id)\n const placed = queue.length\n // the queue grows while the loop walks it, which for-of follows\n for (const id of queue) {\n const from = key.get(id)!\n for (const nb of adjacent.get(id) ?? []) {\n if (!key.has(nb)) {\n key.set(nb, { at: from.at, side: 1, hops: from.hops + 1 })\n queue.push(nb)\n }\n }\n }\n\n const searched = new Set(queue.slice(placed))\n const sided = new Set<string>()\n for (const seed of searched) {\n if (sided.has(seed)) {\n continue\n }\n const { at } = key.get(seed)!\n const run = [seed]\n sided.add(seed)\n let farHalf = true\n for (const id of run) {\n const { hops } = key.get(id)!\n let deadEnd = true\n let touchesLeft = false\n for (const nb of adjacent.get(id) ?? []) {\n const other = key.get(nb)!\n if (other.at === at && searched.has(nb)) {\n deadEnd &&= other.hops <= hops\n if (!sided.has(nb)) {\n sided.add(nb)\n run.push(nb)\n }\n } else if (other.at !== at) {\n touchesLeft ||= other.at < at\n farHalf &&= other.at < at\n }\n }\n farHalf &&= !deadEnd || touchesLeft\n }\n if (farHalf) {\n for (const id of run) {\n const k = key.get(id)!\n key.set(id, { ...k, side: -1, hops: -k.hops })\n }\n }\n }\n}\n\nfunction orderKeys(graph: Graph) {\n const ids = new Set(graph.nodes.map(n => n.id))\n const adjacent = new Map<string, string[]>()\n for (const e of graph.edges) {\n if (!ids.has(e.from) || !ids.has(e.to)) {\n continue\n }\n ;(adjacent.get(e.from) ?? adjacent.set(e.from, []).get(e.from)!).push(e.to)\n ;(adjacent.get(e.to) ?? adjacent.set(e.to, []).get(e.to)!).push(e.from)\n }\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort(\n (a, b) =>\n compare(a.stable.start, b.stable.start) || compare(a.name, b.name),\n )\n const key = new Map<string, OrderKey>()\n placeByWalks(graph, backbone).forEach((id, rank) => {\n key.set(id, { at: rank, side: 0, hops: 0 })\n })\n placeBySearch(adjacent, key)\n return key\n}\n\nexport interface LayeredGraph {\n layers: string[][]\n layerOf: Map<string, number>\n // predecessors in layer order, for the y sweep\n preds: Map<string, string[]>\n}\n\n// The layered DAG the ordered layout draws and the bubble decomposition reads\n// (bubbles/bubblesFromGraph.ts), from one function so the two cannot disagree\n// about which nodes share a layer.\nexport function layerGraph(graph: Graph): LayeredGraph {\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const order = referenceOrder(graph)\n const key = new Map(order.map((id, i) => [id, i]))\n const isRef = (id: string) => isBackbone(byId.get(id)!)\n\n // Every edge directed from lower reference key to higher, so the graph is\n // acyclic by construction and no cycle-removal heuristic can misplace the\n // reference.\n const preds = new Map<string, string[]>(order.map(id => [id, []]))\n const succs = new Map<string, string[]>(order.map(id => [id, []]))\n const link = (a: string, b: string) => {\n preds.get(b)!.push(a)\n succs.get(a)!.push(b)\n }\n for (const e of graph.edges) {\n if (e.from === e.to || !key.has(e.from) || !key.has(e.to)) {\n continue\n }\n if (key.get(e.from)! < key.get(e.to)!) {\n link(e.from, e.to)\n } else {\n link(e.to, e.from)\n }\n }\n // The backbone chained by virtual edges, so the reference stays monotone even\n // where adjacent segments are not linked inside the cut.\n const backbone = order.filter(isRef)\n for (let i = 1; i < backbone.length; i++) {\n link(backbone[i - 1]!, backbone[i]!)\n }\n\n // Longest-path layering; the key order is a topological order of this DAG.\n const layerOf = new Map<string, number>()\n for (const id of order) {\n let l = 0\n for (const p of preds.get(id)!) {\n l = Math.max(l, layerOf.get(p)! + 1)\n }\n layerOf.set(id, l)\n }\n // Then each allele slides right to centre between the layer its predecessors\n // force and the one its successors allow, so a short allele sits in the\n // middle of its bubble rather than hugging the left anchor.\n for (let i = order.length - 1; i >= 0; i--) {\n const id = order[i]!\n const s = succs.get(id)!\n if (isRef(id) || s.length === 0) {\n continue\n }\n let minSucc = Infinity\n for (const t of s) {\n minSucc = Math.min(minSucc, layerOf.get(t)!)\n }\n const lo = layerOf.get(id)!\n const hi = Math.max(lo, minSucc - 1)\n layerOf.set(id, Math.floor((lo + hi) / 2))\n }\n\n const layers: string[][] = []\n for (const id of order) {\n ;(layers[layerOf.get(id)!] ??= []).push(id)\n }\n return { layers, layerOf, preds }\n}\n\nexport function orderedLayout(graph: Graph): LayoutResult | undefined {\n if (!graph.nodes.some(isBackbone)) {\n return undefined\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const isRef = (id: string) => isBackbone(byId.get(id)!)\n const { layers, layerOf: layer, preds } = layerGraph(graph)\n\n const layerWidth = layers.map(ids => {\n let w = 0\n for (const id of ids) {\n w = Math.max(w, nodeWidth(byId.get(id)!))\n }\n return w\n })\n const layerX: number[] = []\n let x = 0\n for (let l = 0; l < layers.length; l++) {\n layerX[l] = x\n x += layerWidth[l]! + GUTTER\n }\n\n // y: sweep the layers left to right, each allele at the barycenter of its\n // placed predecessors, then at the nearest FREE lane to it. Lane 0 is the\n // reference line and stays reserved even in a layer with no backbone node,\n // or an allele between two backbone segments reads as reference.\n const y = new Map<string, number>()\n for (const ids of layers) {\n const free: { id: string; ideal: number }[] = []\n for (const id of ids) {\n if (isRef(id)) {\n y.set(id, 0)\n continue\n }\n const placed = preds.get(id)!.filter(p => y.has(p))\n let ideal = 0\n for (const p of placed) {\n ideal += y.get(p)! / placed.length\n }\n // a node straight off the backbone wants to leave it, not sit on it\n free.push({ id, ideal: ideal === 0 ? LANE_PX : ideal })\n }\n free.sort((a, b) => a.ideal - b.ideal)\n const taken = new Set([0])\n for (const { id, ideal } of free) {\n let lane = Math.round(ideal / LANE_PX)\n if (lane === 0) {\n lane = ideal >= 0 ? 1 : -1\n }\n for (let d = 1; taken.has(lane); d++) {\n if (!taken.has(lane + d)) {\n lane += d\n } else if (!taken.has(lane - d)) {\n lane -= d\n }\n }\n taken.add(lane)\n y.set(id, lane * LANE_PX)\n }\n }\n\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const n of graph.nodes) {\n const l = layer.get(n.id)!\n const w = nodeWidth(n)\n const cx = layerX[l]! + layerWidth[l]! / 2\n const ny = y.get(n.id)!\n nodePositions[n.id] = [\n { x: cx - w / 2, y: ny },\n { x: cx + w / 2, y: ny },\n ]\n }\n return { nodePositions, referenceAxis: false, pixelRows: true }\n}\n", "import { placeOffReference } from './placeOffReference'\nimport { ROW_HEIGHT_PX } from './rowSpacing'\nimport { parsePanSN } from '../alleleProjection/projectAlleles'\nimport {\n backboneNodes,\n backbonePositions,\n isOffReference,\n referenceSpan,\n} from '../anchoredNodes'\n\nimport type { Graph, LayoutResult, RowLabel } from '../types'\n\n// One row per contributing assembly, x on the reference.\n//\n// The anchored layout rows by *rank*, which is a property of the graph's\n// construction order rather than of any sample: at an HPRC locus rank 1 holds\n// alleles from dozens of different haplotypes, so a row there means nothing\n// biological. This layout rows by the assembly each allele came from, so a row\n// is one sample and reading across it says what that sample does to the\n// reference: a deletion leaves the row empty across the span it removes, and an\n// insertion is a mark at the point it attaches. Insertion *size* is deliberately\n// not on this axis \u2014 see placeOffReference.\n//\n// Positions come from `projectAlleles`, which derives them from the stable\n// coordinates and the L-lines alone. rGFA has no W/P records to consult, so\n// anything requiring walks could not run on the format the graph tracks read.\n//\n// What a row means differs with where the coordinates came from, and the\n// difference is not cosmetic. On rGFA a segment's SN names the assembly that\n// *first contributed* it (SR is build order), so a row is first-seen\n// attribution. On a path-anchored graph it names an assembly that actually\n// carries the segment. Carriage is a set, though, and a row here is one node's\n// one position: where several assemblies carry a segment, it draws on the row\n// of the first path in the file that visits it, and `GraphNode.samples` holds\n// the rest. Drawing a segment once per carrier needs the layout to emit more\n// nodes than the graph has, which the renderer keys by node id and cannot do.\n\n// Every assembly with a segment in this subgraph, ordered by how much\n// off-reference sequence it contributes here, most first. Taken from the nodes\n// rather than from the projection's per-allele attribution: a bubble path can\n// mix assemblies, and a segment belongs on the row of the assembly its own\n// stable name gives it, not on the row of whichever contributor happens to\n// dominate the run it sits in.\n//\n// Read off the same field `rowY` places by, so every row that exists has nodes\n// on it and every node lands on a row that exists.\n//\n// **Sorted by content rather than by name**, which is the change and the\n// tradeoff. Alphabetical put `HG00099` above `HG00280` for no reason a reader\n// could see, so a row's neighbours said nothing and the stack had to be read one\n// row at a time. By contributed sequence it reads the way a sorted pileup does:\n// the haplotypes doing the most to the reference are together at the top, and\n// how far down the stack the marks stop IS how many samples carry anything.\n//\n// What that costs is comparing one window against another \u2014 the order is a fact\n// about the window, so a sample is not in the same place in two of them. That is\n// the case \u00A78's \"a row set cannot be requested\" is for, and an explicit sample\n// list is the right answer to it rather than an order nobody can read. A\n// follow's re-cut is the exception it can answer already: `keep` is the rows on\n// screen, those samples hold their order, and a sample new to the window goes\n// below them by content.\n//\n// Name breaks ties, so the order is still deterministic and a pan that changes\n// nothing about the content reshuffles nothing.\nfunction contributingSamples(graph: Graph, keep: readonly string[]) {\n const carried = new Map<string, number>()\n for (const node of graph.nodes) {\n if (isOffReference(node)) {\n const { sample } = parsePanSN(node.stable.refName)\n carried.set(sample, (carried.get(sample) ?? 0) + node.length)\n }\n }\n const held = new Map(keep.map((sample, row) => [sample, row]))\n return [...carried.keys()].sort(\n (a, b) =>\n (held.get(a) ?? Infinity) - (held.get(b) ?? Infinity) ||\n carried.get(b)! - carried.get(a)! ||\n a.localeCompare(b),\n )\n}\n\nexport function sampleRowLayout(\n graph: Graph,\n region?: { start: number; end: number },\n keep: readonly string[] = [],\n): LayoutResult | undefined {\n const backbone = backboneNodes(graph)\n const samples = contributingSamples(graph, keep)\n if (backbone.length === 0 || samples.length === 0) {\n return undefined\n }\n const span = referenceSpan(backbone, region)\n\n // The backbone keeps row 0 and every sample gets a row below it, in the\n // order contributingSamples gives.\n const rowOf = new Map(samples.map((s, i) => [s, i + 1]))\n const nodePositions = backbonePositions(backbone)\n\n // `placeOffReference` also reaches nodes with no coordinate at all: it walks\n // out from what it placed to close a run that leaves the fetched window, and\n // on a path-anchored graph a segment no P or W line visits never got a\n // `stable` to begin with. That is legal GFA rather than a broken file, so\n // asserting the coordinate here threw a bare \"cannot read properties of\n // undefined\" out of the layout and left the view showing it instead of a\n // graph. Such a node belongs on no sample's row, so it takes the spare row\n // below the named ones, which is what the sibling layout does with an\n // unrecognised rank.\n const spareRow = samples.length + 1\n const alleleDeletions = placeOffReference({\n graph,\n span,\n rowY: node => {\n const stable = node.stable\n const row = stable\n ? rowOf.get(parsePanSN(stable.refName).sample)\n : undefined\n return (row ?? spareRow) * ROW_HEIGHT_PX\n },\n positions: nodePositions,\n })\n\n // Built from the same `rowOf` map that placed the alleles, so a label cannot\n // name a row the layout put somewhere else. Row 0 is the backbone, named for\n // the assembly it comes from rather than \"reference\", because on this layout\n // that name is one of the samples and reads as a peer of the rows below it.\n const referenceSample = parsePanSN(backbone[0]!.stable.refName).sample\n const rowLabels: RowLabel[] = [\n { label: referenceSample, y: 0 },\n ...samples.map(sample => ({\n label: sample,\n y: rowOf.get(sample)! * ROW_HEIGHT_PX,\n })),\n ]\n\n return {\n nodePositions,\n rowLabels,\n sampleRows: samples,\n referenceAxis: true,\n pixelRows: true,\n alleleDeletions,\n }\n}\n", "export function emptyTrackShapes() {\n return {\n rectangles: [],\n curves: [],\n corners: [],\n verticalRectangles: [],\n featureRectangles: [],\n };\n}\n", "// The tube map layout, from input nodes and tracks to drawable shapes. Ported\n// from the original sequenceTubeMap JS, where it shared one module with the d3\n// drawing; the passes below still share module state, but layoutTubeMap resets\n// all of it on entry and runs synchronously, so no two calls see each other's.\nimport { emptyTrackShapes } from './types.js';\nconst DEBUG = false;\nconst DEFAULT_TRACK_COLORS = [\n '#1f77b4',\n '#ff7f0e',\n '#2ca02c',\n '#d62728',\n '#9467bd',\n '#8c564b',\n '#e377c2',\n '#7f7f7f',\n];\nfunction configFrom(options) {\n return {\n mergeNodesFlag: options.mergeNodes ?? true,\n showReads: options.showReads ?? true,\n coarsenedReadView: options.coarsenedReadView ?? false,\n ignoreStrand: options.ignoreStrand ?? false,\n nodeWidthOption: options.nodeWidthOption ?? 'normal',\n charWidth: options.charWidth ?? 8.401,\n trackWidth: options.trackWidth ?? 15,\n mappingQualityCutoff: options.mappingQualityCutoff ?? 0,\n focusReadNames: options.focusReadNames ?? null,\n showExonsFlag: options.showExons ?? false,\n trackColor: options.trackColor ??\n (track => DEFAULT_TRACK_COLORS[track.id % DEFAULT_TRACK_COLORS.length]),\n trackAlpha: options.trackAlpha ?? (() => 1),\n };\n}\nlet config = configFrom({});\nlet bed = null;\nlet nodes = [];\n// Each track has a `path`, which is an array of Segment objects describing pieces of the path that need to be drawn, in order along the path.\nlet tracks = [];\n// Each read also has a `path` list of Segments, but reads are organized vertically using a different system than non-read tracks.\nlet reads = [];\nlet nodeMap = new Map();\nlet nodesPerOrder = [];\n// Scratch array used only during generateNodeOrder. Indexed by node index;\n// undefined = \"not yet assigned.\" Copied into node.order at end of layout.\nlet nodeOrders = [];\n// Lane assignment info for tracks, in one list per horizontal \"order\" slot.\n// Duplicates info in tracks' `path` lists but is organized by order. Reads do not use this.\nlet assignments = [];\nlet extraLeft = [];\nlet extraRight = [];\nlet maxOrder = -1; // horizontal order of the rightmost node\nlet shapes = emptyTrackShapes();\nlet imageBounds = { minX: 0, maxX: 0, minY: 0, maxY: 0 };\nlet trackForRuler;\nlet coarsenedEdgeMeta = new Map();\n// Lay out `inputNodes` and `inputTracks` (and `inputReads`, when\n// `showReads`), or return undefined when nothing visible is left to draw.\n// Neither input is modified. Track 0 is the reference the layout straightens.\nexport function layoutTubeMap(inputNodes, inputTracks, inputReads = [], options = {}) {\n config = configFrom(options);\n bed = options.bed ?? null;\n shapes = emptyTrackShapes();\n assignments = [];\n extraLeft = [];\n extraRight = [];\n nodesPerOrder = [];\n imageBounds = { minX: 0, maxX: 0, minY: 0, maxY: 0 };\n trackForRuler = undefined;\n coarsenedEdgeMeta = new Map();\n if (inputNodes.length === 0 || inputTracks.length === 0) {\n return undefined;\n }\n // Boundary promotion: layout passes below populate the Node/Track fields\n // (width, order, x/y, path, indexSequence, etc.) before any reader runs.\n const copies = deepCopy(inputNodes);\n // Nodes are referenced in inputs by internal `name` attribute and not by\n // index. Internally in e.g. a path's indexSequence we need to reference\n // nodes by *signed* index, so index 0 can never be used: budge everything\n // down and leave a hole (rather than an `undefined` entry) at 0, which we\n // won't iterate over. Made after the copy, because not every structuredClone\n // keeps a hole.\n nodes = [];\n copies.forEach((node, i) => {\n nodes[i + 1] = node;\n });\n tracks = deepCopy(inputTracks);\n // Whether any reads were loaded at all, distinct from `reads.length` below:\n // a mapping-quality cutoff or focus-name filter can filter every read out,\n // and that should not make the coarsened view fall back to bunching\n // haplotypes instead \u2014 the graph does have reads, they're just all hidden.\n const hadInputReads = inputReads.length > 0;\n // Drop the reads we will never draw before cloning them \u2014 the deep copy of a\n // large GAM is the single most expensive step in a redraw.\n reads = deepCopy(filterReads(inputReads));\n for (let i = tracks.length - 1; i >= 0; i -= 1) {\n const t = tracks[i];\n if (t.type === undefined) {\n t.type = 'haplotype';\n }\n if (t.hidden === true) {\n tracks.splice(i, 1);\n continue;\n }\n if (t.indexOfFirstBase !== undefined) {\n trackForRuler = t.name;\n }\n }\n if (tracks.length === 0) {\n return undefined;\n }\n // Run against the visible tracks only, so hiding the reference doesn't leave\n // the layout straightened around a track that is no longer drawn.\n straightenTrack(0);\n nodeMap = generateNodeMap();\n generateTrackIndexSequences(tracks);\n if (config.showReads && reads.length > 0)\n generateTrackIndexSequences(reads);\n generateNodeWidth();\n if (config.mergeNodesFlag) {\n generateNodeSuccessors();\n generateNodeOrder();\n if (config.showReads && reads.length > 0)\n reverseReversedReads();\n mergeNodes();\n nodeMap = generateNodeMap();\n generateNodeWidth();\n generateTrackIndexSequences(tracks);\n if (config.showReads && reads.length > 0)\n generateTrackIndexSequences(reads);\n }\n generateNodeSuccessors();\n generateNodeDegree();\n if (DEBUG)\n console.log(`${nodes.length} nodes.`);\n generateNodeOrder();\n maxOrder = getMaxOrder();\n // can cause problems when there is a reversed single track node\n // OTOH, can solve problems with complex inversion patterns\n switchNodeOrientation();\n generateNodeOrder();\n maxOrder = getMaxOrder();\n // Coarsened (Sankey) mode normally collapses the *read* list into synthetic\n // per-edge bands (below). A haplotype-only graph has no reads to coarsen,\n // so a coarsened request there instead coarsens every haplotype but the\n // reference: that one keeps its normal per-track lane (so it still carries\n // the ruler) while the rest are pulled out of `tracks` here, before\n // calculateTrackWidth/generateLaneAssignment ever see them, and rejoin the\n // layout below through the same reads-style overlay used for coarsened\n // reads.\n const coarsenHaplotypes = config.showReads && config.coarsenedReadView && !hadInputReads;\n if (coarsenHaplotypes) {\n const rulerIndex = trackForRuler === undefined\n ? -1\n : tracks.findIndex(t => t.name === trackForRuler);\n const refIndex = rulerIndex === -1 ? 0 : rulerIndex;\n const altHaplotypes = tracks.filter((_, i) => i !== refIndex);\n if (altHaplotypes.length > 0) {\n const bands = buildCoarsenedSyntheticBands(altHaplotypes, 'haplotype');\n // A haplotype that only ever visits one node produces no edge, so an\n // all-single-node set of alts would otherwise leave `reads` empty and\n // fall through to the no-overlay branch below, which never gives their\n // nodes a y/contentHeight. Keep them off to the side instead of\n // dropping them silently.\n if (bands.length > 0) {\n tracks = [tracks[refIndex]];\n reads = bands;\n }\n }\n }\n calculateTrackWidth();\n generateLaneAssignment();\n if (config.showExonsFlag && bed !== null)\n addTrackFeatures();\n // Coarsened (Sankey) mode: collapse the read list (or, when coarsening\n // haplotypes, the alt haplotypes pulled out above) to one synthetic \"read\"\n // per (srcSigned \u2192 dstSigned) edge BEFORE the normal read placement runs.\n // Each synthetic read traverses exactly two nodes, so the rest of the\n // pipeline (placeReads, generateSVGShapesFromPath, curve drawing) handles\n // it like any normal read: it gets a lane, picks up the \"right-down-left-\n // up-right\" topology for loops automatically, and uses the same elegant\n // bezier. Node heights end up proportional to *edge* count (typically\n // tens) rather than *read* or *haplotype* count (potentially thousands).\n const drawCoarsenedReads = config.showReads &&\n config.coarsenedReadView &&\n reads.length > 0 &&\n !coarsenHaplotypes;\n if (config.showReads && reads.length > 0) {\n generateReadOnlyNodeAttributes();\n reverseReversedReads();\n generateTrackIndexSequences(reads);\n if (drawCoarsenedReads) {\n reads = buildCoarsenedSyntheticBands(reads, 'read');\n reverseReversedReads();\n generateTrackIndexSequences(reads);\n }\n placeReads();\n tracks = tracks.concat(reads);\n }\n else {\n nodes.forEach(node => {\n if (node) {\n node.incomingReads = [];\n node.outgoingReads = [];\n node.internalReads = [];\n }\n });\n }\n generateNodeXCoords();\n generateSVGShapesFromPath();\n if (DEBUG) {\n console.log('Tracks:');\n console.log(tracks);\n console.log('Nodes:');\n console.log(nodes);\n console.log('Lane assignment:');\n console.log(assignments);\n }\n getImageDimensions();\n return {\n nodes,\n tracks,\n reads,\n nodeMap,\n shapes,\n bounds: imageBounds,\n maxOrder,\n trackForRuler,\n coarsenedEdgeMeta,\n };\n}\nfunction generateTrackColor(track, highlight = 'plain') {\n return config.trackColor(track, highlight);\n}\nfunction generateTrackAlpha(track) {\n return config.trackAlpha(track);\n}\n// structuredClone preserves sparse-array holes; JSON round-trip would fill them with null.\nfunction deepCopy(val) {\n return structuredClone(val);\n}\n// Return true if the given name names a reverse strand node, and false otherwise.\nexport function isReverse(nodeName) {\n const s = String(nodeName);\n return s.length >= 1 && s.startsWith('-');\n}\n// Get the forward version of a node name, which may be either forward or backward (negative)\nexport function forward(nodeName) {\n return isReverse(nodeName) ? String(nodeName).substring(1) : nodeName;\n}\n// Get the reverse version of a node name, which may be either forward or backward (negative)\nexport function reverse(nodeName) {\n return isReverse(nodeName) ? nodeName : `-${nodeName}`;\n}\n// Get the opposite orientation node name for the given node.\nexport function flip(nodeName) {\n return isReverse(nodeName) ? forward(nodeName) : reverse(nodeName);\n}\n// Signed-index orientation test. Uses 1/n to distinguish -0 from +0 since\n// indexSequence entries can be negative-zero for reverse-strand visits of node 0.\nexport function isForwardIndex(n) {\n return (n || 1 / n) >= 0;\n}\n// straighten track given by index by inverting inverted nodes\n// only keep them inverted if this single track runs through them in both directions\n// Operates on the per-render `nodes`/`tracks` copies; inputs are left untouched.\nfunction straightenTrack(index) {\n const nodesToInvert = new Set();\n // find out which nodes should be inverted\n let currentSequence = tracks[index].sequence;\n for (let i = 0; i < currentSequence.length; i += 1) {\n const cur = currentSequence[i];\n if (isReverse(cur)) {\n const nodeName = forward(cur);\n const firstForwardIndex = currentSequence.indexOf(nodeName);\n if (firstForwardIndex === -1 || firstForwardIndex > i) {\n // only if this inverted node is no repeat\n nodesToInvert.add(nodeName);\n }\n }\n }\n // invert nodes in the tracks' sequence\n for (const track of tracks) {\n currentSequence = track.sequence;\n for (let j = 0; j < currentSequence.length; j += 1) {\n const cur = currentSequence[j];\n if (!isReverse(cur)) {\n if (nodesToInvert.has(cur)) {\n currentSequence[j] = reverse(cur);\n }\n }\n else if (nodesToInvert.has(forward(cur))) {\n currentSequence[j] = forward(cur);\n }\n }\n }\n // invert the sequence within the nodes\n nodes.forEach(node => {\n if (node && nodesToInvert.has(node.name)) {\n node.seq = node.seq.split('').reverse().join('');\n }\n });\n}\n// generates attributes (node.y, node.contentHeight) for nodes without tracks, only reads\nfunction generateReadOnlyNodeAttributes() {\n nodesPerOrder = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n nodesPerOrder[i] = [];\n }\n const orderY = new Map();\n nodes.forEach(node => {\n if (node.y !== undefined) {\n setMapToMax(orderY, node.order, node.y + node.contentHeight);\n }\n });\n // for order values where there is no node with haplotypes, orderY is calculated via tracks\n tracks.forEach(track => {\n if (track.type === 'haplotype') {\n track.path.forEach(step => {\n setMapToMax(orderY, step.order, (step.y ?? 0) + track.width);\n });\n }\n });\n nodes.forEach((node, i) => {\n if (node.order >= 0 && node.y === undefined) {\n node.y = (orderY.get(node.order) ?? 0) + 25;\n node.contentHeight = 0;\n nodesPerOrder[node.order].push(i);\n }\n });\n}\nfunction setMapToMax(map, key, value) {\n const existing = map.get(key);\n if (existing === undefined) {\n map.set(key, value);\n }\n else {\n map.set(key, Math.max(existing, value));\n }\n}\nexport const READ_WIDTH = 7;\n// add info about reads to nodes (incoming, outgoing and internal reads)\nfunction assignReadsToNodes() {\n nodes.forEach(node => {\n if (node) {\n node.incomingReads = [];\n node.outgoingReads = [];\n node.internalReads = [];\n }\n });\n reads.forEach((read, idx) => {\n // Honor a pre-set width (e.g. coarsened Sankey bands pre-size themselves\n // by traversing-read count) \u2014 only fall back to the default for normal\n // reads which leave width unset.\n if (read.width === undefined || read.width === 0) {\n read.width = READ_WIDTH;\n }\n if (read.path.length === 1) {\n const firstNode = read.path[0].node;\n if (firstNode !== null) {\n nodes[firstNode].internalReads.push(idx);\n }\n }\n else {\n read.path.forEach((element, pathIdx) => {\n if (pathIdx === 0) {\n const firstNode = read.path[0].node;\n if (firstNode !== null) {\n nodes[firstNode].outgoingReads.push([idx, pathIdx]);\n }\n }\n else {\n const elemNode = element.node;\n if (elemNode !== null) {\n nodes[elemNode].incomingReads.push([idx, pathIdx]);\n }\n }\n });\n }\n });\n}\n// calculate paths (incl. correct y coordinate) for all reads\nfunction placeReads() {\n generateBasicPathsForReads();\n assignReadsToNodes();\n // sort nodes by order, then by y-coordinate\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n // Organize read IDs by source track\n const readsBySource = new Map();\n for (let i = 0; i < reads.length; i++) {\n const source = reads[i].sourceTrackID;\n const bucket = readsBySource.get(source);\n if (bucket === undefined) {\n readsBySource.set(source, [i]);\n }\n else {\n bucket.push(i);\n }\n }\n const allSources = Array.from(readsBySource.keys()).sort((a, b) => a - b);\n // Space out read tracks if multiple exist\n const topMargin = allSources.length > 1 ? READ_WIDTH : 0;\n sortedNodes.forEach(node => {\n if (!node)\n return;\n // For each node\n for (const source of allSources) {\n // Go through all source tracks in order\n // Place the reads from this source in this node.\n // Use a margin to separate multiple read tracks if we have them.\n placeReadSet(readsBySource.get(source), node, topMargin);\n }\n });\n // place read segments which are without node\n const bottomY = calculateBottomY();\n const elementsWithoutNode = [];\n reads.forEach((read, idx) => {\n const len = read.path.length;\n // For each path index, precompute the nearest preceding/following segment\n // whose node is non-null and truthy. Falls back to the boundary segment when\n // none qualifies, matching the original walking-loop semantics.\n const prevSegment = new Array(len);\n let lastValidPrev;\n for (let i = 0; i < len; i++) {\n if (i === 0) {\n prevSegment[i] = undefined;\n }\n else {\n prevSegment[i] = lastValidPrev ?? read.path[0];\n }\n const seg = read.path[i];\n if (seg.node !== null && seg.node)\n lastValidPrev = seg;\n }\n const nextSegment = new Array(len);\n let firstValidNext;\n for (let i = len - 1; i >= 0; i--) {\n if (i === len - 1) {\n nextSegment[i] = undefined;\n }\n else {\n nextSegment[i] = firstValidNext ?? read.path[len - 1];\n }\n const seg = read.path[i];\n if (seg.node !== null && seg.node)\n firstValidNext = seg;\n }\n read.path.forEach((element, pathIdx) => {\n if (element.y === undefined) {\n const previousVisitToNode = prevSegment[pathIdx];\n const nextVisitToNode = nextSegment[pathIdx];\n // Specifically referring to segments between a cycle that's traversing from right to left\n const betweenCycleReverseTraversal = Boolean(\n // A segment can be between a cycle if it there are nodes on both sides\n nextVisitToNode &&\n previousVisitToNode &&\n nextVisitToNode.isForward !== undefined &&\n previousVisitToNode.isForward !== undefined &&\n // A segment is between a cycle if the next node it visits is behind the previous node it visited\n (previousVisitToNode.order > nextVisitToNode.order ||\n // A segment can also be between a cycle if it's visiting the same node it just visited in the same direction\n (nextVisitToNode.order === previousVisitToNode.order &&\n nextVisitToNode.isForward === previousVisitToNode.isForward)));\n element.betweenCycleReverseTraversal = betweenCycleReverseTraversal;\n elementsWithoutNode.push({\n readIndex: idx,\n pathIndex: pathIdx,\n previousY: previousVisitToNode?.y,\n previousNode: previousVisitToNode?.node,\n });\n }\n });\n });\n elementsWithoutNode.sort(compareNoNodeReads);\n elementsWithoutNode.forEach(element => {\n const read = reads[element.readIndex];\n const segment = read.path[element.pathIndex];\n segment.y = bottomY[segment.order];\n bottomY[segment.order] += read.width;\n });\n if (DEBUG) {\n console.log('Reads:');\n console.log(reads);\n }\n}\n// Place a particular collection of reads, identified by a list of read\n// numbers, into the given node at the right Y coordinates. All reads in all\n// nodes above it, and no reads in any nodes below it, are already placed.\n// Makes the given node bigger if needed and moves other nodes down if needed.\n// If topMargin is set, applies that amount of spacing down from whatever is above the reads.\nfunction placeReadSet(readIDs, node, topMargin) {\n // Turn the read IDs into a set\n const toPlace = new Set(readIDs);\n // Get arrays of the read entry/exit/internal-ness records we want to work on\n let incomingReads = node.incomingReads.filter(([readID]) => toPlace.has(readID));\n const outgoingReads = node.outgoingReads.filter(([readID]) => toPlace.has(readID));\n const internalReads = node.internalReads.filter(readID => toPlace.has(readID));\n // Only actually use the top margin if we have any reads on the node.\n if (incomingReads.length === 0 &&\n outgoingReads.length === 0 &&\n internalReads.length === 0) {\n topMargin = 0;\n }\n // Determine where we start vertically in the node.\n // TODO: Why do we have to double this to keep reads out of adjacent lanes???\n const startY = node.y + node.contentHeight + topMargin * 2;\n // sort incoming reads \u2014 decorate with a precomputed key so each compare is O(1).\n // The original comparator walks pathIdx backwards in lockstep on both sides, picking\n // the first step where either has a defined y (or reaches index 0). The walk's outcome\n // for a single entry is fully captured by:\n // decisionStep: the smallest k>=1 where path[pathIdx-k].y is defined, or pathIdx+1\n // if no preceding segment has a defined y (the \"reached 0\" case).\n // y: the defined y at decisionStep, or undefined for \"reached 0\".\n // Smaller decisionStep wins; ties put the \"reached 0\" entries first;\n // otherwise compare y's.\n const incomingKeys = incomingReads.map(entry => {\n const [readID, pathIdx] = entry;\n const path = reads[readID].path;\n let decisionStep = pathIdx + 1;\n let foundY;\n for (let k = 1; k <= pathIdx; k++) {\n const y = path[pathIdx - k]?.y;\n if (y !== undefined) {\n decisionStep = k;\n foundY = y;\n break;\n }\n }\n return { entry, decisionStep, y: foundY };\n });\n incomingKeys.sort(compareIncomingReadKeys);\n incomingReads = incomingKeys.map(k => k.entry);\n // place incoming reads\n let currentY = startY;\n const occupiedUntil = new Map();\n incomingReads.forEach(readElement => {\n const read = reads[readElement[0]];\n read.path[readElement[1]].y = currentY;\n setOccupiedUntil(occupiedUntil, read, readElement[1], currentY, node);\n currentY += read.width;\n });\n let maxY = currentY;\n // sort outgoing reads\n outgoingReads.sort(compareReadOutgoingSegmentsByGoingTo);\n // place outgoing reads\n const occupiedFrom = new Map();\n currentY = startY;\n outgoingReads.forEach(readElement => {\n const read = reads[readElement[0]];\n const firstNodeOffset = read.firstNodeOffset ?? 0;\n // place in next lane\n read.path[readElement[1]].y = currentY;\n occupiedFrom.set(currentY, firstNodeOffset);\n // if no conflicts. The original predicate was strict-less-than, which\n // treats two reads that exactly touch (incoming ends at base K, outgoing\n // starts at base K+1) as conflicting. They don't overlap, so they can\n // share a lane \u2014 relax to <=. This also matters for coarsened Sankey\n // bands at single-base nodes (e.g. a 1bp \"G\" node): with edge-only offsets\n // the math becomes 0+1 < 1 (conflict, wrong) vs. 0+1 <= 1 (no conflict).\n const occUntil = occupiedUntil.get(currentY);\n if (occUntil === undefined || occUntil + 1 <= firstNodeOffset) {\n currentY += read.width;\n maxY = Math.max(maxY, currentY);\n }\n else {\n // otherwise push down incoming reads to make place for outgoing Read\n occupiedUntil.set(currentY, 0);\n incomingReads.forEach(incReadElementIndices => {\n const incRead = reads[incReadElementIndices[0]];\n const incReadPathElement = incRead.path[incReadElementIndices[1]];\n if (incReadPathElement.y !== undefined &&\n incReadPathElement.y >= currentY) {\n incReadPathElement.y += read.width;\n setOccupiedUntil(occupiedUntil, incRead, incReadElementIndices[1], incReadPathElement.y, node);\n }\n });\n currentY += read.width;\n maxY += read.width;\n }\n });\n // sort internal reads\n internalReads.sort(compareInternalReads);\n // place internal reads\n internalReads.forEach(readIdx => {\n const currentRead = reads[readIdx];\n const firstNodeOffset = currentRead.firstNodeOffset ?? 0;\n const finalNodeCoverLength = currentRead.finalNodeCoverLength ?? 0;\n currentY = startY;\n while (firstNodeOffset < (occupiedUntil.get(currentY) ?? -Infinity) + 2 ||\n finalNodeCoverLength > (occupiedFrom.get(currentY) ?? Infinity) - 3) {\n currentY += currentRead.width || READ_WIDTH;\n }\n currentRead.path[0].y = currentY;\n occupiedUntil.set(currentY, finalNodeCoverLength);\n maxY = Math.max(maxY, currentY);\n });\n // adjust node height and move other nodes vertically down\n const heightIncrease = maxY - node.y - node.contentHeight;\n node.contentHeight += heightIncrease;\n adjustVertically3(node, heightIncrease);\n}\n// Smaller decisionStep wins. On a tie, entries that walked off the front of the\n// path (no defined y) sort first and tie with each other; otherwise compare y.\nexport function compareIncomingReadKeys(a, b) {\n if (a.decisionStep !== b.decisionStep) {\n return a.decisionStep - b.decisionStep;\n }\n if (a.y === undefined || b.y === undefined) {\n return (a.y === undefined ? 0 : 1) - (b.y === undefined ? 0 : 1);\n }\n return a.y - b.y;\n}\n// keeps track of where reads end within nodes\nfunction setOccupiedUntil(map, read, pathIndex, y, node) {\n if (pathIndex === read.path.length - 1) {\n // last node of current read\n map.set(y, read.finalNodeCoverLength ?? 0);\n }\n else {\n // read covers the whole node\n map.set(y, node.sequenceLength);\n }\n}\n// compare read segments which are outside of nodes to sort them in a good horizontal\n// and then vertical display order.\nfunction compareNoNodeReads(a, b) {\n const readA = reads[a.readIndex];\n const readB = reads[b.readIndex];\n const segmentA = readA.path[a.pathIndex];\n const segmentB = readB.path[b.pathIndex];\n // Sort by order by segments\n if (segmentA.order !== segmentB.order) {\n return segmentA.order - segmentB.order;\n }\n // Sort by reads' source track\n if (readA.sourceTrackID !== readB.sourceTrackID) {\n return readA.sourceTrackID - readB.sourceTrackID;\n }\n // Sort by order of previous node\n if (a.previousNode && b.previousNode) {\n const prevNodeA = nodes[a.previousNode];\n const prevNodeB = nodes[b.previousNode];\n if (prevNodeA && prevNodeB && prevNodeA.order !== prevNodeB.order) {\n return prevNodeA.order - prevNodeB.order;\n }\n }\n // We want to sort in reverse order when the segment is along the reverse-going part of a cycle.\n // This ensures a loop that starts on the outside, stays on the outside,\n // and rolls up in order with other loops.\n const aPrev = a.previousY ?? 0;\n const bPrev = b.previousY ?? 0;\n if (segmentA.betweenCycleReverseTraversal &&\n segmentB.betweenCycleReverseTraversal) {\n return bPrev - aPrev;\n }\n else {\n return aPrev - bPrev;\n }\n}\n// compare read segments by where they are going to\nfunction compareReadOutgoingSegmentsByGoingTo([readIndexA, pathIndexA], [readIndexB, pathIndexB]) {\n // Expect two arrays both containing 2 integers.\n // The first index of each array contains the read index\n // The second index of each array contains the path index\n // Segments are first sorted by the y value of their last node,\n // then by the node they end on,\n // then by length in final node\n const readA = reads[readIndexA];\n const readB = reads[readIndexB];\n let previousValidYA;\n let previousValidYB;\n let lastPathIndexA = readA.path.length - 1;\n let lastPathIndexB = readB.path.length - 1;\n while (!previousValidYA && lastPathIndexA >= 0) {\n previousValidYA = readA.path[lastPathIndexA]?.y;\n lastPathIndexA -= 1;\n }\n while (!previousValidYB && lastPathIndexB >= 0) {\n previousValidYB = readB.path[lastPathIndexB]?.y;\n lastPathIndexB -= 1;\n }\n if (previousValidYA && previousValidYB) {\n return previousValidYA - previousValidYB;\n }\n // Couldn't find a valid y value for at least one of the reads, sort by which node reads end on\n const initialNodeA = readA.path[pathIndexA]?.node;\n const initialNodeB = readB.path[pathIndexB]?.node;\n let nodeA = initialNodeA != null ? nodes[initialNodeA] : null;\n let nodeB = initialNodeB != null ? nodes[initialNodeB] : null;\n // Follow the reads' paths until we find the node they diverge at\n // Or, they go through all the same nodes and we do a tiebreaker at the end\n while (nodeA != null && nodeB != null && nodeA === nodeB) {\n if (pathIndexA < readA.path.length - 1) {\n pathIndexA += 1;\n while (readA.path[pathIndexA]?.node === null)\n pathIndexA += 1; // skip null nodes in path\n const nextNodeIdx = readA.path[pathIndexA]?.node;\n nodeA = nextNodeIdx != null ? nodes[nextNodeIdx] : null;\n }\n else {\n nodeA = null;\n }\n if (pathIndexB < readB.path.length - 1) {\n pathIndexB += 1;\n while (readB.path[pathIndexB]?.node === null)\n pathIndexB += 1; // skip null nodes in path\n const nextNodeIdx = readB.path[pathIndexB]?.node;\n nodeB = nextNodeIdx != null ? nodes[nextNodeIdx] : null;\n }\n else {\n nodeB = null;\n }\n }\n if (nodeA != null) {\n if (nodeB != null)\n return compareNodesByOrder(nodeA, nodeB);\n return 1; // nodeB is null, nodeA not null\n }\n if (nodeB != null)\n return -1; // nodeB not null, nodeA null\n // both nodes are null -> both end in the same node\n const beginDiff = (readA.firstNodeOffset ?? 0) - (readB.firstNodeOffset ?? 0);\n if (beginDiff !== 0)\n return beginDiff;\n // break tie: both reads cover the same nodes and begin at the same position\n // One or both reads didn't have a previously valid Y value, compare by the endPosition of the read\n return (readA.finalNodeCoverLength ?? 0) - (readB.finalNodeCoverLength ?? 0);\n}\n// Compare tracks based on ordering at their first convergence\nfunction compareTrackByInitialOrdering(trackA, trackB) {\n // Find the first node where the two tracks converge, sort by layer.\n // Tracks with no path sort first, and two of them tie.\n if (trackA.path === undefined || trackB.path === undefined) {\n return ((trackA.path === undefined ? 0 : 1) - (trackB.path === undefined ? 0 : 1));\n }\n const pathA = trackA.path;\n const pathB = trackB.path;\n let AIndex = 0;\n let BIndex = 0;\n while (AIndex < pathA.length && BIndex < pathB.length) {\n const a = pathA[AIndex];\n const b = pathB[BIndex];\n const trackAOrder = a.order;\n const trackBOrder = b.order;\n if (trackAOrder === trackBOrder && a.node && b.node) {\n return (b.y ?? 0) - (a.y ?? 0);\n }\n else if (trackAOrder < trackBOrder) {\n AIndex += 1;\n }\n else if (trackAOrder > trackBOrder) {\n BIndex += 1;\n }\n else {\n // Orders are equal but are traversing out of nodes\n AIndex += 1;\n BIndex += 1;\n }\n }\n // Tracks do not converge, keep the same ordering\n return 0;\n}\n// compare 2 reads which are completely within a single node\nfunction compareInternalReads(idxA, idxB) {\n const a = reads[idxA];\n const b = reads[idxB];\n // compare by first base within first node\n const aFirst = a.firstNodeOffset ?? 0;\n const bFirst = b.firstNodeOffset ?? 0;\n if (aFirst < bFirst)\n return -1;\n else if (aFirst > bFirst)\n return 1;\n // compare by last base within last node\n const aLast = a.finalNodeCoverLength ?? 0;\n const bLast = b.finalNodeCoverLength ?? 0;\n if (aLast < bLast)\n return -1;\n else if (aLast > bLast)\n return 1;\n return 0;\n}\n// determine biggest y-coordinate for each order-value\nfunction calculateBottomY() {\n const bottomY = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n bottomY.push(0);\n }\n nodes.forEach(node => {\n if (node.y !== undefined) {\n bottomY[node.order] = Math.max(bottomY[node.order], node.y + node.contentHeight + 20);\n }\n });\n tracks.forEach(track => {\n track.path.forEach(element => {\n bottomY[element.order] = Math.max(bottomY[element.order], (element.y ?? 0) + track.width);\n });\n });\n return bottomY;\n}\n// generate path-info for each read\n// containing order, node and orientation, but no concrete coordinates\n// TODO: Duplicates a lot of the same work as generateLaneAssignment() does for non-read tracks.\nfunction generateBasicPathsForReads() {\n reads.forEach(read => {\n // add info for start of track\n let currentNodeIndex = Math.abs(read.indexSequence[0]);\n let currentNodeIsForward = isForwardIndex(read.indexSequence[0]);\n let currentNode = nodes[currentNodeIndex];\n let previousNode;\n let previousNodeIsForward;\n read.path = [];\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n for (let i = 1; i < read.sequence.length; i += 1) {\n previousNode = currentNode;\n previousNodeIsForward = currentNodeIsForward;\n currentNodeIndex = Math.abs(read.indexSequence[i]);\n currentNodeIsForward = isForwardIndex(read.indexSequence[i]);\n currentNode = nodes[currentNodeIndex];\n if (currentNode.order > previousNode.order) {\n if (!previousNodeIsForward) {\n // backward to forward at previous node\n read.path.push({\n order: previousNode.order,\n isForward: true,\n node: null,\n });\n }\n for (let j = previousNode.order + 1; j < currentNode.order; j += 1) {\n // forward without nodes\n read.path.push({ order: j, isForward: true, node: null });\n }\n if (!currentNodeIsForward) {\n // forward to backward at current node\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: currentNodeIndex,\n });\n }\n else {\n // current Node forward\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: currentNodeIndex,\n });\n }\n }\n else if (currentNode.order < previousNode.order) {\n if (previousNodeIsForward) {\n // turnaround from fw to bw at previous node\n read.path.push({\n order: previousNode.order,\n isForward: false,\n node: null,\n });\n }\n for (let j = previousNode.order - 1; j > currentNode.order; j -= 1) {\n // bachward without nodes\n read.path.push({ order: j, isForward: false, node: null });\n }\n if (currentNodeIsForward) {\n // backward to forward at current node\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: true,\n node: currentNodeIndex,\n });\n }\n else {\n // backward at current node\n read.path.push({\n order: currentNode.order,\n isForward: false,\n node: currentNodeIndex,\n });\n }\n }\n else {\n if (currentNodeIsForward !== previousNodeIsForward) {\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n }\n else {\n read.path.push({\n order: currentNode.order,\n isForward: !currentNodeIsForward,\n node: null,\n });\n read.path.push({\n order: currentNode.order,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n }\n }\n }\n });\n}\n// reverse reads which are reversed\nfunction reverseReversedReads() {\n reads.forEach(read => {\n let pos = 0;\n while (pos < read.sequence.length && read.sequence[pos].startsWith('-')) {\n pos += 1;\n }\n if (pos === read.sequence.length) {\n // completely reversed read\n read.is_reverse = true;\n read.sequence = read.sequence.reverse(); // invert sequence\n for (let i = 0; i < read.sequence.length; i += 1) {\n read.sequence[i] = forward(read.sequence[i]); // visit nodes forward\n // TODO: Do we really want to visit all the nodes forward here? Are we\n // sure we aren't in mixed orientation?\n }\n const sequenceNew = read.sequenceNew ?? [];\n sequenceNew.reverse(); // invert sequence\n read.sequenceNew = sequenceNew;\n for (const entry of sequenceNew) {\n entry.nodeName = forward(entry.nodeName); // visit nodes forward\n reverseMismatches(entry.mismatches, nodeByName(entry.nodeName).sequenceLength);\n }\n // adjust firstNodeOffset and finalNodeCoverLength\n const temp = read.firstNodeOffset ?? 0;\n const firstLen = nodeByName(read.sequence[0]).sequenceLength;\n read.firstNodeOffset = firstLen - (read.finalNodeCoverLength ?? 0);\n const lastLen = nodeByName(read.sequence[read.sequence.length - 1]).sequenceLength;\n read.finalNodeCoverLength = lastLen - temp;\n }\n });\n}\n// Flip a node's mismatches onto the opposite strand: positions are measured\n// from the other end of the node, and sequences are complemented.\n// sequenceLength (not node.width, which is only equal to it in 'normal'\n// node-width mode) is the right pivot because mismatch positions are base\n// offsets. Exported for testing.\nexport function reverseMismatches(mismatches, sequenceLength) {\n mismatches.forEach(mm => {\n if (mm.type === 'insertion') {\n mm.pos = sequenceLength - mm.pos;\n }\n else if (mm.type === 'deletion') {\n mm.pos = sequenceLength - mm.pos - (mm.length ?? 0);\n }\n else if (mm.type === 'substitution') {\n mm.pos = sequenceLength - mm.pos - (mm.seq?.length ?? 0);\n }\n if (mm.seq !== undefined) {\n // NOTE: reverse-complement followed by reverse is a plain complement.\n // Preserved verbatim from the original code rather than \"fixed\" here.\n mm.seq = getReverseComplement(mm.seq).split('').reverse().join('');\n }\n });\n}\nconst COMPLEMENT = { A: 'T', T: 'A', C: 'G', G: 'C' };\nfunction getReverseComplement(s) {\n let result = '';\n for (let i = s.length - 1; i >= 0; i -= 1) {\n result += COMPLEMENT[s.charAt(i)] ?? 'N';\n }\n return result;\n}\n// for each track: generate sequence of node indices from seq. of node names\nfunction generateTrackIndexSequences(tracksOrReads) {\n tracksOrReads.forEach(track => {\n track.indexSequence = [];\n track.sequence.forEach(rawNodeName => {\n let nodeName = rawNodeName;\n // if node was switched, reverse it here\n // Q? Is flipping the index enough? It looks like yes. Or should we also flip the node name in 'sequence'?\n const fwdIdx = nodeMap.get(forward(nodeName));\n const switched = nodes[fwdIdx]?.switched ?? false;\n if (switched) {\n nodeName = flip(nodeName);\n }\n // Get the index to visit the node. If the node is switched, this means\n // visiting it reverse. Otherwise, this means visiting it forward.\n const nodeIndex = nodeMap.get(forward(nodeName));\n if (nodeIndex === 0) {\n // If a node index is ever 0, we can't visit it in reverse, so we don't allow that to happen.\n throw new Error('Node ' + forward(nodeName) + ' has prohibited index 0');\n }\n if (isReverse(nodeName) !== switched) {\n // If we visit the node in reverse XOR the node is switched, go through\n // it right to left as displayed.\n track.indexSequence.push(-nodeIndex);\n }\n else {\n // If either the node isn't switched and we go through it forward, or\n // the node is switched *and* we go through it backward, go through it\n // left to right as displayed.\n track.indexSequence.push(nodeIndex);\n }\n });\n });\n}\n// Tracks enter and leave a node this far outside it horizontally. Note that\n// getImageDimensions() below only adds it on the right: the pannable area\n// understates the left edge of the drawing by this much.\nconst NODE_HORIZONTAL_SLACK = 20;\n// get the minimum and maximum coordinates used in the image to calculate image dimensions\nfunction getImageDimensions() {\n // Sentinels, deliberately crossed: if nothing below runs, minZoom() and\n // alignSVG() detect the empty content rather than computing a negative scale.\n const bounds = { minX: 99, maxX: -99, minY: 99, maxY: -99 };\n nodes.forEach(node => {\n if (!node)\n return;\n if (node.x !== undefined) {\n bounds.minX = Math.min(bounds.minX, node.x);\n bounds.maxX = Math.max(bounds.maxX, node.x + NODE_HORIZONTAL_SLACK + node.pixelWidth);\n }\n if (node.y !== undefined) {\n bounds.minY = Math.min(bounds.minY, node.y - 10);\n bounds.maxY = Math.max(bounds.maxY, node.y + node.contentHeight + 10);\n }\n });\n tracks.forEach(track => {\n track.path.forEach(segment => {\n const y = segment.y ?? 0;\n bounds.maxY = Math.max(bounds.maxY, y + track.width);\n bounds.minY = Math.min(bounds.minY, y);\n });\n });\n imageBounds = bounds;\n}\n// Resolve a (possibly reverse-oriented) node name to its layout node. Every\n// caller runs after generateNodeMap, so a miss is a programming error.\nfunction nodeByName(nodeName) {\n const index = nodeMap.get(forward(nodeName));\n const node = index === undefined ? undefined : nodes[index];\n if (node === undefined) {\n throw new Error(`Unknown node ${nodeName}`);\n }\n return node;\n}\n// map node names to node indices\nfunction generateNodeMap() {\n nodeMap = new Map();\n nodes.forEach((node, index) => {\n if (node) {\n nodeMap.set(node.name, index);\n }\n });\n return nodeMap;\n}\n// adds a successor-array to each node containing the indices of the nodes coming directly after the current node\nfunction generateNodeSuccessors() {\n const successorSets = nodes.map(() => new Set());\n const predecessorSets = nodes.map(() => new Set());\n const addEdges = (track) => {\n for (let i = 0; i < track.indexSequence.length - 1; i += 1) {\n const current = Math.abs(track.indexSequence[i]);\n const follower = Math.abs(track.indexSequence[i + 1]);\n successorSets[current].add(follower);\n predecessorSets[follower].add(current);\n }\n };\n tracks.forEach(addEdges);\n if (config.showReads && reads.length > 0) {\n reads.forEach(addEdges);\n }\n nodes.forEach((node, i) => {\n if (node) {\n node.successors = Array.from(successorSets[i]);\n node.predecessors = Array.from(predecessorSets[i]);\n }\n });\n}\nfunction generateNodeOrderOfSingleTrack(sequence) {\n let forwardOrder = 0;\n let backwardOrder = 0;\n let minOrder = 0;\n sequence.forEach(nodeIndex => {\n const idx = Math.abs(nodeIndex);\n if (nodeIndex < 0) {\n if (nodeOrders[idx] === undefined)\n nodeOrders[idx] = backwardOrder;\n const order = nodeOrders[idx];\n if (order < minOrder)\n minOrder = order;\n forwardOrder = order;\n backwardOrder = order - 1;\n }\n else {\n if (nodeOrders[idx] === undefined)\n nodeOrders[idx] = forwardOrder;\n const order = nodeOrders[idx];\n forwardOrder = order + 1;\n backwardOrder = order;\n }\n });\n if (minOrder < 0) {\n increaseOrderForAllNodes(-minOrder);\n }\n}\n// calculate the order-value of nodes contained in sequence which are to the left of the first node which already has an order-value\nfunction generateNodeOrderTrackBeginning(sequence) {\n let anchorIndex = 0;\n let minOrder = 0;\n while (anchorIndex < sequence.length &&\n nodeOrders[Math.abs(sequence[anchorIndex])] === undefined) {\n anchorIndex += 1; // anchor = first node in common with existing graph\n }\n if (anchorIndex >= sequence.length) {\n return null;\n }\n const anchorSeqVal = sequence[anchorIndex];\n let currentOrder;\n let increment;\n if (anchorSeqVal >= 0) {\n // regular node\n currentOrder = nodeOrders[anchorSeqVal] - 1;\n increment = -1;\n }\n else {\n // reverse node\n currentOrder = nodeOrders[-anchorSeqVal] + 1;\n increment = 1;\n }\n for (let j = anchorIndex - 1; j >= 0; j -= 1) {\n // assign order to nodes which are left of anchor node\n const idx = Math.abs(sequence[j]);\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder += increment;\n }\n }\n if (minOrder < 0) {\n increaseOrderForAllNodes(-minOrder);\n }\n return anchorIndex;\n}\n// Sentinel order for nodes no track or read reaches.\nexport const UNREACHABLE_ORDER = -1;\n// Replace every unassigned entry with UNREACHABLE_ORDER, producing a dense\n// array. Holes matter here: `new Array(n)` is all holes and forEach/map skip\n// them, so an in-place forEach would leave the sentinel unset.\nexport function fillUnassignedOrders(orders) {\n const filled = [];\n for (const order of orders) {\n filled.push(order ?? UNREACHABLE_ORDER);\n }\n return filled;\n}\n// generate global sequence of nodes from left to right, starting with first track and adding other tracks sequentially\nfunction generateNodeOrder() {\n let modifiedSequence;\n let currentOrder;\n let rightIndex;\n let leftIndex;\n let minOrder = 0;\n const tracksAndReads = config.showReads && reads.length > 0 ? tracks.concat(reads) : tracks;\n const reachability = {\n stamp: new Int32Array(nodes.length),\n generation: 0,\n };\n // fill() makes the array dense: `new Array(n)` alone is all holes, which\n // forEach skips, so neither the sentinel pass nor the copy-back below ran.\n nodeOrders = new Array(nodes.length).fill(undefined);\n nodes.forEach(node => {\n if (node) {\n node.order = undefined;\n }\n });\n generateNodeOrderOfSingleTrack(tracks[0].indexSequence);\n for (let i = 1; i < tracksAndReads.length; i += 1) {\n if (DEBUG)\n console.log(`generating order for track ${i + 1}`);\n rightIndex = generateNodeOrderTrackBeginning(tracksAndReads[i].indexSequence);\n if (rightIndex === null) {\n if (tracksAndReads[i].type === 'haplotype') {\n generateNodeOrderOfSingleTrack(tracksAndReads[i].indexSequence);\n }\n else {\n tracksAndReads.splice(i, 1);\n reads.splice(i - tracks.length, 1);\n i -= 1;\n }\n continue;\n }\n modifiedSequence = uninvert(tracksAndReads[i].indexSequence);\n while (rightIndex < modifiedSequence.length) {\n // move right until the end of the sequence\n leftIndex = rightIndex;\n rightIndex += 1;\n while (rightIndex < modifiedSequence.length &&\n nodeOrders[modifiedSequence[rightIndex]] === undefined) {\n rightIndex += 1;\n }\n if (rightIndex < modifiedSequence.length) {\n // middle segment between two anchors\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] + 1;\n for (let j = leftIndex + 1; j < rightIndex; j += 1) {\n nodeOrders[modifiedSequence[j]] = currentOrder;\n currentOrder += 1;\n }\n if (nodeOrders[modifiedSequence[rightIndex]] >\n nodeOrders[modifiedSequence[leftIndex]]) {\n if (nodeOrders[modifiedSequence[rightIndex]] < currentOrder) {\n increaseOrderForSuccessors(modifiedSequence[rightIndex], modifiedSequence[rightIndex - 1], currentOrder);\n }\n }\n else {\n if (tracksAndReads[i].indexSequence[rightIndex] >= 0 &&\n !isSuccessor(modifiedSequence[rightIndex], modifiedSequence[leftIndex], reachability)) {\n // no real reversal\n increaseOrderForSuccessors(modifiedSequence[rightIndex], modifiedSequence[rightIndex - 1], currentOrder);\n }\n else {\n // real reversal\n if (tracksAndReads[i].indexSequence[leftIndex] < 0 ||\n (nodes[modifiedSequence[leftIndex + 1]].degree < 2 &&\n nodeOrders[modifiedSequence[rightIndex]] <\n nodeOrders[modifiedSequence[leftIndex]])) {\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] - 1;\n for (let j = leftIndex + 1; j < rightIndex; j += 1) {\n nodeOrders[modifiedSequence[j]] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder -= 1;\n }\n }\n }\n }\n }\n else {\n // right segment to the right of last anchor\n if (tracksAndReads[i].indexSequence[leftIndex] >= 0) {\n // elongate towards the right\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] + 1;\n for (let j = leftIndex + 1; j < modifiedSequence.length; j += 1) {\n const idx = modifiedSequence[j];\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n currentOrder += 1;\n }\n }\n }\n else {\n // elongate towards the left\n currentOrder = nodeOrders[modifiedSequence[leftIndex]] - 1;\n for (let j = leftIndex + 1; j < modifiedSequence.length; j += 1) {\n const idx = modifiedSequence[j];\n if (nodeOrders[idx] === undefined) {\n nodeOrders[idx] = currentOrder;\n minOrder = Math.min(minOrder, currentOrder);\n currentOrder -= 1;\n }\n }\n }\n }\n }\n }\n if (minOrder < 0)\n increaseOrderForAllNodes(-minOrder);\n // Nodes unreachable from any track get UNREACHABLE_ORDER so every node ends\n // up with a defined order; downstream code uses `order >= 0` to skip them.\n const finalOrders = fillUnassignedOrders(nodeOrders);\n nodeOrders = finalOrders;\n finalOrders.forEach((order, i) => {\n const node = nodes[i];\n if (node !== undefined) {\n node.order = order;\n }\n });\n}\nfunction isSuccessor(first, second, scratch) {\n scratch.generation += 1;\n const { stamp, generation } = scratch;\n const stack = [first];\n stamp[first] = generation;\n while (stack.length > 0) {\n const current = stack.pop();\n if (current === second)\n return true;\n for (const childIndex of nodes[current].successors) {\n if (stamp[childIndex] !== generation) {\n stamp[childIndex] = generation;\n stack.push(childIndex);\n }\n }\n }\n return false;\n}\n// get order number of the rightmost node\nfunction getMaxOrder() {\n let max = -1;\n nodeOrders.forEach(order => {\n if (order !== undefined && order > max)\n max = order;\n });\n return max;\n}\n// generates sequence keeping the order but switching all reversed (negative) nodes to forward nodes\nfunction uninvert(sequence) {\n return sequence.map(v => Math.abs(v));\n}\n// increases the order-value of all nodes by amount\nfunction increaseOrderForAllNodes(amount) {\n nodeOrders.forEach((order, i) => {\n if (order !== undefined)\n nodeOrders[i] = order + amount;\n });\n}\n// increases the order-value for currentNode and (if necessary) successor nodes recursively\nfunction increaseOrderForSuccessors(startingNode, tabuNode, newOrder) {\n const increasedOrders = new Map();\n // Walked with a head pointer rather than shift(), which is O(length) per pop.\n const queue = [[startingNode, newOrder]];\n let head = 0;\n while (head < queue.length) {\n const [currentNode, currentOrder] = queue[head];\n head += 1;\n const currentNodeOrder = nodeOrders[currentNode];\n if (currentNodeOrder !== undefined && currentNodeOrder < currentOrder) {\n if (!increasedOrders.has(currentNode) ||\n increasedOrders.get(currentNode) < currentOrder) {\n increasedOrders.set(currentNode, currentOrder);\n nodes[currentNode].successors.forEach(successor => {\n if (nodeOrders[successor] > currentNodeOrder &&\n successor !== tabuNode) {\n // only increase order of successors to the right of currentNode\n queue.push([successor, currentOrder + 1]);\n }\n });\n if (currentNode !== startingNode) {\n nodes[currentNode].predecessors.forEach(predecessor => {\n if (nodeOrders[predecessor] > currentNodeOrder &&\n predecessor !== tabuNode) {\n // only increase order of predecessors to the right of currentNode\n queue.push([predecessor, currentOrder + 1]);\n }\n });\n }\n }\n }\n }\n increasedOrders.forEach((value, key) => {\n nodeOrders[key] = value;\n });\n}\n// calculates the node degree: the number of tracks passing through the node / the node height\nfunction generateNodeDegree() {\n nodes.forEach(node => {\n node.tracks = [];\n });\n tracks.forEach(track => {\n track.indexSequence.forEach(nodeIndex => {\n nodes[Math.abs(nodeIndex)].tracks.push(track.id);\n });\n });\n nodes.forEach(node => {\n if (node.tracks !== undefined)\n node.degree = node.tracks.length;\n });\n}\n// Optimize the orientations for nodes in the global `nodes` for displaying the\n// paths in the global `tracks` and the read paths, if applicable, in the\n// global `reads`\nfunction switchNodeOrientation() {\n const pivotPath = tracks[0];\n let countPaths = tracks.slice(1, tracks.length);\n if (config.showReads && reads.length > 0) {\n countPaths = countPaths.concat(reads);\n }\n switchNodeOrientationForPaths(countPaths, pivotPath);\n if (config.showReads && reads.length > 0) {\n // Any changes should be committed back\n for (let i = 0; i < reads.length; i++) {\n if (reads[i] !== countPaths[i + tracks.length - 1]) {\n throw new Error('Read inequality');\n }\n }\n }\n}\n// If more of the given paths pass through a specific node in reverse direction than in\n// regular direction, switch its orientation. Processes all paths' nodes in\n// place, so if you want e.g. a first track with nodes fixed in that\n// orientation, pass it as pivotPath.\n// References and modifies the global nodes variable.\nfunction switchNodeOrientationForPaths(paths, pivotPath) {\n const toSwitch = new Map();\n const pivotNames = pivotPath ? new Set(pivotPath.sequence) : null;\n for (const path of paths) {\n const sequence = path.sequence;\n for (let j = 0; j < sequence.length; j += 1) {\n const nodeName = forward(sequence[j]);\n const currentNode = nodeByName(nodeName);\n if (pivotNames && !pivotNames.has(nodeName)) {\n // do not change orientation for nodes which are part of the pivot path\n const prevOrder = j > 0 ? nodeByName(sequence[j - 1]).order : undefined;\n const nextOrder = j < sequence.length - 1\n ? nodeByName(sequence[j + 1]).order\n : undefined;\n if ((prevOrder === undefined || prevOrder < currentNode.order) &&\n (nextOrder === undefined || currentNode.order < nextOrder)) {\n // Node is visited in increasing order along the path. Reverse\n // visits count towards switching, forward visits against it.\n addToSwitchScore(toSwitch, nodeName, isReverse(sequence[j]) ? 1 : -1);\n }\n if ((prevOrder === undefined || prevOrder > currentNode.order) &&\n (nextOrder === undefined || currentNode.order > nextOrder)) {\n // Node is visited in *decreasing* order along the path, so is already\n // backward: the votes are the other way round.\n addToSwitchScore(toSwitch, nodeName, isReverse(sequence[j]) ? -1 : 1);\n }\n }\n }\n }\n for (const path of paths) {\n path.sequence.forEach((node, nodeIndex) => {\n const score = toSwitch.get(forward(node));\n if (score !== undefined && score > 0) {\n // This node is backward more so flip it around\n path.sequence[nodeIndex] = flip(node);\n path.indexSequence[nodeIndex] = -path.indexSequence[nodeIndex];\n }\n });\n }\n // invert the sequence within the nodes and mark them as \"switched\"\n toSwitch.forEach((value, key) => {\n if (value > 0) {\n const node = nodeByName(key);\n node.seq = getReverseComplement(node.seq);\n node.switched = true;\n }\n });\n}\nfunction addToSwitchScore(scores, nodeName, delta) {\n scores.set(nodeName, (scores.get(nodeName) ?? 0) + delta);\n}\n// calculates the concrete values for the nodes' x-coordinates\nfunction generateNodeXCoords() {\n let currentX = 0;\n let nextX = 20;\n let currentOrder = -1;\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n const extra = calculateExtraSpace();\n sortedNodes.forEach(node => {\n if (node.order >= 0) {\n if (node.order > currentOrder) {\n currentOrder = node.order;\n currentX = nextX + 10 * extra[node.order];\n }\n node.x = currentX;\n nextX = Math.max(nextX, currentX + 40 + node.pixelWidth);\n }\n });\n}\n// calculates additional horizontal space needed between two nodes\n// two neighboring nodes have to be moved further apart if there is a lot going on in between them\n// -> edges turning to vertical orientation should not overlap\nfunction calculateExtraSpace() {\n const leftSideEdges = [];\n const rightSideEdges = [];\n const fallAngleAdjustment = [];\n const extra = [];\n for (let i = 0; i <= maxOrder; i += 1) {\n leftSideEdges.push(0);\n rightSideEdges.push(0);\n fallAngleAdjustment.push(0);\n }\n tracks.forEach(track => {\n for (let i = 1; i < track.path.length; i += 1) {\n const seg = track.path[i];\n const prevSeg = track.path[i - 1];\n // Track is going to the same node, account for space taken up by edges looping around\n if (seg.order === prevSeg.order) {\n // repeat or translocation\n if (seg.isForward) {\n leftSideEdges[seg.order] = leftSideEdges[seg.order] + 1;\n }\n else {\n rightSideEdges[seg.order] = rightSideEdges[seg.order] + 1;\n }\n }\n else {\n // Track is going to a different node; account for space needed to limit rise/fall angle\n const yDifference = Math.abs((seg.y ?? 0) - (prevSeg.y ?? 0));\n //TODO: Extra space should also be accounted when there are too many tracks curving at the nodes\n fallAngleAdjustment[seg.order] = Math.max(yDifference / 17.5, fallAngleAdjustment[seg.order]);\n }\n }\n });\n extra.push(Math.max(0, leftSideEdges[0] - 1));\n for (let i = 1; i <= maxOrder; i += 1) {\n // Extra space uses space needed for edges(tracks looping), or space needed to limit rise/fall angle, whichever is larger\n extra.push(Math.max(Math.max(0, leftSideEdges[i] - 1) +\n Math.max(0, rightSideEdges[i - 1] - 1), fallAngleAdjustment[i]));\n }\n return extra;\n}\n// create and fill assignment-variable, which contains info about tracks and lanes for each order-value\nfunction generateLaneAssignment() {\n let segmentNumber;\n let currentNodeIndex;\n let currentNodeIsForward;\n let currentNode;\n let previousNode;\n let previousNodeIsForward;\n // For each horizontal order slot, for each track number, holds the\n // SegmentAssignment object for the visit of that track to that order slot.\n // When an order slot is visited multiple times, holds whatever\n // SegmentAssignment was created most recently.\n const prevSegmentPerOrderPerTrack = [];\n // Index into assignments[order] by node, so addToAssignment can find an\n // existing entry without scanning the whole order slot.\n const assignmentByOrderAndNode = [];\n // create empty variables\n for (let i = 0; i <= maxOrder; i += 1) {\n assignments[i] = [];\n assignmentByOrderAndNode[i] = new Map();\n prevSegmentPerOrderPerTrack[i] = [];\n for (let j = 0; j < tracks.length; j += 1) {\n prevSegmentPerOrderPerTrack[i][j] = null;\n }\n }\n tracks.forEach((track, trackNo) => {\n // Trace along each track and create Segment objects in the track's path\n // field, and SegmentAssignment objects in NodeAssignment objects in all\n // the order slots that are visited by the track. Set up all the\n // cross-reverencing indexes and work out when we need segments to let\n // tracks pass nodes and turn around, but leave all the assigned lane\n // values empty for now.\n // add info for start of track\n currentNodeIndex = Math.abs(track.indexSequence[0]);\n currentNodeIsForward = isForwardIndex(track.indexSequence[0]);\n currentNode = nodes[currentNodeIndex];\n track.path = [];\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, 0, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber = 1;\n for (let i = 1; i < track.sequence.length; i += 1) {\n previousNode = currentNode;\n previousNodeIsForward = currentNodeIsForward;\n currentNodeIndex = Math.abs(track.indexSequence[i]);\n currentNodeIsForward = isForwardIndex(track.indexSequence[i]);\n currentNode = nodes[currentNodeIndex];\n if (currentNode.order > previousNode.order) {\n if (!previousNodeIsForward) {\n // backward to forward at previous node\n track.path.push({\n order: previousNode.order,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(previousNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n for (let j = previousNode.order + 1; j < currentNode.order; j += 1) {\n // forward without nodes\n track.path.push({\n order: j,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(j, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n if (!currentNodeIsForward) {\n // forward to backward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n // current Node forward\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n else if (currentNode.order < previousNode.order) {\n if (previousNodeIsForward) {\n // turnaround from fw to bw at previous node\n track.path.push({\n order: previousNode.order,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(previousNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n for (let j = previousNode.order - 1; j > currentNode.order; j -= 1) {\n // bachward without nodes\n track.path.push({\n order: j,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(j, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n if (currentNodeIsForward) {\n // backward to forward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: true,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n // backward at current node\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: false,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n else {\n if (currentNodeIsForward !== previousNodeIsForward) {\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n else {\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: !currentNodeIsForward,\n node: null,\n });\n addToAssignment(currentNode.order, null, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n track.path.push({\n order: currentNode.order,\n lane: null,\n isForward: currentNodeIsForward,\n node: currentNodeIndex,\n });\n addToAssignment(currentNode.order, currentNodeIndex, trackNo, segmentNumber, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode);\n segmentNumber += 1;\n }\n }\n }\n });\n // Now sweep left to right across order slots and assign vertical lanes to all the segments.\n for (let i = 0; i <= maxOrder; i += 1) {\n generateSingleLaneAssignment(assignments[i], i); // this is where the lanes get assigned\n }\n}\nfunction addToAssignment(order, nodeIndex, trackNo, segmentID, prevSegmentPerOrderPerTrack, assignmentByOrderAndNode) {\n const segment = {\n trackID: trackNo,\n segmentID,\n compareToFromSame: prevSegmentPerOrderPerTrack[order][trackNo] ?? null,\n };\n // A null node means the track is passing through / turning around here rather\n // than visiting a node, so such segments never share a NodeAssignment.\n const existing = nodeIndex === null\n ? undefined\n : assignmentByOrderAndNode[order].get(nodeIndex);\n if (existing === undefined) {\n const assignment = {\n type: 'single',\n node: nodeIndex,\n tracks: [segment],\n };\n assignments[order].push(assignment);\n if (nodeIndex !== null) {\n assignmentByOrderAndNode[order].set(nodeIndex, assignment);\n }\n }\n else {\n // add to existing node in assignment\n existing.type = 'multiple';\n existing.tracks.push(segment);\n }\n prevSegmentPerOrderPerTrack[order][trackNo] = segment;\n}\n// looks at assignment and sets idealY and idealLane by looking at where the tracks come from\nfunction getIdealLanesAndCoords(assignment, order) {\n let index;\n assignment.forEach(node => {\n node.idealLane = 0;\n node.tracks.forEach(track => {\n if (track.segmentID === 0) {\n track.idealLane = track.trackID;\n track.idealY = null;\n }\n else {\n if (tracks[track.trackID].path[track.segmentID - 1].order ===\n order - 1) {\n track.idealLane =\n tracks[track.trackID].path[track.segmentID - 1].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[track.segmentID - 1].y;\n }\n else if (track.segmentID < tracks[track.trackID].path.length - 1 &&\n tracks[track.trackID].path[track.segmentID + 1].order === order - 1) {\n track.idealLane =\n tracks[track.trackID].path[track.segmentID + 1].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[track.segmentID + 1].y;\n }\n else {\n index = track.segmentID - 1;\n while (index >= 0 &&\n tracks[track.trackID].path[index].order !== order - 1) {\n index -= 1;\n }\n if (index < 0) {\n track.idealLane = track.trackID;\n track.idealY = null;\n }\n else {\n track.idealLane =\n tracks[track.trackID].path[index].lane ?? undefined;\n track.idealY = tracks[track.trackID].path[index].y;\n }\n }\n }\n node.idealLane += track.idealLane;\n });\n node.idealLane /= node.tracks.length;\n });\n}\n// assigns the optimal lanes for a single horizontal position (=order)\n// first an ideal lane is calculated for each track (which is ~ the lane of its predecessor)\n// then the nodes are sorted by their average ideal lane\n// and the whole construct is then moved up or down if necessary\nfunction generateSingleLaneAssignment(assignment, order) {\n let currentLane = 0;\n const potentialAdjustmentValues = new Set();\n let currentY = 20;\n let prevNameIsNull = false;\n let prevTrack = -1;\n getIdealLanesAndCoords(assignment, order);\n assignment.sort(compareByIdealLane);\n assignment.forEach(node => {\n if (node.node !== null) {\n nodes[node.node].topLane = currentLane;\n if (prevNameIsNull)\n currentY -= 10;\n nodes[node.node].y = currentY;\n nodes[node.node].contentHeight = 0;\n prevNameIsNull = false;\n }\n else {\n if (prevNameIsNull)\n currentY -= 25;\n else if (currentY > 20)\n currentY -= 10;\n prevNameIsNull = true;\n }\n node.tracks.sort(compareByIdealLane);\n node.tracks.forEach(track => {\n track.lane = currentLane;\n if (track.trackID === prevTrack && node.node === null && prevNameIsNull) {\n currentY += 10;\n }\n tracks[track.trackID].path[track.segmentID].lane = currentLane;\n tracks[track.trackID].path[track.segmentID].y = currentY;\n if (track.idealY != null) {\n potentialAdjustmentValues.add(track.idealY - currentY);\n }\n currentLane += 1;\n currentY += tracks[track.trackID].width;\n if (node.node !== null) {\n nodes[node.node].contentHeight += tracks[track.trackID].width;\n }\n prevTrack = track.trackID;\n });\n currentY += 25;\n });\n adjustVertically(assignment, potentialAdjustmentValues);\n}\n// moves all tracks at a single horizontal location (=order) up/down to minimize lane changes\nfunction adjustVertically(assignment, potentialAdjustmentValues) {\n let verticalAdjustment = 0;\n let minAdjustmentCost = Number.MAX_SAFE_INTEGER;\n potentialAdjustmentValues.forEach(moveBy => {\n if (getVerticalAdjustmentCost(assignment, moveBy) < minAdjustmentCost) {\n minAdjustmentCost = getVerticalAdjustmentCost(assignment, moveBy);\n verticalAdjustment = moveBy;\n }\n });\n assignment.forEach(node => {\n if (node.node !== null) {\n nodes[node.node].y += verticalAdjustment;\n }\n node.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n seg.y = seg.y + verticalAdjustment;\n });\n });\n}\n// Budge down all nodes and out-of-node tracks below this node by this amount\nfunction adjustVertically3(node, adjustBy) {\n if (node.order < 0 || assignments[node.order] === undefined)\n return;\n assignments[node.order].forEach(assignmentNode => {\n if (assignmentNode.node !== null) {\n const aNode = nodes[assignmentNode.node];\n if (aNode !== node && aNode.y > node.y) {\n aNode.y += adjustBy;\n assignmentNode.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n seg.y = seg.y + adjustBy;\n });\n }\n }\n else {\n // track-segment not within a node\n assignmentNode.tracks.forEach(track => {\n const seg = tracks[track.trackID].path[track.segmentID];\n if (seg.y >= node.y) {\n seg.y = seg.y + adjustBy;\n }\n });\n }\n });\n if (nodesPerOrder[node.order].length > 0) {\n nodesPerOrder[node.order].forEach(nodeIndex => {\n if (nodes[nodeIndex] !== node && nodes[nodeIndex].y > node.y) {\n nodes[nodeIndex].y += adjustBy;\n }\n });\n }\n}\n// calculates cost of vertical adjustment as vertical distance * width of track\nfunction getVerticalAdjustmentCost(assignment, moveBy) {\n let result = 0;\n assignment.forEach(node => {\n node.tracks.forEach(track => {\n if (track.idealY != null && tracks[track.trackID].type !== 'read') {\n result +=\n Math.abs(track.idealY -\n moveBy -\n tracks[track.trackID].path[track.segmentID].y) * tracks[track.trackID].width;\n }\n });\n });\n return result;\n}\nfunction compareByIdealLane(a, b) {\n if (a.idealLane !== undefined) {\n if (b.idealLane !== undefined) {\n if (a.idealLane < b.idealLane)\n return -1;\n else if (a.idealLane > b.idealLane)\n return 1;\n return 0;\n }\n return -1;\n }\n if (b.idealLane !== undefined) {\n return 1;\n }\n return 0;\n}\nfunction compareNodesByOrder(a, b) {\n if (!a) {\n if (!b)\n return 0;\n return -1;\n }\n if (!b)\n return 1;\n if (a.order !== undefined) {\n if (b.order !== undefined) {\n if (a.order < b.order)\n return -1;\n else if (a.order > b.order)\n return 1;\n if (a.y !== undefined && b.y !== undefined) {\n if (a.y < b.y)\n return -1;\n else if (a.y > b.y)\n return 1;\n }\n return 0;\n }\n return -1;\n }\n if (b.order !== undefined)\n return 1;\n return 0;\n}\nfunction addTrackFeatures() {\n let nodeStart;\n let nodeEnd;\n let feature = {};\n bed.forEach(line => {\n let i = 0;\n while (i < tracks.length && tracks[i].name !== line.track)\n i += 1;\n if (i < tracks.length) {\n nodeStart = 0;\n tracks[i].path.forEach(node => {\n if (node.node !== null) {\n feature = {};\n nodeEnd = nodeStart + nodes[node.node].sequenceLength - 1;\n if (nodeStart >= line.start && nodeStart <= line.end) {\n feature.start = 0;\n }\n if (nodeStart < line.start && nodeEnd >= line.start) {\n feature.start = line.start - nodeStart;\n }\n if (nodeEnd <= line.end && nodeEnd >= line.start) {\n feature.end = nodeEnd - nodeStart;\n if (nodeEnd < line.end)\n feature.continue = true;\n }\n if (nodeEnd > line.end && nodeStart <= line.end) {\n feature.end = line.end - nodeStart;\n }\n if (feature.start !== undefined) {\n feature.type = line.type;\n feature.name = line.name;\n if (node.features === undefined)\n node.features = [];\n node.features.push(feature);\n }\n nodeStart = nodeEnd + 1;\n }\n });\n }\n });\n}\nfunction calculateTrackWidth() {\n // flag: if vg returns freq of 0 for all tracks, we will increase width manually\n let allAreFour = true;\n const NARROW_WIDTH = 4;\n const WIDE_WIDTH = config.trackWidth;\n tracks.forEach(track => {\n if (track.freq !== undefined) {\n // custom track width\n track.width = Math.round((Math.log(track.freq) + 1) * NARROW_WIDTH);\n }\n else {\n // default track width\n track.width = WIDE_WIDTH;\n if (track.type !== undefined && track.type === 'read') {\n track.width = NARROW_WIDTH;\n }\n }\n if (track.width !== NARROW_WIDTH) {\n allAreFour = false;\n }\n });\n if (allAreFour) {\n tracks.forEach(track => {\n if (track.freq !== undefined) {\n track.width = WIDE_WIDTH;\n }\n });\n }\n}\nfunction getReadXStart(read) {\n const seg = read.path[0];\n const node = nodes[seg.node];\n const offset = read.firstNodeOffset ?? 0;\n // read starts in forward direction, or backward from the node's far end\n return clampedXCoordinateOfBaseWithinNode(node, seg.isForward ? offset : node.sequenceLength - offset);\n}\nfunction getReadXEnd(read) {\n const seg = read.path[read.path.length - 1];\n const node = nodes[seg.node];\n const cover = read.finalNodeCoverLength ?? 0;\n // read ends in forward direction, or backward from the node's far end\n return clampedXCoordinateOfBaseWithinNode(node, seg.isForward ? cover : node.sequenceLength - cover);\n}\n// returns the x coordinate (in pixels) of (the left side) of the given base\n// position within the given node, or null if the position is past its end\nexport function getXCoordinateOfBaseWithinNode(node, base) {\n if (base > node.sequenceLength)\n return null; // equality is allowed\n return clampedXCoordinateOfBaseWithinNode(node, base);\n}\n// Same, but a base outside the node maps to the nearest node edge. Callers that\n// have to produce a coordinate no matter what use this: falling back to 0 would\n// put the shape at the far left of the whole image instead.\nexport function clampedXCoordinateOfBaseWithinNode(node, base) {\n const [nodeLeftX, nodeRightX] = nodePixelCoordinatesInX(node);\n if (node.sequenceLength === 0) {\n return nodeLeftX;\n }\n const clamped = Math.min(Math.max(base, 0), node.sequenceLength);\n return nodeLeftX + (clamped / node.sequenceLength) * (nodeRightX - nodeLeftX);\n}\n// transforms the info in the tracks' path attribute into actual coordinates\n// and saves them in shapes.rectangles and shapes.curves\nfunction generateSVGShapesFromPath() {\n let xStart;\n let xEnd;\n let yStart;\n let yEnd;\n let trackColor;\n let trackAlpha;\n let highlight;\n let dummy;\n let reversalFlag;\n for (let i = 0; i <= maxOrder; i += 1) {\n extraLeft.push(0);\n extraRight.push(0);\n }\n // generate x coords where each order starts and ends\n const orderStartX = [];\n const orderEndX = [];\n nodes.forEach(node => {\n if (node.x !== undefined) {\n orderStartX[node.order] = node.x;\n if (orderEndX[node.order] === undefined) {\n orderEndX[node.order] = node.x + node.pixelWidth;\n }\n else {\n orderEndX[node.order] = Math.max(orderEndX[node.order], node.x + node.pixelWidth);\n }\n }\n });\n // Helps generation of verticalRectangles, correct increments of extraRight and extraLeft\n tracks.sort(compareTrackByInitialOrdering);\n tracks.forEach(track => {\n highlight = 'plain';\n // Both are pure functions of (track, highlight); alpha doesn't even look at\n // highlight. Recomputing them per path segment was pure overhead.\n trackColor = generateTrackColor(track, highlight);\n trackAlpha = generateTrackAlpha(track);\n let colorHighlight = highlight;\n const colorForCurrentHighlight = () => {\n if (colorHighlight !== highlight) {\n colorHighlight = highlight;\n trackColor = generateTrackColor(track, highlight);\n }\n return trackColor;\n };\n // start of path\n yStart = track.path[0].y;\n if (track.type !== 'read') {\n if (track.sequence[0].startsWith('-')) {\n // The track starts with an inversed node\n xStart = orderEndX[track.path[0].order] + 20;\n }\n else {\n // The track starts with a forward node\n xStart = orderStartX[track.path[0].order] - 20;\n }\n }\n else {\n xStart = getReadXStart(track);\n }\n // middle of path\n for (let i = 0; i < track.path.length; i += 1) {\n if (track.path[i].y === yStart) {\n if (track.path[i].features !== undefined) {\n reversalFlag =\n i > 0 && track.path[i - 1].order === track.path[i].order;\n dummy = createFeatureRectangle(track.path[i], orderStartX[track.path[i].order], orderEndX[track.path[i].order], highlight, track, xStart, yStart, trackColor, reversalFlag);\n highlight = dummy.highlight;\n xStart = dummy.xStart;\n }\n }\n else {\n if (track.path[i - 1].isForward) {\n xEnd = orderEndX[track.path[i - 1].order];\n }\n else {\n xEnd = orderStartX[track.path[i - 1].order];\n }\n if (xEnd !== xStart) {\n trackColor = colorForCurrentHighlight();\n shapes.rectangles.push({\n xStart: Math.min(xStart, xEnd),\n yStart,\n xEnd: Math.max(xStart, xEnd),\n yEnd: yStart + track.width - 1,\n color: trackColor,\n alpha: trackAlpha,\n // TODO: This is not actually the index of the track!\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (track.path[i].order - 1 === track.path[i - 1].order) {\n // regular forward connection\n xStart = xEnd;\n xEnd = orderStartX[track.path[i].order];\n yEnd = track.path[i].y;\n trackColor = colorForCurrentHighlight();\n shapes.curves.push({\n xStart,\n yStart,\n xEnd: xEnd + 1,\n yEnd,\n width: track.width,\n color: trackColor,\n alpha: trackAlpha,\n laneChange: Math.abs(track.path[i].lane - track.path[i - 1].lane),\n id: track.id,\n name: track.name,\n type: track.type,\n nodeStart: track.path[i - 1]?.node,\n nodeEnd: track.path[i]?.node,\n });\n xStart = xEnd;\n yStart = yEnd;\n }\n else if (track.path[i].order + 1 === track.path[i - 1].order) {\n // regular backward connection\n xStart = xEnd;\n xEnd = orderEndX[track.path[i].order];\n yEnd = track.path[i].y;\n trackColor = colorForCurrentHighlight();\n shapes.curves.push({\n xStart: xStart + 1,\n yStart,\n xEnd,\n yEnd,\n width: track.width,\n color: trackColor,\n alpha: trackAlpha,\n laneChange: Math.abs(track.path[i].lane - track.path[i - 1].lane),\n id: track.id,\n name: track.name,\n type: track.type,\n nodeStart: track.path[i - 1]?.node,\n nodeEnd: track.path[i]?.node,\n });\n xStart = xEnd;\n yStart = yEnd;\n }\n else {\n // change of direction\n if (track.path[i - 1].isForward) {\n yEnd = track.path[i].y;\n generateTurnaround(1, xEnd, yStart, yEnd, track.width, trackColor, track.id, track.path[i].order, track.type, track.name);\n xStart = orderEndX[track.path[i].order];\n yStart = track.path[i].y;\n }\n else {\n yEnd = track.path[i].y;\n generateTurnaround(-1, xEnd, yStart, yEnd, track.width, trackColor, track.id, track.path[i].order, track.type, track.name);\n xStart = orderStartX[track.path[i].order];\n yStart = track.path[i].y;\n }\n }\n if (track.path[i].features !== undefined) {\n reversalFlag = track.path[i - 1].order === track.path[i].order;\n dummy = createFeatureRectangle(track.path[i], orderStartX[track.path[i].order], orderEndX[track.path[i].order], highlight, track, xStart, yStart, trackColor, reversalFlag);\n highlight = dummy.highlight;\n xStart = dummy.xStart;\n }\n }\n }\n // ending edges\n if (track.type !== 'read') {\n if (!track.path[track.path.length - 1].isForward) {\n // The track ends with an inversed node\n xEnd = orderStartX[track.path[track.path.length - 1].order] - 20;\n }\n else {\n // The track ends with a forward node\n xEnd = orderEndX[track.path[track.path.length - 1].order] + 20;\n }\n }\n else {\n xEnd = getReadXEnd(track);\n }\n shapes.rectangles.push({\n xStart: Math.min(xStart, xEnd),\n yStart,\n xEnd: Math.max(xStart, xEnd),\n yEnd: yStart + track.width - 1,\n color: trackColor,\n alpha: trackAlpha,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n });\n}\n// Sankey-mode synthesis: one synthetic \"read\" per (srcSigned \u2192 dstSigned) edge,\n// fed through the normal placeReads/generateSVGShapesFromPath pipeline so the\n// band gets the same lane assignment, loop topology, bezier shape, AND color\n// scheme as a real read \u2014 colors come from generateTrackColor's natural\n// `id % palette.length` rotation, no custom palette needed.\n//\n// Synthetic-track ids live above this base so they don't collide with real\n// track ids and so click/hover handlers can show count info instead of the\n// per-read info dialog.\nconst COARSENED_ID_BASE = 1_000_000_000;\nexport const isCoarsenedId = (id) => id >= COARSENED_ID_BASE;\n// Build one synthetic read per (srcSigned \u2192 dstSigned) edge in `source`\n// (real reads, or the alt haplotype tracks). The synthetic reads replace the\n// real ones BEFORE placeReads runs, so:\n// \u2022 placeReads sees N_edges reads instead of N_reads (huge node-height win),\n// \u2022 each band gets a lane and an \"right-down-left-up-right\" loop topology\n// for free (placeReads handles reversals and out-of-order jumps),\n// \u2022 generateSVGShapesFromPath emits exactly one TrackCurve per band using\n// the same bezier as a real read \u2014 no special draw path needed.\n//\n// The synthetic track's `sequence` is just the two signed node names. After\n// generateTrackIndexSequences runs, indexSequence is set and the rest of the\n// layout follows.\nfunction buildCoarsenedSyntheticBands(source, unit) {\n coarsenedEdgeMeta = new Map();\n const edges = new Map();\n // When ignoring strand, (+A\u2192+B) and (-B\u2192-A) refer to the same underlying\n // graph connection \u2014 collapse both into one canonical key. We pick the\n // lexicographically smaller of the two orientations so a stable canonical\n // form is chosen; whichever orientation is seen first determines the\n // visual band direction.\n const ignoreStrand = config.ignoreStrand;\n for (const item of source) {\n const seq = item.indexSequence;\n if (!seq || seq.length < 2)\n continue;\n for (let i = 0; i < seq.length - 1; i += 1) {\n const sSigned = seq[i];\n const dSigned = seq[i + 1];\n const sIdx = Math.abs(sSigned);\n const dIdx = Math.abs(dSigned);\n const srcNode = nodes[sIdx];\n const dstNode = nodes[dIdx];\n if (!srcNode || !dstNode)\n continue;\n let key;\n if (ignoreStrand) {\n const a = `${sSigned}>${dSigned}`;\n const b = `${-dSigned}>${-sSigned}`;\n key = a < b ? a : b;\n }\n else {\n key = `${sSigned}>${dSigned}`;\n }\n const existing = edges.get(key);\n if (existing === undefined) {\n edges.set(key, {\n sSigned,\n dSigned,\n sName: sSigned < 0 ? `-${srcNode.name}` : srcNode.name,\n dName: dSigned < 0 ? `-${dstNode.name}` : dstNode.name,\n count: 1,\n sourceTrackID: item.sourceTrackID,\n });\n }\n else {\n existing.count += 1;\n }\n }\n }\n // Square-root scaling on count gives heavy edges visible weight without\n // letting one massive edge dwarf everything else. Min stays near READ_WIDTH\n // so single-read edges still look like a normal read; max is generous so\n // hot edges read as obvious \"highways.\"\n let maxEdgeCount = 0;\n for (const e of edges.values()) {\n if (e.count > maxEdgeCount)\n maxEdgeCount = e.count;\n }\n const BAND_MIN_WIDTH = READ_WIDTH;\n const BAND_MAX_WIDTH = 60;\n const widthForCount = (c) => {\n if (maxEdgeCount <= 1)\n return BAND_MIN_WIDTH;\n return (BAND_MIN_WIDTH +\n (BAND_MAX_WIDTH - BAND_MIN_WIDTH) * Math.sqrt(c / maxEdgeCount));\n };\n const synthetic = [];\n let i = 0;\n for (const edge of edges.values()) {\n const id = COARSENED_ID_BASE + i;\n const label = `${edge.count.toLocaleString()} ${unit}${edge.count === 1 ? '' : 's'}: Node ${edge.sName} \u2192 Node ${edge.dName}`;\n coarsenedEdgeMeta.set(id, { count: edge.count, label });\n // Place each synthetic band so it touches *only* its endpoint nodes' edges:\n // \u2022 firstNodeOffset = src.sequenceLength \u2192 curve exits at src's right\n // pixel edge, no rectangle drawn inside src.\n // \u2022 finalNodeCoverLength = 0 \u2192 curve enters at dst's left pixel edge,\n // no rectangle drawn inside dst.\n // The crucial side effect is in placeReadSet's conflict check: at any\n // node B with an incoming edge (A\u2192B) and an outgoing edge (B\u2192C), the\n // incoming's finalNodeCoverLength=0 and the outgoing's firstNodeOffset=\n // B.length leave them with no horizontal overlap, so they share a single\n // lane. Node heights collapse from ~(N_in + N_out) lanes down to\n // ~max(N_in, N_out) lanes.\n //\n // Note: reverseReversedReads will swap firstNodeOffset/finalNodeCoverLength\n // for fully-reverse synthetics. The math works out: pre-flip values of\n // (srcLen, 0) become post-flip (newSrcLen, 0), which is the same pattern\n // \u2014 bands stay edge-only regardless of orientation.\n const srcLen = nodes[Math.abs(edge.sSigned)]?.sequenceLength ?? 0; // node may be absent for a dangling edge\n synthetic.push({\n id,\n sourceTrackID: edge.sourceTrackID,\n type: 'read',\n // Human-readable so the name surfaces sensibly in any UI that uses it\n // (right-click \u2192 add to group, etc.) \u2014 and so trackTooltipText fallback\n // shows something meaningful.\n name: label,\n sequence: [edge.sName, edge.dName],\n indexSequence: [],\n path: [],\n // Pre-set width so placeReads / placeReadSet allocate the right vertical\n // space and assignReadsToNodes (patched to honor pre-set widths) leaves\n // it alone.\n width: widthForCount(edge.count),\n firstNodeOffset: srcLen,\n finalNodeCoverLength: 0,\n sequenceNew: [\n { nodeName: edge.sName, mismatches: [] },\n { nodeName: edge.dName, mismatches: [] },\n ],\n mapping_quality: 0,\n is_secondary: false,\n sample_name: null,\n read_group: null,\n score: edge.count,\n });\n i += 1;\n }\n // Single-line debug summary so the user can verify node sizing.\n let tallestName = '?';\n let tallestH = 0;\n for (const n of nodes) {\n if (!n)\n continue;\n const h = n.contentHeight;\n if (h > tallestH) {\n tallestH = h;\n tallestName = n.name;\n }\n }\n if (DEBUG) {\n console.log(`[coarsened] ${unit}s_in=${source.length} edges_out=${synthetic.length} ` +\n `tallestNodeBeforePlace=${tallestName}(${tallestH.toFixed(1)})`);\n }\n return synthetic;\n}\nfunction createFeatureRectangle(node, nodeXStart, nodeXEnd, highlight, track, rectXStart, yStart, trackColor, reversalFlag) {\n let currentHighlight = highlight;\n let c;\n let co;\n let featureXStart;\n let featureXEnd;\n nodeXStart -= 8;\n nodeXEnd += 8;\n const nodeWidth = nodes[node.node].sequenceLength;\n node.features.sort((a, b) => a.start - b.start);\n node.features.forEach(feature => {\n if (currentHighlight !== feature.type) {\n // finish incoming rectangle\n c = generateTrackColor(track, currentHighlight);\n if (node.isForward) {\n featureXStart =\n nodeXStart +\n Math.round((feature.start * (nodeXEnd - nodeXStart + 1)) / nodeWidth);\n // overwrite narrow post-inversion rectangle if highlight starts near beginning of node\n if (reversalFlag && featureXStart < nodeXStart + 8) {\n featureXEnd =\n nodeXStart +\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n co = generateTrackColor(track, feature.type);\n shapes.featureRectangles.push({\n xStart: featureXStart,\n yStart,\n xEnd: featureXEnd,\n yEnd: yStart + track.width - 1,\n color: co,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (featureXStart > rectXStart + 1) {\n shapes.featureRectangles.push({\n xStart: rectXStart,\n yStart,\n xEnd: featureXStart - 1,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n }\n else {\n featureXStart =\n nodeXEnd -\n Math.round((feature.start * (nodeXEnd - nodeXStart + 1)) / nodeWidth);\n // overwrite narrow post-inversion rectangle if highlight starts near beginning of node\n if (reversalFlag && featureXStart > nodeXEnd - 8) {\n featureXEnd =\n nodeXEnd -\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n co = generateTrackColor(track, feature.type);\n shapes.featureRectangles.push({\n xStart: featureXEnd,\n yStart,\n xEnd: featureXStart,\n yEnd: yStart + track.width - 1,\n color: co,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n if (rectXStart > featureXStart + 1) {\n shapes.featureRectangles.push({\n xStart: featureXStart + 1,\n yStart,\n xEnd: rectXStart,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n }\n rectXStart = featureXStart;\n currentHighlight = feature.type;\n }\n if (feature.end < nodeWidth - 1 || feature.continue === undefined) {\n // finish internal rectangle\n c = generateTrackColor(track, currentHighlight);\n if (node.isForward) {\n featureXEnd =\n nodeXStart +\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n shapes.featureRectangles.push({\n xStart: rectXStart,\n yStart,\n xEnd: featureXEnd,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n else {\n featureXEnd =\n nodeXEnd -\n Math.round(((feature.end + 1) * (nodeXEnd - nodeXStart + 1)) / nodeWidth) -\n 1;\n shapes.featureRectangles.push({\n xStart: featureXEnd,\n yStart,\n xEnd: rectXStart,\n yEnd: yStart + track.width - 1,\n color: c,\n id: track.id,\n name: track.name,\n type: track.type,\n });\n }\n rectXStart = featureXEnd + 1;\n currentHighlight = 'plain';\n }\n });\n return { xStart: rectXStart, highlight: currentHighlight };\n}\nconst MIN_BEND_WIDTH = 7;\n// Emit the vertical rectangles + rounded corners for a track turning around at\n// one end of an order slot. The two directions are mirror images of each other:\n// a forward-to-reverse turn bulges out to the right of the node and consumes an\n// `extraRight` slot, a reverse-to-forward turn bulges out to the left and\n// consumes an `extraLeft` slot. `dir` is +1 for right, -1 for left.\nfunction generateTurnaround(dir, x, yStart, yEnd, trackWidth, trackColor, trackID, order, type, trackName) {\n const extra = dir === 1 ? extraRight : extraLeft;\n const offset = 10 * extra[order];\n // The turn's apex, and the outward direction from it.\n const apex = x + dir * (offset + 5);\n const radius = MIN_BEND_WIDTH;\n const stem = Math.min(MIN_BEND_WIDTH, trackWidth);\n const yTop = Math.min(yStart, yEnd);\n const yBottom = Math.max(yStart, yEnd);\n // The incoming/outgoing stubs run from the node's edge out to the apex. The\n // one-pixel asymmetry between the two directions is carried over verbatim\n // from the original pair of functions.\n const stubNear = dir === 1 ? x : apex - 1;\n const stubFar = dir === 1 ? apex : x;\n const horizontal = (segTop) => {\n shapes.verticalRectangles.push({\n xStart: stubNear,\n yStart: segTop,\n xEnd: stubFar,\n yEnd: segTop + trackWidth - 1,\n color: trackColor,\n id: trackID,\n name: trackName,\n type,\n });\n };\n // elongate the incoming and outgoing rectangles a bit past the node\n horizontal(yStart);\n shapes.verticalRectangles.push({\n xStart: dir === 1 ? apex + radius : apex - radius - stem,\n yStart: yTop + trackWidth + radius - 1,\n xEnd: dir === 1 ? apex + radius + stem - 1 : apex - radius - 1,\n yEnd: yBottom - radius + 1,\n color: trackColor,\n id: trackID,\n name: trackName,\n type,\n });\n horizontal(yEnd);\n const inner = apex + dir * radius;\n const outer = apex + dir * (radius + stem);\n // bottom 90 degree bend\n let d = `M ${apex} ${yBottom}`;\n d += ` Q ${inner} ${yBottom} ${inner} ${yBottom - radius}`;\n d += ` H ${outer}`;\n d += ` Q ${outer} ${yBottom + trackWidth} ${apex} ${yBottom + trackWidth}`;\n d += ' Z ';\n shapes.corners.push({ path: d, color: trackColor, id: trackID, type });\n // top 90 degree bend\n d = `M ${apex} ${yTop}`;\n d += ` Q ${outer} ${yTop} ${outer} ${yTop + trackWidth + radius}`;\n d += ` H ${inner}`;\n d += ` Q ${inner} ${yTop + trackWidth} ${apex} ${yTop + trackWidth}`;\n d += ' Z ';\n shapes.corners.push({ path: d, color: trackColor, id: trackID, type });\n extra[order] += 1;\n}\nexport function nodePixelCoordinatesInX(node) {\n // Add and subtract 4 to account for stroke width - TODO: figure out what the 4 means\n const nodeLeftX = node.x - 4;\n const nodeRightX = node.x + node.pixelWidth + 4;\n return [nodeLeftX, nodeRightX];\n}\n// calculate node widths depending on sequence lengths and chosen calculation method\nfunction generateNodeWidth() {\n // Promote the input's optional sequenceLength; Node declares it required, so\n // widen to InputNode to make the still-possibly-undefined read explicit.\n nodes.forEach((node) => {\n if (node.sequenceLength === undefined) {\n node.sequenceLength = node.seq.length;\n }\n });\n switch (config.nodeWidthOption) {\n case 'compressed':\n nodes.forEach(node => {\n node.width = 1 + Math.log(node.sequenceLength) / Math.log(2);\n node.pixelWidth = Math.round((node.width - 1) * 8.401);\n });\n break;\n case 'small':\n nodes.forEach(node => {\n node.width = node.sequenceLength / 100;\n node.pixelWidth = Math.round((node.width - 1) * 8.401);\n });\n break;\n case 'fixed':\n // when there's no reads in the node, it should be a little wider\n nodes.forEach(node => {\n node.width = 10;\n node.pixelWidth = Math.round(node.width * 8.401);\n });\n break;\n case 'normal': {\n // The sequence is drawn in a monospace font, so one character's width,\n // which the caller measures, sizes every node.\n const charWidth = config.charWidth;\n nodes.forEach(node => {\n node.width = node.sequenceLength;\n const len = node.seq?.length ?? 1;\n node.pixelWidth = Math.round(charWidth * len);\n });\n break;\n }\n default:\n throw new Error(`${config.nodeWidthOption} not implemented`);\n }\n}\n// remove redundant nodes\n// two nodes A and B can be merged if all tracks leaving A go directly into B\n// and all tracks entering B come directly from A\n// (plus no inversions involved)\nfunction mergeNodes() {\n let nodeName;\n let nodeName2;\n // One set per node index, including the hole at 0 (Array.from materializes\n // holes, unlike forEach/map).\n const predSets = Array.from(nodes, () => new Set());\n const succSets = Array.from(nodes, () => new Set());\n let tracksAndReads;\n if (config.showReads && reads.length > 0)\n tracksAndReads = tracks.concat(reads);\n else\n tracksAndReads = tracks;\n tracksAndReads.forEach(track => {\n for (let i = 0; i < track.sequence.length; i += 1) {\n if (!isReverse(track.sequence[i])) {\n // forward Node\n if (i > 0) {\n nodeName = track.sequence[i - 1];\n predSets[nodeMap.get(track.sequence[i])].add(nodeName);\n if (isReverse(nodeName)) {\n // add 2 predecessors, to make sure there is no node merging in this case\n predSets[nodeMap.get(track.sequence[i])].add(forward(nodeName));\n }\n }\n else if (track.type === 'haplotype') {\n predSets[nodeMap.get(track.sequence[i])].add('None');\n }\n if (i < track.sequence.length - 1) {\n nodeName = track.sequence[i + 1];\n succSets[nodeMap.get(track.sequence[i])].add(nodeName);\n if (isReverse(nodeName)) {\n // add 2 successors, to make sure there is no node merging in this case\n succSets[nodeMap.get(track.sequence[i])].add(forward(nodeName));\n }\n }\n else if (track.type === 'haplotype') {\n succSets[nodeMap.get(track.sequence[i])].add('None');\n }\n }\n else {\n // reverse Node\n nodeName = forward(track.sequence[i]);\n if (i > 0) {\n nodeName2 = track.sequence[i - 1];\n if (isReverse(nodeName2)) {\n succSets[nodeMap.get(nodeName)].add(forward(nodeName2));\n }\n else {\n // add 2 successors, to make sure there is no node merging in this case\n succSets[nodeMap.get(nodeName)].add(nodeName2);\n succSets[nodeMap.get(nodeName)].add(reverse(nodeName2));\n }\n }\n else if (track.type === 'haplotype') {\n succSets[nodeMap.get(nodeName)].add('None');\n }\n if (i < track.sequence.length - 1) {\n nodeName2 = track.sequence[i + 1];\n if (isReverse(nodeName2)) {\n predSets[nodeMap.get(nodeName)].add(forward(nodeName2));\n }\n else {\n predSets[nodeMap.get(nodeName)].add(nodeName2);\n predSets[nodeMap.get(nodeName)].add(reverse(nodeName2));\n }\n }\n else if (track.type === 'haplotype') {\n predSets[nodeMap.get(nodeName)].add('None');\n }\n }\n }\n });\n // convert sets to arrays\n const pred = predSets.map(s => Array.from(s));\n const succ = succSets.map(s => Array.from(s));\n // update reads which pass through merging nodes\n if (config.showReads && reads.length > 0) {\n // sort nodes by order, then by y-coordinate\n const sortedNodes = nodes.slice();\n sortedNodes.sort(compareNodesByOrder);\n // iterate over all nodes and calculate their position within the new merged node\n // Both maps carry an entry for each orientation of every node name (\"7\"\n // and \"-7\"), because the read update below looks up whichever orientation\n // the read happens to visit.\n const mergeOffset = new Map();\n // maps to leftmost node of a node's \"merging cascade\"\n const mergeOrigin = new Map();\n const offsetOf = (nodeName) => {\n const offset = mergeOffset.get(nodeName);\n if (offset === undefined) {\n throw new Error(`No merge offset recorded for node ${nodeName}`);\n }\n return offset;\n };\n const originOf = (nodeName) => {\n const origin = mergeOrigin.get(nodeName);\n if (origin === undefined) {\n throw new Error(`No merge origin recorded for node ${nodeName}`);\n }\n return origin;\n };\n sortedNodes.forEach(node => {\n const predecessor = mergeableWithPred(nodeMap.get(node.name), pred, succ);\n if (predecessor) {\n // Nodes are visited in order, so the predecessor of a merge cascade is\n // always recorded before the node that follows it.\n const offset = offsetOf(predecessor) + nodeByName(predecessor).sequenceLength;\n mergeOffset.set(node.name, offset);\n mergeOffset.set(reverse(node.name), offset);\n mergeOrigin.set(node.name, originOf(predecessor));\n mergeOrigin.set(reverse(node.name), originOf(predecessor));\n }\n else {\n mergeOffset.set(node.name, 0);\n mergeOffset.set(reverse(node.name), 0);\n mergeOrigin.set(node.name, node.name);\n mergeOrigin.set(reverse(node.name), node.name);\n }\n });\n reads.forEach(read => {\n const sequenceNew = read.sequenceNew ?? [];\n read.firstNodeOffset =\n (read.firstNodeOffset ?? 0) + offsetOf(read.sequence[0]);\n read.finalNodeCoverLength =\n (read.finalNodeCoverLength ?? 0) +\n offsetOf(read.sequence[read.sequence.length - 1]);\n for (let i = read.sequence.length - 1; i >= 0; i -= 1) {\n const nodeName = forward(read.sequence[i]);\n const predecessor = mergeableWithPred(nodeMap.get(nodeName), pred, succ);\n if (predecessor &&\n mergeableWithSucc(nodeMap.get(predecessor), pred, succ)) {\n if (i > 0) {\n read.sequence.splice(i, 1);\n // adjust position of mismatches\n const predLength = nodeByName(predecessor).sequenceLength;\n sequenceNew[i].mismatches.forEach(mismatch => {\n mismatch.pos += predLength;\n });\n // append mismatches to previous entry's mismatches\n sequenceNew[i - 1].mismatches = sequenceNew[i - 1].mismatches.concat(sequenceNew[i].mismatches);\n sequenceNew.splice(i, 1);\n }\n else {\n read.sequence[0] = originOf(read.sequence[0]);\n const firstEntry = sequenceNew[0];\n const firstOffset = offsetOf(firstEntry.nodeName);\n firstEntry.mismatches.forEach(mismatch => {\n mismatch.pos += firstOffset;\n });\n firstEntry.nodeName = originOf(firstEntry.nodeName);\n }\n }\n }\n });\n }\n // update node sequences + sequence lengths\n for (let i = 0; i < nodes.length; i += 1) {\n if (mergeableWithSucc(i, pred, succ) && !mergeableWithPred(i, pred, succ)) {\n let donor = i;\n while (mergeableWithSucc(donor, pred, succ)) {\n donor = nodeMap.get(forward(succ[donor][0]));\n nodes[i].sequenceLength += nodes[donor].sequenceLength;\n nodes[i].seq += nodes[donor].seq;\n }\n }\n }\n // actually merge the nodes by removing the corresponding nodes from track data\n tracks.forEach(track => {\n for (let i = track.sequence.length - 1; i >= 0; i -= 1) {\n nodeName = forward(track.sequence[i]);\n const nodeIndex = nodeMap.get(nodeName);\n if (mergeableWithPred(nodeIndex, pred, succ)) {\n track.sequence.splice(i, 1);\n }\n }\n });\n // remove the nodes from node-array\n for (let i = nodes.length - 1; i >= 0; i -= 1) {\n if (mergeableWithPred(i, pred, succ)) {\n nodes.splice(i, 1);\n }\n }\n}\nfunction mergeableWithPred(index, pred, succ) {\n if (pred[index].length !== 1)\n return false;\n if (pred[index][0] === 'None')\n return false;\n const predecessor = forward(pred[index][0]);\n const predecessorIndex = nodeMap.get(predecessor);\n if (succ[predecessorIndex].length !== 1)\n return false;\n if (succ[predecessorIndex][0] === 'None')\n return false;\n return predecessor;\n}\nfunction mergeableWithSucc(index, pred, succ) {\n if (succ[index].length !== 1)\n return false;\n if (succ[index][0] === 'None')\n return false;\n const successor = forward(succ[index][0]);\n const successorIndex = nodeMap.get(successor);\n if (pred[successorIndex].length !== 1)\n return false;\n if (pred[successorIndex][0] === 'None')\n return false;\n return true;\n}\nfunction filterReads(readList) {\n const focusNames = config.focusReadNames\n ? new Set(config.focusReadNames)\n : null;\n return readList.filter(read => read.is_secondary !== true &&\n (read.mapping_quality ?? 0) >= config.mappingQualityCutoff &&\n (focusNames === null ||\n (read.name !== undefined && focusNames.has(read.name))));\n}\n", "function groupBy(items, key) {\n const groups = new Map();\n for (const item of items) {\n const k = key(item);\n const group = groups.get(k);\n if (group === undefined) {\n groups.set(k, [item]);\n }\n else {\n group.push(item);\n }\n }\n return groups;\n}\nfunction compareCurvesByXYStartValue(a, b) {\n if (a.xStart < b.xStart)\n return 1;\n else if (a.xStart > b.xStart)\n return -1;\n else if (a.yStart > b.yStart)\n return 1;\n else if (a.yStart < b.yStart)\n return -1;\n return 0;\n}\n// The curves of one track type in draw order, each with its `path` set. Curves\n// between the same pair of nodes form a group whose control points fan out, so\n// a bundle of tracks changing lanes together stays parallel rather than\n// crossing at one x.\nexport function curvePaths(curves, type) {\n const groupedCurves = groupBy(curves.filter(curve => curve.type === type), curve => `${curve.nodeStart},${curve.nodeEnd}`);\n groupedCurves.forEach(curveGroup => {\n // Control point adjustment range: 30% to 70% of the original x values\n let adjustValue = 0.3;\n let adjustIncrement = 0.4 / curveGroup.length;\n // Ignore Beziar Curve skewing when a group is not too big\n if (curveGroup.length <= 5) {\n adjustValue = 0.5;\n adjustIncrement = 0;\n }\n curveGroup.sort(compareCurvesByXYStartValue);\n curveGroup.forEach(curve => {\n let xAdjusted;\n // NextAdjusted is used to try to draw a parallel curve on the way back\n let xNextAdjusted;\n if (curve.yStart < curve.yEnd) {\n xAdjusted =\n curve.xStart + (curve.xEnd - curve.xStart) * (1 - adjustValue);\n adjustValue += adjustIncrement;\n xNextAdjusted =\n curve.xStart + (curve.xEnd - curve.xStart) * (1 - adjustValue);\n }\n else {\n xAdjusted = curve.xStart + (curve.xEnd - curve.xStart) * adjustValue;\n adjustValue += adjustIncrement;\n xNextAdjusted = curve.xStart + (curve.xEnd - curve.xStart) * adjustValue;\n }\n let d = `M ${curve.xStart} ${curve.yStart}`;\n d += ` C ${xAdjusted} ${curve.yStart} ${xAdjusted} ${curve.yEnd} ${curve.xEnd} ${curve.yEnd}`;\n d += ` V ${curve.yEnd + curve.width}`;\n d += ` C ${xNextAdjusted} ${curve.yEnd + curve.width} ${xNextAdjusted} ${curve.yStart + curve.width} ${curve.xStart} ${curve.yStart + curve.width}`;\n d += ' Z';\n curve.path = d;\n });\n });\n // One flat list ordered by group, then by the within-group sort\n const groupKeys = Array.from(groupedCurves.keys());\n groupKeys.sort(function (a, b) {\n const aParts = a.split(',').map(Number);\n const bParts = b.split(',').map(Number);\n const a0 = aParts[0] ?? 0;\n const a1 = aParts[1] ?? 0;\n const b0 = bParts[0] ?? 0;\n const b1 = bParts[1] ?? 0;\n if (a0 < b0) {\n return -1;\n }\n else if (a0 > b0) {\n return 1;\n }\n else {\n if (a1 < b1) {\n return -1;\n }\n else if (a1 > b1) {\n return 1;\n }\n else {\n return 0;\n }\n }\n });\n const flattenedGroups = [];\n for (const key of groupKeys) {\n const group = groupedCurves.get(key);\n if (group !== undefined) {\n flattenedGroups.push(...group);\n }\n }\n return flattenedGroups;\n}\n// A node's outline: a rounded box 9 units outside the node's x extent and its\n// content height, the tracks through it inside.\nexport function nodeOutlinePath(node) {\n // top left arc\n let d = `M ${node.x - 9} ${node.y} Q ${node.x - 9} ${node.y - 9} ${node.x} ${node.y - 9}`;\n let x = node.x;\n let y = node.y - 9;\n // top straight\n if (node.width > 1) {\n x += node.pixelWidth;\n d += ` L ${x} ${y}`;\n }\n // top right arc\n d += ` Q ${x + 9} ${y} ${x + 9} ${y + 9}`;\n x += 9;\n y += 9;\n // right straight\n if (node.contentHeight > 0) {\n y += node.contentHeight - 0;\n d += ` L ${x} ${y}`;\n }\n // bottom right arc\n d += ` Q ${x} ${y + 9} ${x - 9} ${y + 9}`;\n x -= 9;\n y += 9;\n // bottom straight\n if (node.width > 1) {\n x -= node.pixelWidth;\n d += ` L ${x} ${y}`;\n }\n // bottom left arc\n d += ` Q ${x - 9} ${y} ${x - 9} ${y - 9}`;\n x -= 9;\n y -= 9;\n // left straight\n if (node.contentHeight > 0) {\n y -= node.contentHeight - 0;\n d += ` L ${x} ${y}`;\n }\n return d;\n}\n", "import { layoutTubeMap } from '@gmod/tubemap-core'\n\nimport { isBackbone } from '../anchoredNodes'\nimport { pathOrigin } from '../pathAnchoring'\nimport { pathCssColor, pathGreyCssColor } from '../pathColors'\nimport {\n canonicalStrand,\n readColor,\n stepName,\n tubeMapReads,\n} from '../tubeMap/reads'\n\nimport type { Coarsened } from '../tubeMap/coarsen'\nimport type { Graph, LayoutResult, NodeSegment } from '../types'\nimport type {\n InputNode,\n InputTrack,\n LayoutNode,\n TubeMapLayout,\n} from '@gmod/tubemap-core'\n\n// The sequenceTubeMap drawing of a graph's paths: every P or W line a tube,\n// every node a box the tubes pass through, laid out by @gmod/tubemap-core. The\n// canvas draws none of it; TubeMapOverlay draws the shapes, and nodePositions\n// is each box's centreline, for the hit test, the fit and the labels.\n//\n// Two x axes. `own` is the tube map's: columns in node order, a node as wide as\n// log2 of its length, which is the compact picture sequenceTubeMap draws.\n// `reference` pins each column to the reference bp its backbone node covers,\n// so the tubes line up with the linear view's other tracks; the overlay makes\n// room for the curves between columns when it draws (tubeMapWarp.ts).\n\n// A column of the tube map: the nodes at one `order`, the tube x they were\n// drawn over, and under the reference axis the reference bp they stand for.\nexport interface TubeMapColumn {\n order: number\n x0: number\n x1: number\n bp0: number\n bp1: number\n}\n\nexport interface TubeMapDrawing {\n layout: TubeMapLayout\n // each path's tube colour, in the order the graph states its paths\n pathColors: string[]\n // tube y minus this is the drawing's y, so the drawing starts at 0\n yOffset: number\n // set on the reference axis, sorted by order\n columns?: TubeMapColumn[]\n // set when the cut was folded before layout (coarsen.ts): the drawing's node\n // ids are the coarse graph's, not the cut's\n coarse?: Coarsened\n}\n\n// Strand of every visit a path makes to a segment, in walk order, so the k-th\n// time a path reaches a segment reads the k-th entry. Visits name a path\n// without the range suffix `odgi extract` appends (pathOrigin).\nexport function visitStrands(graph: Graph) {\n const strands = new Map<string, ('+' | '-')[]>()\n for (const [segment, visits] of graph.pathVisits ?? []) {\n for (const visit of visits) {\n const key = `${visit.path}\\t${segment}`\n const list = strands.get(key)\n if (list) {\n list.push(visit.strand)\n } else {\n strands.set(key, [visit.strand])\n }\n }\n }\n return strands\n}\n\n// A tube map track per path, the reference first: the layout straightens\n// track 0 and orders every other node around it. A step reads its node in\n// reverse where the path walks the segment against the strand the node is\n// drawn in.\nexport function tubeMapTracks(graph: Graph): InputTrack[] {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const strands = visitStrands(graph)\n const paths = (graph.paths ?? []).map((path, index) => ({ path, index }))\n const reference = paths.findIndex(\n p => pathOrigin(p.path.name).name === graph.referencePath,\n )\n if (reference > 0) {\n paths.unshift(...paths.splice(reference, 1))\n }\n return paths.flatMap(({ path, index }) => {\n const visitName = pathOrigin(path.name).name\n const seen = new Map<string, number>()\n const sequence = path.nodeIds.flatMap(id => {\n const node = nodeById.get(id)\n if (!node) {\n return []\n }\n const k = seen.get(node.name) ?? 0\n seen.set(node.name, k + 1)\n const strand =\n strands.get(`${visitName}\\t${node.name}`)?.[k] ?? canonicalStrand(node)\n return [stepName(node, strand)]\n })\n return sequence.length > 0\n ? [\n {\n id: index,\n name: path.name,\n sequence,\n sourceTrackID: 0,\n type: 'haplotype' as const,\n },\n ]\n : []\n })\n}\n\nfunction tubeMapNodes(graph: Graph): InputNode[] {\n return graph.nodes.map(n => ({\n name: n.id,\n seq: '',\n sequenceLength: Math.max(1, n.length),\n }))\n}\n\nexport function hasTubeMapPaths(graph: Graph) {\n return (graph.paths?.length ?? 0) > 0\n}\n\n// Tubes narrow as paths are added, so ninety haplotypes stack about as tall as\n// forty at full width rather than filling a screen and a half each.\nconst STACK_PX = 600\nconst MIN_TUBE_PX = 3\nconst MAX_TUBE_PX = 15\n\nexport function tubeWidth(trackCount: number) {\n return Math.max(\n MIN_TUBE_PX,\n Math.min(MAX_TUBE_PX, Math.floor(STACK_PX / Math.max(1, trackCount))),\n )\n}\n\nfunction runTubeMap(graph: Graph) {\n const paths = graph.paths ?? []\n const tracks = tubeMapTracks(graph)\n const reads = graph.reads\n ? tubeMapReads(graph, graph.reads, paths.length)\n : []\n const tubeColor = reads.length > 0 ? pathGreyCssColor : pathCssColor\n const pathColors = paths.map((_, i) => tubeColor(i, paths.length))\n const layout = layoutTubeMap(tubeMapNodes(graph), tracks, reads, {\n nodeWidthOption: 'compressed',\n trackWidth: tubeWidth(tracks.length),\n trackColor: track =>\n track.type === 'read' ? readColor(track) : pathColors[track.id]!,\n })\n return layout ? { layout, pathColors } : undefined\n}\n\n// Drawn nodes only: an unreached one has no x.\nfunction drawnNodes(layout: TubeMapLayout) {\n const nodes: LayoutNode[] = []\n layout.nodes.forEach(node => {\n if (node.order >= 0) {\n nodes.push(node)\n }\n })\n return nodes\n}\n\nfunction centreline(node: LayoutNode, yOffset: number, x0: number, x1: number) {\n const y = node.y + node.contentHeight / 2 - yOffset\n return [\n { x: x0, y },\n { x: x1, y },\n ]\n}\n\nfunction extentOf(layout: TubeMapLayout, yOffset: number) {\n return {\n minY: layout.bounds.minY - yOffset,\n maxY: layout.bounds.maxY - yOffset,\n }\n}\n\nexport function tubeMapLayout(graph: Graph): LayoutResult | undefined {\n const run = hasTubeMapPaths(graph) ? runTubeMap(graph) : undefined\n if (!run) {\n return undefined\n }\n const { layout, pathColors } = run\n const yOffset = layout.bounds.minY\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const node of drawnNodes(layout)) {\n nodePositions[node.name] = centreline(\n node,\n yOffset,\n node.x,\n node.x + node.pixelWidth,\n )\n }\n const { minY, maxY } = extentOf(layout, yOffset)\n return {\n nodePositions,\n tubeMap: { layout, yOffset, pathColors },\n extent: {\n minX: layout.bounds.minX,\n maxX: layout.bounds.maxX,\n minY,\n maxY,\n },\n }\n}\n\n// Each column's reference bp: the span of its backbone nodes, whose merged\n// length the layout summed, or a point where the previous column ended for a\n// column of inserted sequence alone. Clamped to run left to right, so an\n// inversion the reference walks backwards cannot fold the axis over.\nfunction tubeMapColumns(graph: Graph, layout: TubeMapLayout): TubeMapColumn[] {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const byOrder = new Map<number, LayoutNode[]>()\n for (const node of drawnNodes(layout)) {\n const list = byOrder.get(node.order)\n if (list) {\n list.push(node)\n } else {\n byOrder.set(node.order, [node])\n }\n }\n const columns: TubeMapColumn[] = []\n let end = -Infinity\n for (const order of [...byOrder.keys()].sort((a, b) => a - b)) {\n const nodes = byOrder.get(order)!\n let bp0 = Infinity\n let bp1 = -Infinity\n for (const node of nodes) {\n const graphNode = nodeById.get(node.name)\n if (graphNode && isBackbone(graphNode)) {\n bp0 = Math.min(bp0, graphNode.stable.start)\n bp1 = Math.max(bp1, graphNode.stable.start + node.sequenceLength)\n }\n }\n if (bp0 === Infinity) {\n bp0 = bp1 = end\n }\n if (bp0 < end) {\n bp0 = end\n bp1 = Math.max(end, bp1)\n }\n end = Math.max(end, bp1)\n columns.push({\n order,\n x0: Math.min(...nodes.map(n => n.x)),\n x1: Math.max(...nodes.map(n => n.x + n.pixelWidth)),\n bp0,\n bp1,\n })\n }\n // columns before the first backbone node take its start\n const first = columns.find(c => c.bp0 !== -Infinity)\n for (const column of columns) {\n if (column.bp0 === -Infinity) {\n column.bp0 = column.bp1 = first?.bp0 ?? 0\n }\n }\n return columns\n}\n\nfunction pathsReachBackbone(graph: Graph) {\n return graph.nodes.some(isBackbone) && hasTubeMapPaths(graph)\n}\n\nexport function tubeMapReferenceLayout(graph: Graph): LayoutResult | undefined {\n const run = pathsReachBackbone(graph) ? runTubeMap(graph) : undefined\n if (!run) {\n return undefined\n }\n const { layout, pathColors } = run\n const columns = tubeMapColumns(graph, layout)\n const byOrder = new Map(columns.map(c => [c.order, c]))\n const yOffset = layout.bounds.minY\n const nodePositions: Record<string, NodeSegment[]> = {}\n for (const node of drawnNodes(layout)) {\n const column = byOrder.get(node.order)!\n nodePositions[node.name] = centreline(node, yOffset, column.bp0, column.bp1)\n }\n return {\n nodePositions,\n tubeMap: { layout, yOffset, columns, pathColors },\n referenceAxis: true,\n pixelRows: true,\n extent: extentOf(layout, yOffset),\n }\n}\n", "import { panSNContig, 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// tell nodes on the reference walk's path from nodes off it. Off the path is\n// an alternative route through the graph, not sequence the reference lacks: at\n// a duplication the graph may route a copy the reference carries through nodes\n// of its own, so an off-path run is no copy count.\n\nexport interface WalkRun {\n // bp offset from the start of this walk's slice\n start: number\n bp: number\n onReference: boolean\n // lowest reference bp an on-reference run covers; the run covers `bp` of\n // reference contiguously. In a tandem array that is the reference copy the\n // graph threads this copy through, which need not be the one it most\n // resembles.\n referenceStart?: number\n // the walk crosses that reference from its end back to its start\n reversed?: true\n // bases of the walk's contig between two pieces the cut returned: the walk\n // left the window's nodes and came back, and the cut holds nothing of them\n gap?: true\n}\n\n// Where a row's bar lies on the walk's own contig: the contig position at the\n// bar's left end, and whether positions fall rightward along it\nexport interface WalkAxis {\n contig: string\n start: number\n reversed: boolean\n}\n\nexport interface WalkRow {\n name: string\n label: string\n sample: string\n haplotype?: number\n bp: number\n offReferenceBp: number\n // bp of the gap runs, counted in `bp` and not in `offReferenceBp`\n gapBp: 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 // undefined where a piece states no start or overlaps the one before, so\n // bar offsets don't map linearly onto the contig\n axis?: WalkAxis\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// A walk's steps through the cut: its node ids, and between two pieces of it\n// the bp of contig the cut does not hold\ntype Step = string | number\n\n// The pieces of one walk in contig order, with the bp between them as gaps.\n// A piece with no start, or one that overlaps the last, follows it directly.\nfunction stepsOf(pieces: GraphPath[], lengthOf: Map<string, number>): Step[] {\n const steps: Step[] = []\n let end: number | undefined\n for (const piece of pieces) {\n if (end !== undefined && piece.start !== undefined && piece.start > end) {\n steps.push(piece.start - end)\n }\n steps.push(...piece.nodeIds)\n const bp = piece.nodeIds.reduce(\n (sum, id) => sum + (lengthOf.get(id) ?? 0),\n 0,\n )\n end = piece.start === undefined ? undefined : piece.start + bp\n }\n return steps\n}\n\n// Where each step lies on the walk's contig, steps and gaps alike; undefined\n// where a piece states no start or overlaps the one before\nfunction stepSpans(pieces: GraphPath[], lengthOf: Map<string, number>) {\n const spans: { start: number; end: number }[] = []\n let end: number | undefined\n for (const piece of pieces) {\n if (piece.start === undefined || (end !== undefined && piece.start < end)) {\n return undefined\n }\n if (end !== undefined && piece.start > end) {\n spans.push({ start: end, end: piece.start })\n }\n let pos = piece.start\n for (const id of piece.nodeIds) {\n const len = lengthOf.get(id) ?? 0\n spans.push({ start: pos, end: pos + len })\n pos += len\n }\n end = pos\n }\n return spans\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 steps: Step[],\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: steps, complete: true, from: -1, to: -1 }\n }\n let i0 = -1\n let i1 = -1\n let bestEnd = -Infinity\n let bestStart = Infinity\n steps.forEach((id, i) => {\n const s = typeof id === 'string' ? span.get(id) : undefined\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: steps, complete: false, from: -1, to: -1 }\n }\n const ids = steps.slice(Math.min(i0, i1) + 1, Math.max(i0, i1))\n return {\n ids: i0 < i1 ? ids : ids.reverse(),\n complete: true,\n from: i0,\n to: i1,\n }\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. A cut over several\n // fragments of the reference holds one record per fragment.\n const isReference = (p: GraphPath) =>\n pathOrigin(p.name).name === graph.referencePath\n const referencePieces = paths.some(isReference)\n ? paths.filter(isReference).sort((a, b) => (a.start ?? 0) - (b.start ?? 0))\n : paths.slice(0, 1)\n const first = referencePieces[0]\n const others = paths.filter(p => !referencePieces.includes(p))\n if (!first || others.length === 0) {\n return undefined\n }\n // a cut hands a walk back as one record per piece inside its nodes\n const walks = new Map<string, GraphPath[]>()\n for (const path of others) {\n walks.set(path.name, [...(walks.get(path.name) ?? []), path])\n }\n const lengthOf = new Map(graph.nodes.map(n => [n.id, n.length]))\n const referenceStart = first.start ?? 0\n\n const cut = region && region.end > region.start ? region : undefined\n const span = new Map<string, { start: number; end: number }>()\n for (const piece of referencePieces) {\n let pos = piece.start ?? 0\n for (const id of piece.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\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 = (pieces: GraphPath[]): WalkRow => {\n const path = pieces[0]!\n const ordered = [...pieces].sort((a, b) => (a.start ?? 0) - (b.start ?? 0))\n const { ids, complete, from, to } = sliceBetween(\n stepsOf(ordered, lengthOf),\n span,\n cut,\n flanked,\n )\n const spans = stepSpans(ordered, lengthOf)\n const contig = path.contig ?? panSNContig(pathOrigin(path.name).name)\n const axis: WalkAxis | undefined = !spans?.length\n ? undefined\n : from < 0\n ? { contig, start: spans[0]!.start, reversed: false }\n : from < to\n ? { contig, start: spans[from]!.end, reversed: false }\n : { contig, start: spans[from]!.start, reversed: true }\n const runs: WalkRun[] = []\n let bp = 0\n let offReferenceBp = 0\n let gapBp = 0\n // which way the last run steps through the reference, 0 while it holds\n // one node\n let step = 0\n for (const id of ids) {\n if (typeof id === 'number') {\n runs.push({ start: bp, bp: id, onReference: false, gap: true })\n bp += id\n gapBp += id\n step = 0\n continue\n }\n const len = lengthOf.get(id) ?? 0\n const s = span.get(id)\n const last = runs.at(-1)\n const at = last?.referenceStart\n const forward =\n s && at !== undefined && step >= 0 && at + last!.bp === s.start\n const backward = s && at !== undefined && step <= 0 && s.end === at\n if (!s && last && !last.onReference && !last.gap) {\n last.bp += len\n } else if (forward || backward) {\n last!.bp += len\n if (!forward) {\n last!.referenceStart = s.start\n last!.reversed = true\n }\n step = forward ? 1 : -1\n } else {\n runs.push({\n start: bp,\n bp: len,\n onReference: s !== undefined,\n referenceStart: s?.start,\n })\n step = 0\n }\n bp += len\n if (!s) {\n offReferenceBp += len\n }\n }\n return {\n name: path.name,\n label: labelOf(path),\n sample: sampleOf(path),\n haplotype: path.haplotype,\n bp,\n offReferenceBp,\n gapBp,\n complete,\n runs,\n axis,\n }\n }\n\n const origin = cut ? cut.start : referenceStart\n return {\n origin,\n unit,\n reference: rowOf(referencePieces),\n rows: [...walks.values()]\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 {\n backboneNodes,\n backbonePositions,\n overlapsWindow,\n} 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 // The bars start at the window, so row 0 draws only the backbone over it;\n // the cut's context nodes would run left of the bars, under the labels.\n const overWindow = region\n ? backbone.filter(n => overlapsWindow(n, region))\n : backbone\n const nodePositions = backbonePositions(\n overWindow.length > 0 ? overWindow : backbone,\n )\n const rowLabels: RowLabel[] = [\n { label: walks.reference.label, y: 0 },\n ...walks.rows.map((row, i) => ({\n label: row.label,\n y: (i + 1) * ROW_HEIGHT_PX,\n })),\n ]\n return {\n nodePositions,\n rowLabels,\n referenceAxis: true,\n pixelRows: true,\n extent: walkRowsExtent(walks),\n }\n}\n", "import { isBackbone, isOffReference } from './anchoredNodes'\nimport { anchoredLayout } from './layout/anchoredLayout'\nimport { orderedLayout } from './layout/orderedLayout'\nimport { sampleRowLayout } from './layout/sampleRowLayout'\nimport {\n hasTubeMapPaths,\n tubeMapLayout,\n tubeMapReferenceLayout,\n} from './layout/tubeMapLayout'\nimport { walkRowLayout } from './layout/walkRowLayout'\n\nimport type { Graph, LayoutResult } from './types'\n\n// The force layout's menu label. jbrowse-components' check-menu-labels scans\n// this src/ for string literals, so a documented **Layout \u2192 \u2026** path is held\n// to what the dropdown renders.\nexport const FORCE_LAYOUT_LABEL = 'Force-directed layout'\n\n// One table drives the persisted enum, the toolbar dropdown, and the dispatch in\n// the model, so adding a layout is one entry rather than several edits that can\n// disagree.\n//\n// `run` returning undefined means \"this mode can't draw this graph\" and hands\n// off to the remote Bandage FMMM engine. 'auto' uses it when a GFA has no\n// rank-0 backbone \u2014 neither declared in rGFA tags nor derivable from P/W lines\n// (pathAnchoring.ts) \u2014 and 'force' is simply the mode that always takes it.\nexport interface LayoutMode {\n value: string\n label: string\n description: string\n // `region` is the interval the cut was made for, absent for a whole-file\n // import. The reference-anchored modes scale their rows and their allele\n // floor against it rather than against the backbone they were handed, which\n // a long-range allele's far anchor stretches well past the window\n // (referenceSpan). `keepRows` is the sample rows on screen, which only the\n // sample-row layout reads (LayoutResult.sampleRows).\n run: (\n graph: Graph,\n region?: { start: number; end: number },\n keepRows?: readonly string[],\n ) => LayoutResult | undefined\n // whether this mode can draw this graph at all; the dropdown greys out the\n // rest rather than hiding them, so the reason a mode is unavailable stays\n // visible instead of the menu silently changing shape between graphs\n available: (graph: Graph) => boolean\n // whether a cut for this mode carries a window-width margin each side, to\n // pan over before the next cut. A mode that draws a picture of the window\n // \u2014 force, ordered, and walk rows, whose bars are lengths through it \u2014 is\n // cut to the window alone.\n cutMargins: boolean\n // whether this mode measures each haplotype's walk whole, so a GBZ cut\n // follows every snarl that leaves the window rather than breaking a walk\n // where it leaves the window's snarls: at a duplication array the lighter\n // cut hands one walk back in pieces\n wholeWalks: boolean\n // whether `run` draws this graph itself, as opposed to handing off to the\n // engine. Not the same question as `available` and not derivable from it:\n // 'force' is available for every graph and draws none of them itself. The two\n // coincide for the anchored modes, which is stated by sharing the predicate\n // rather than by restating it.\n drawsLocally: (graph: Graph) => boolean\n // whether the canvas draws this mode's nodes, which genes, bubble halos,\n // deletion arcs, the reference strip, facet panels and an SVG figure sit\n // on. A tube map and walk rows draw a picture of their own over it.\n drawsNodes: boolean\n}\n\nconst hasBackbone = (graph: Graph) => graph.nodes.some(isBackbone)\nconst hasWalks = (graph: Graph) =>\n hasBackbone(graph) && (graph.paths?.length ?? 0) > 1\nconst hasTubeMapBackbone = (graph: Graph) =>\n hasBackbone(graph) && hasTubeMapPaths(graph)\nconst hasAlleles = (graph: Graph) =>\n hasBackbone(graph) && graph.nodes.some(isOffReference)\n\n// `satisfies` rather than a `: LayoutMode[]` annotation: the annotation widened\n// every `value` to string, which collapsed LayoutModeValue to string and left the\n// persisted enum unable to state which values it accepts.\nexport const LAYOUT_MODES = [\n {\n value: 'auto',\n label: 'Anchored',\n description:\n 'x is reference bp, one row per stable rank. Needs rGFA tags or a reference path.',\n run: anchoredLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n drawsNodes: true,\n },\n {\n value: 'samplerows',\n label: 'Sample rows',\n description:\n 'x is reference bp, one row per contributing assembly. Needs rGFA tags or a reference path.',\n run: sampleRowLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasAlleles,\n drawsLocally: hasAlleles,\n drawsNodes: true,\n },\n {\n value: 'walkrows',\n label: 'Walk rows',\n description:\n \"x is each walk's own bp: one bar per haplotype, blue on the reference's path and purple off it, so a repeat expansion reads as bar length, and each bar boxes its haplotype's own genes where the session has them. Needs W or P lines.\",\n run: walkRowLayout,\n cutMargins: false,\n wholeWalks: true,\n available: hasWalks,\n drawsLocally: hasWalks,\n drawsNodes: false,\n },\n {\n value: 'ordered',\n label: 'Ordered',\n description:\n 'x is reference order, not bp: every node gets room, bubbles read as lenses. Needs rGFA tags or a reference path.',\n run: orderedLayout,\n cutMargins: false,\n wholeWalks: false,\n available: hasBackbone,\n drawsLocally: hasBackbone,\n drawsNodes: true,\n },\n {\n value: 'tubemap',\n label: 'Tube map',\n description:\n 'sequenceTubeMap: every path a coloured tube through boxed nodes, columns in node order and node widths log-scaled. Needs P or W lines.',\n run: tubeMapLayout,\n cutMargins: false,\n wholeWalks: false,\n available: hasTubeMapPaths,\n drawsLocally: hasTubeMapPaths,\n drawsNodes: false,\n },\n {\n value: 'tubemapref',\n label: 'Tube map on reference',\n description:\n 'The tube map with each column at the reference bp it covers, so the tubes line up with the tracks around them. Needs P or W lines and a reference path.',\n run: tubeMapReferenceLayout,\n cutMargins: true,\n wholeWalks: false,\n available: hasTubeMapBackbone,\n drawsLocally: hasTubeMapBackbone,\n drawsNodes: false,\n },\n {\n value: 'force',\n label: FORCE_LAYOUT_LABEL,\n description: 'OGDF FMMM, via the external Bandage engine.',\n run: () => undefined,\n cutMargins: false,\n wholeWalks: false,\n available: () => true,\n drawsLocally: () => false,\n drawsNodes: true,\n },\n] as const satisfies readonly LayoutMode[]\n\nexport type LayoutModeValue = (typeof LAYOUT_MODES)[number]['value']\n\nexport const LAYOUT_MODE_VALUES = LAYOUT_MODES.map(m => m.value)\n\n// Whether the drawing a mode produces for a graph comes from the FMMM engine.\n// The settings that only the engine reads \u2014 layout quality, bubble spread \u2014\n// are shown against this, so a control that cannot do anything to the drawing\n// on screen says so instead of appearing to work.\nexport function modeUsesLayoutEngine(value: string, graph: Graph) {\n return !layoutModeByValue(value).drawsLocally(graph)\n}\n\nexport function layoutModeByValue(value: string) {\n // An unknown value can only come from a snapshot written by a build that had\n // a mode this one doesn't. It falls back to 'auto' rather than to the model's\n // own default of 'force', because 'auto' declines any graph it cannot draw and\n // hands off to force anyway, so this is the fallback that covers both.\n return LAYOUT_MODES.find(m => m.value === value) ?? LAYOUT_MODES[0]\n}\n", "import { SATURATED_PATH_COUNT } from './bubbleLine'\n\nimport type { MinigraphBubble } from './bubbleLine'\n\n// What kind of variation a bubble is, read off the numbers `gfatools bubble`\n// states: the reference interval it replaces, its shortest and longest route,\n// how many routes, and whether one of them inverts. The reference route is one\n// of the routes and its length is the interval, so the shortest and longest\n// against the interval say whether the alternatives add sequence, remove it,\n// or swap it. Whether it is a repeat is not in those numbers: a\n// presence/absence insertion carried by many haplotypes has the same shape as\n// an expanding array, so that comes from the session's repeat annotation.\nexport type BubbleKind =\n | 'snp'\n | 'substitution'\n | 'insertion'\n | 'deletion'\n | 'inversion'\n | 'repeat'\n | 'superbubble'\n | 'complex'\n\nexport interface BubbleClass {\n kind: BubbleKind\n label: string\n}\n\n// An annotated tandem repeat array over the cut (repeats/repeatFeatures).\n// Its refName is not compared: the arrays are read over the same window as\n// the bubbles, which spell the reference their own way (PanSN).\nexport interface RepeatSpan {\n start: number\n end: number\n name?: string\n}\n\n// How far past its reference length an array's alleles may run and still be\n// copies of it: KIV-2's 40 kb array carries 175 kb alleles\nconst REPEAT_GROWTH = 10\n\n// The array covering at least half the bubble's reference interval, or the\n// point of an insertion, and long enough to account for its alleles: a\n// microsatellite at an insertion point does not make the insertion a repeat.\nfunction coveringRepeat(b: MinigraphBubble, repeats: readonly RepeatSpan[]) {\n const span = b.end - b.start\n const growth = b.longestAlleleLength - b.shortestAlleleLength\n return repeats.find(\n r =>\n growth <= REPEAT_GROWTH * (r.end - r.start) &&\n (span === 0\n ? r.start <= b.start && b.start <= r.end\n : Math.min(b.end, r.end) - Math.max(b.start, r.start) >= span / 2),\n )\n}\n\nconst SUPERBUBBLE_SEGMENTS = 40\n\nexport function formatBp(bp: number) {\n if (bp >= 10_000) {\n return `${(bp / 1000).toFixed(0)} kb`\n }\n if (bp >= 1000) {\n return `${(bp / 1000).toFixed(1)} kb`\n }\n return `${bp} bp`\n}\n\nfunction formatBpRange(shortest: number, longest: number) {\n const low = formatBp(shortest)\n const high = formatBp(longest)\n const unit = high.slice(high.indexOf(' '))\n return low.endsWith(unit)\n ? `${low.slice(0, -unit.length)}\u2013${high}`\n : `${low}\u2013${high}`\n}\n\nfunction routes(count: number) {\n if (count >= SATURATED_PATH_COUNT) {\n return '\u22652.1B routes'\n }\n if (count >= 100_000) {\n return `${(count / 1000).toFixed(0)}k routes`\n }\n return `${count} routes`\n}\n\nexport function classifyBubble(\n b: MinigraphBubble,\n repeats: readonly RepeatSpan[] = [],\n): BubbleClass {\n const c = classifyShape(b, repeats)\n return b.partial ? { ...c, label: `${c.label}, partial` } : c\n}\n\nfunction classifyShape(\n b: MinigraphBubble,\n repeats: readonly RepeatSpan[],\n): BubbleClass {\n const refSpan = b.end - b.start\n const {\n shortestAlleleLength: shortest,\n longestAlleleLength: longest,\n pathCount,\n segmentCount,\n inversion,\n } = b\n const range = formatBpRange(shortest, longest)\n if (segmentCount >= SUPERBUBBLE_SEGMENTS) {\n return {\n kind: 'superbubble',\n label: `${range} superbubble, ${segmentCount} segments, ${routes(pathCount)}${inversion ? ', inv' : ''}`,\n }\n }\n if (inversion) {\n return { kind: 'inversion', label: `${formatBp(longest)} inv` }\n }\n // routes of one length are substitutions inside an array, not copies\n const repeat = longest !== shortest ? coveringRepeat(b, repeats) : undefined\n if (repeat) {\n const name = repeat.name ? ` (${repeat.name})` : ''\n return {\n kind: 'repeat',\n label: `${range} repeat array${name}, ${routes(pathCount)}`,\n }\n }\n const alleles = pathCount > 2 ? `, ${pathCount} alleles` : ''\n if (shortest === longest && shortest === refSpan) {\n return refSpan <= 1\n ? { kind: 'snp', label: 'SNP' }\n : { kind: 'substitution', label: `${formatBp(refSpan)} sub` }\n }\n if (refSpan === 0 || shortest === refSpan) {\n return {\n kind: 'insertion',\n label: `\u2264${formatBp(longest - refSpan)} ins${alleles}`,\n }\n }\n if (longest === refSpan) {\n return {\n kind: 'deletion',\n label: `${formatBp(refSpan - shortest)} del${alleles}`,\n }\n }\n return {\n kind: 'complex',\n label: `${formatBp(refSpan)} ref \u2192 ${range}, ${pathCount} alleles`,\n }\n}\n\n// One hue per kind, the deletion arc's near-black and the reference blue kept\n// clear of, so a glyph is not mistaken for either.\nexport const BUBBLE_KIND_COLORS: Record<BubbleKind, string> = {\n snp: '#5b6b7a',\n substitution: '#5b6b7a',\n insertion: '#2f8fd6',\n deletion: '#c94040',\n inversion: '#e07b00',\n repeat: '#8e3fbf',\n superbubble: '#8e3fbf',\n complex: '#8e3fbf',\n}\n\n// The kind as a short name, for a place that has no room for the label\nexport const BUBBLE_KIND_NAMES: Record<BubbleKind, string> = {\n snp: 'SNP',\n substitution: 'substitution',\n insertion: 'insertion',\n deletion: 'deletion',\n inversion: 'inversion',\n repeat: 'repeat array',\n superbubble: 'superbubble',\n complex: 'site',\n}\n\nexport function bubbleSegmentIds(b: MinigraphBubble) {\n return b.segments.split(',').filter(Boolean)\n}\n", "// Panels this far apart, each a title row over a drawing padded this much\nexport const FACET_GAP_PX = 8\nexport const FACET_TITLE_PX = 34\nexport const FACET_PAD_PX = 12\nconst MIN_FACET_PANEL_PX = 60\n\nexport interface FacetGrid {\n columns: number\n width: number\n height: number\n // the drawing's scale in each panel\n scale: number\n total: number\n}\n\n// How `count` panels of one drawing tile a pane `width` wide that may grow to\n// `room` tall. Unless `columns` says, as many go across as draws each panel\n// largest, as facet_wrap chooses its grid: four square drawings go two by\n// two in a tall pane and four across in a wide one, and a drawing far wider\n// than it is tall stacks. A row layout's rows are px, so its scale is x\n// alone and its panels stack. Each panel is as tall as its drawing needs.\nexport function facetGrid({\n count,\n bounds,\n pixelRows,\n width,\n room,\n columns,\n}: {\n count: number\n bounds: { w: number; h: number }\n pixelRows: boolean\n width: number\n room: number\n columns?: number\n}): FacetGrid {\n function tile(across: number): FacetGrid {\n const rows = Math.ceil(count / across)\n const panelWidth = Math.floor(\n (width - (across - 1) * FACET_GAP_PX) / across,\n )\n const panelRoom =\n (room - rows * FACET_TITLE_PX - (rows - 1) * FACET_GAP_PX) / rows\n const fitX = (panelWidth - 2 * FACET_PAD_PX) / bounds.w\n const fitY =\n bounds.h > 0 ? (panelRoom - 2 * FACET_PAD_PX) / bounds.h : Infinity\n const scale = pixelRows ? fitX : Math.min(fitX, fitY)\n const drawn = pixelRows ? bounds.h : bounds.h * scale\n const height = Math.floor(\n Math.max(\n MIN_FACET_PANEL_PX,\n Math.min(drawn + 2 * FACET_PAD_PX, panelRoom),\n ),\n )\n return {\n columns: across,\n width: panelWidth,\n height,\n scale,\n total: rows * (height + FACET_TITLE_PX) + (rows - 1) * FACET_GAP_PX,\n }\n }\n if (columns !== undefined) {\n return tile(Math.max(1, Math.min(count, Math.round(columns))))\n }\n let best = tile(1)\n for (let across = 2; across <= count; across++) {\n const grid = tile(across)\n if (grid.scale > best.scale) {\n best = grid\n }\n }\n return best\n}\n\nexport type FacetBy = 'walk' | 'sample'\n\n/**\n * The facet as a session or a figure spec writes it, in the shape every\n * JBrowse display's `facet` takes: the field the panels split on, `walk` for\n * a panel per lifted walk or `sample` for a row per sample and a column per\n * haplotype, the order the panels take, and how many go across under `walk`.\n */\nexport interface FacetSetting {\n field: '' | FacetBy\n /** The walks, or under `sample` the samples, whose panels come first, in order. */\n domain: string[]\n /** How many panels go across; unset takes whichever count draws each largest. */\n columns?: number\n}\n\n/** What a session or a spec may write for the facet: the field bare, or the setting's members. */\nexport type FacetInput =\n string | { field?: string; domain?: readonly string[]; columns?: number }\n\n/** A facet written bare is its field, and a field the pane cannot split on is none. */\nexport function facetSettingOf(facet: FacetInput | undefined): FacetSetting {\n const raw = typeof facet === 'string' ? { field: facet } : (facet ?? {})\n const field = raw.field === 'walk' || raw.field === 'sample' ? raw.field : ''\n return {\n field,\n domain: [...(raw.domain ?? [])],\n ...(raw.columns === undefined ? {} : { columns: raw.columns }),\n }\n}\n\n// The listed names first, in the domain's order, then the rest as they came\nfunction domainFirst(names: readonly string[], domain: readonly string[]) {\n const rank = new Map(domain.map((name, i) => [name, i]))\n const listed = (name: string) => rank.get(name) ?? domain.length\n return names\n .map((name, i) => ({ name, i }))\n .sort((a, b) => listed(a.name) - listed(b.name) || a.i - b.i)\n .map(({ name }) => name)\n}\n\n// Which grid cell each walk's panel takes, and how many go across when the\n// arrangement fixes it. By walk the panels wrap in reading order. By sample\n// each sample takes a row and each haplotype a column, as facet_grid(sample ~\n// haplotype) lays out a plot, so a sample's haplotypes read across its row and\n// a haploid reference sits in the first column. Names that are not\n// `sample#haplotype#contig`, or two walks landing in one cell, wrap instead.\n// A `domain` puts the walks, or the samples, it lists first.\nexport function facetCells(\n names: string[],\n by: FacetBy,\n domain: readonly string[] = [],\n) {\n const wrapOrder = domainFirst(names, domain)\n const wrap = {\n columns: undefined,\n count: names.length,\n cells: names.map(name => wrapOrder.indexOf(name)),\n }\n if (by === 'walk') {\n return wrap\n }\n const places = names.map(name => {\n const [sample, haplotype, contig] = name.split('#')\n const n = Number(haplotype)\n return contig !== undefined && Number.isInteger(n) && n >= 0\n ? { sample: sample!, column: Math.max(0, n - 1) }\n : undefined\n })\n const rows = domainFirst(\n [...new Set(places.flatMap(p => (p ? [p.sample] : [])))],\n domain,\n )\n const columns = Math.max(1, ...places.map(p => (p ? p.column + 1 : 1)))\n const cells = places.map(p =>\n p ? rows.indexOf(p.sample) * columns + p.column : -1,\n )\n return cells.includes(-1) || new Set(cells).size < cells.length\n ? wrap\n : { columns, count: rows.length * columns, cells }\n}\n", "// A drawing as a plain element tree, so the hosts render the same picture\n// their own way: the plugin as React elements, BandageJS as DOM nodes, a\n// figure as markup. Attribute names are SVG's own (`stroke-width`,\n// `text-anchor`); serializeEl is the one place text becomes markup.\n\nexport type ElAttrs = Record<string, string | number | undefined>\n\nexport interface El {\n tag: string\n attrs: ElAttrs\n children: (El | string)[]\n}\n\nexport function el(\n tag: string,\n attrs: ElAttrs = {},\n ...children: (El | string | undefined | false)[]\n): El {\n return {\n tag,\n attrs,\n children: children.filter((c): c is El | string => !!c || c === ''),\n }\n}\n\nfunction escapeText(s: string) {\n return s\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n}\n\nfunction escapeAttr(s: string) {\n return escapeText(s).replaceAll('\"', '&quot;')\n}\n\nexport function serializeEl(node: El | string): string {\n if (typeof node === 'string') {\n return escapeText(node)\n }\n const attrs = Object.entries(node.attrs)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => ` ${k}=\"${escapeAttr(String(v))}\"`)\n .join('')\n return `<${node.tag}${attrs}>${node.children.map(serializeEl).join('')}</${node.tag}>`\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { el } from '../el'\nimport { LABEL_CHAR_PX } from '../overlayLabels'\nimport { rampHueCss, rampStops } from '../referenceRampCss'\nimport { REFERENCE_RAMP_ALT_CSS } from '../renderer/GeometryBuilder'\n\nimport type { WalkAxis, WalkRow, WalkRows, WalkRun } from './walkRows'\nimport type { El } from '../el'\nimport type { GeneModel } from '../genes/genePins'\n\n// What walk rows draw, shared by the hosts: walkRowsTree is the drawing as an\n// element tree, which the plugin renders as React elements, BandageJS as DOM\n// nodes and the figure export as markup, so the bars, readouts, genes and key\n// agree everywhere.\n\nexport const ON_REFERENCE = '#2f8fd6'\nexport const OFF_REFERENCE = '#8e3fbf'\nexport const OUTSIDE_CUT = '#bdbdbd'\nexport const GENE_INK = '#1c1c22'\nexport const BAR_PX = 12\nexport const GAP_PX = 4\n// a unit separator is dropped when a unit is under this many px\nconst MIN_TILE_PX = 3\n\nexport function kb(bp: number) {\n return `${(bp / 1000).toFixed(bp < 10_000 ? 1 : 0)} kb`\n}\n\nfunction units(bp: number, unit: number | undefined) {\n return unit ? ` \u2248 ${Math.round(bp / unit)} units` : ''\n}\n\n// a repeat genotype's allele length for a walk, as the plugin's repeat\n// catalogue pairs one with a row\nexport interface ReadoutCall {\n bp: number\n spanningReads?: number\n // whether the walk's length and the called allele agree, where both are\n // measurements\n agrees?: boolean\n}\n\n// The text at the end of a bar: its length, what it carries against the\n// reference row, and what the cut left out\nexport function walkRowReadout(\n row: Pick<WalkRow, 'bp' | 'gapBp' | 'complete'>,\n reference: Pick<WalkRow, 'bp'> | undefined,\n unit?: number,\n call?: ReadoutCall,\n) {\n const { bp, gapBp, complete } = row\n if (!reference) {\n return `${kb(bp)}${units(bp, unit)}`\n }\n const delta = bp - reference.bp\n const against =\n delta === 0 ? '' : ` (${delta > 0 ? '+' : '\u2212'}${kb(Math.abs(delta))})`\n const outside = gapBp > 0 ? ` \u00B7 ${kb(gapBp)} outside the cut` : ''\n const called = call\n ? ` \u00B7 called ${kb(call.bp)}${call.spanningReads === 0 ? ' \u00B7 no spanning read' : ''}`\n : ''\n return `${kb(bp)}${units(bp, unit)}${against}${outside}${complete ? '' : ' \u00B7 partial walk'}${called}`\n}\n\n// A readout right of its bar's end, or inside it where it would leave the pane\nexport function readoutPlacement(text: string, endX: number, width: number) {\n const x = endX + 6\n const fits = x + text.length * LABEL_CHAR_PX < width\n return fits\n ? { x, anchor: 'start' as const, halo: false }\n : { x: endX - 6, anchor: 'end' as const, halo: true }\n}\n\n// Bar offsets one unit apart, so copies are countable, none where a unit is\n// under a few px\nexport function unitTicks(bp: number, unit: number, pxPerBp: number) {\n if (unit * pxPerBp < MIN_TILE_PX) {\n return []\n }\n const ticks: number[] = []\n for (let k = unit; k < bp; k += unit) {\n ticks.push(k)\n }\n return ticks\n}\n\n// A run's paint: a flat colour, or under the reference-position ramp the hues\n// a gradient along a shared run takes, since it covers its reference\n// contiguously\nexport function runPaint(\n run: WalkRun,\n ramp: { start: number; end: number } | undefined,\n): { fill: string } | { stops: string[] } {\n if (run.gap) {\n return { fill: OUTSIDE_CUT }\n }\n if (!ramp) {\n return { fill: run.onReference ? ON_REFERENCE : OFF_REFERENCE }\n }\n if (run.referenceStart === undefined) {\n return { fill: REFERENCE_RAMP_ALT_CSS }\n }\n const stops = rampStops({ ...run, start: run.referenceStart }, ramp)\n return stops.length === 1\n ? { fill: rampHueCss(stops[0]!) }\n : { stops: stops.map(rampHueCss) }\n}\n\n// A contig span as offsets along a row's bar\nexport function alongRow(axis: WalkAxis, start: number, end: number) {\n return axis.reversed\n ? { start: axis.start - end, end: axis.start - start }\n : { start: start - axis.start, end: end - axis.start }\n}\n\n// The contig span a row's bar covers\nexport function rowSpan(axis: WalkAxis, bp: number) {\n return axis.reversed\n ? { start: axis.start - bp, end: axis.start }\n : { start: axis.start, end: axis.start + bp }\n}\n\nexport interface RowGene {\n name: string\n start: number\n end: number\n exons: { start: number; end: number }[]\n}\n\n// Each row's genes as offsets along its bar, from genes the host read for that\n// row's own contig, cut to the bar\nexport function placeRowGenes(\n rows: WalkRow[],\n byRow: Map<string, GeneModel[]>,\n) {\n const placed = new Map<string, RowGene[]>()\n for (const row of rows) {\n const genes = byRow.get(row.name)\n if (!row.axis || !genes) {\n continue\n }\n const axis = row.axis\n const clip = ({ start, end }: { start: number; end: number }) => ({\n start: Math.max(0, start),\n end: Math.min(row.bp, end),\n })\n placed.set(\n row.name,\n genes\n .map(g => ({\n name: g.name,\n ...clip(alongRow(axis, g.start, g.end)),\n exons: g.exons\n .map(e => clip(alongRow(axis, e.start, e.end)))\n .filter(e => e.end > e.start),\n }))\n .filter(g => g.end > g.start)\n .sort((a, b) => a.start - b.start),\n )\n }\n return placed\n}\n\nconst GENE_FONT_PX = 9\nconst GENE_CHAR_PX = 5.2\nconst MIN_LETTERED_BAR_PX = 8\n\nconst genePad = (rowPx: number, barPx: number) =>\n Math.min(2, (rowPx - barPx) / 2 - 1)\n\n// Whether rows at this pitch leave room to box a gene round its bar. Closer\n// rows draw none, since a gene's shade on a bar that thin reads as its colour.\nexport function boxesGenes(rowPx: number, barPx: number) {\n return genePad(rowPx, barPx) >= 1\n}\n\nexport interface GeneBox {\n name: string\n x: number\n y: number\n w: number\n h: number\n exons: { x: number; w: number }[]\n // the name inside the box where it fits and lands on no other name, shorter\n // genes taking their room first\n label?: { x: number; y: number; size: number }\n}\n\n// A row's genes as boxes in screen px around its bar at `y`, at the rows'\n// pitch, named where the bar is tall enough to letter; none where the rows\n// leave no room to box them\nexport function rowGeneBoxes(\n genes: RowGene[],\n X: (offset: number) => number,\n y: number,\n { rowPx = ROW_HEIGHT_PX, barPx = BAR_PX } = {},\n): GeneBox[] {\n if (!boxesGenes(rowPx, barPx)) {\n return []\n }\n const pad = genePad(rowPx, barPx)\n const boxes = genes.map(g => {\n const x = X(g.start)\n return {\n g,\n x,\n w: Math.max(2, X(g.end) - x),\n }\n })\n const taken: [number, number][] = []\n const named = new Set(\n (barPx >= MIN_LETTERED_BAR_PX ? [...boxes] : [])\n .sort((a, b) => a.w - b.w)\n .filter(({ g, x, w }) => {\n const half = (g.name.length * GENE_CHAR_PX) / 2\n const at: [number, number] = [x + w / 2 - half, x + w / 2 + half]\n if (\n 2 * half > w + 6 ||\n taken.some(([a, b]) => at[0] < b + 2 && at[1] > a - 2)\n ) {\n return false\n }\n taken.push(at)\n return true\n }),\n )\n return boxes.map(box => ({\n name: box.g.name,\n x: box.x,\n y: y - barPx / 2 - pad,\n w: box.w,\n h: barPx + 2 * pad,\n exons: box.g.exons.map(e => ({\n x: X(e.start),\n w: Math.max(1, X(e.end) - X(e.start)),\n })),\n label: named.has(box)\n ? { x: box.x + box.w / 2, y: y + 3, size: GENE_FONT_PX }\n : undefined,\n }))\n}\n\nexport type KeySwatch =\n | { kind: 'bar'; fill: string }\n | { kind: 'gap'; fill: string }\n | { kind: 'gene' }\n\nexport interface KeyEntry {\n swatch: KeySwatch\n label: string\n // a line under the entries, saying what the key leaves out\n note?: true\n}\n\n// The rows a host could read no genes for\nexport interface GeneGaps {\n // rows too close to box genes in, which leaves the rest moot\n crowded?: boolean\n untracked: number\n // rows whose walk states no contig coordinates to read genes over\n unplaced?: number\n unread: number\n}\n\nconst rowsText = (n: number) => `${n} row${n === 1 ? '' : 's'}`\n\n// What the bar colours mean: whether a run is on the reference walk's path.\n// Off the path is an alternative route, no statement about the sequence: at a\n// duplication the graph may route a copy the reference carries through nodes\n// of its own.\nexport function walkRowsKey(\n bars: WalkRows,\n o: {\n ramp?: { start: number; end: number }\n rampCss?: string\n genes?: GeneGaps\n } = {},\n): KeyEntry[] {\n const entries: KeyEntry[] = [\n {\n swatch: {\n kind: 'bar',\n fill: o.ramp ? (o.rampCss ?? ON_REFERENCE) : ON_REFERENCE,\n },\n label: `on ${bars.reference.label}'s path`,\n },\n {\n swatch: {\n kind: 'bar',\n fill: o.ramp ? REFERENCE_RAMP_ALT_CSS : OFF_REFERENCE,\n },\n label: `off ${bars.reference.label}'s path`,\n },\n ]\n if ([bars.reference, ...bars.rows].some(row => row.gapBp > 0)) {\n entries.push({\n swatch: { kind: 'gap', fill: OUTSIDE_CUT },\n label: 'walked outside the cut',\n })\n }\n if (o.genes?.crowded) {\n entries.push({\n swatch: { kind: 'gene' },\n label: 'genes left out: too many rows to box them in',\n note: true,\n })\n } else if (o.genes) {\n entries.push({\n swatch: { kind: 'gene' },\n label: \"genes, each row's own annotation\",\n })\n if (o.genes.untracked) {\n entries.push({\n swatch: { kind: 'gene' },\n label: `no gene track for ${rowsText(o.genes.untracked)}`,\n note: true,\n })\n }\n if (o.genes.unplaced) {\n entries.push({\n swatch: { kind: 'gene' },\n label: `no contig coordinates for ${rowsText(o.genes.unplaced)}`,\n note: true,\n })\n }\n if (o.genes.unread) {\n entries.push({\n swatch: { kind: 'gene' },\n label: `genes not read for the last ${rowsText(o.genes.unread)}`,\n note: true,\n })\n }\n }\n return entries\n}\n\n// walk rows whose rows may carry the allele a repeat genotype called\nexport type WalkRowsWithCalls = Omit<WalkRows, 'reference' | 'rows'> & {\n reference: WalkRow & { call?: ReadoutCall }\n rows: (WalkRow & { call?: ReadoutCall })[]\n}\n\nconst CALL_TICK = '#111'\nconst UNBACKED_TICK = '#9e9e9e'\nexport const DISAGREES = '#c62828'\n\n// The key at (x, y), one entry after another along a line, and about how\n// wide it is\nexport function walkRowsKeyTree(entries: KeyEntry[], x: number, y: number) {\n let at = 0\n const parts = entries.flatMap(e => {\n const swatch = e.note\n ? undefined\n : e.swatch.kind === 'gene'\n ? el('rect', {\n x: at,\n y: 5,\n width: 18,\n height: 8,\n fill: 'none',\n stroke: GENE_INK,\n 'stroke-width': 1.5,\n })\n : el('rect', {\n x: at,\n y: e.swatch.kind === 'gap' ? 7 : 5,\n width: 18,\n height: e.swatch.kind === 'gap' ? GAP_PX : 8,\n rx: 2,\n fill: e.swatch.fill,\n })\n const text = el(\n 'text',\n {\n x: e.note ? at : at + 23,\n y: 13,\n 'font-style': e.note ? 'italic' : undefined,\n fill: e.note ? '#666' : undefined,\n },\n e.label,\n )\n at += (e.note ? 0 : 23) + e.label.length * LABEL_CHAR_PX + 16\n return swatch ? [swatch, text] : [text]\n })\n return {\n width: at,\n tree: el(\n 'g',\n {\n transform: `translate(${x} ${y})`,\n 'font-family': 'Helvetica, Arial, sans-serif',\n 'font-size': 11,\n },\n ...parts,\n ),\n }\n}\n\nexport interface WalkRowsFrame {\n scaleX: number\n scaleY: number\n translateX: number\n translateY: number\n width: number\n height: number\n // the rows' pitch and bar height in px, ROW_HEIGHT_PX and BAR_PX unless a\n // dense strip shrinks them\n rowPx?: number\n barPx?: number\n // false where rows are too thin to letter, which leaves readouts to hover\n readouts?: boolean\n}\n\n// Runs under a pixel wide merged into one, painted as the bp most of them\n// carry: a base-level cut splits a bar at every SNP, which draws as noise and\n// costs an element per run. Gaps stay apart.\nexport function coalesceRuns(runs: WalkRun[], pxPerBp: number) {\n const minBp = 1 / pxPerBp\n const out: WalkRun[] = []\n let bucket: WalkRun[] = []\n const flush = () => {\n if (bucket.length === 0) {\n return\n }\n if (bucket.length === 1) {\n out.push(bucket[0]!)\n } else {\n let on = 0\n let off = 0\n for (const r of bucket) {\n if (r.onReference) {\n on += r.bp\n } else {\n off += r.bp\n }\n }\n const onReference = on >= off\n const lead = bucket.find(r => r.onReference === onReference)!\n out.push({\n start: bucket[0]!.start,\n bp: on + off,\n onReference,\n referenceStart: onReference ? lead.referenceStart : undefined,\n ...(lead.reversed ? { reversed: true as const } : {}),\n })\n }\n bucket = []\n }\n let held = 0\n for (const run of runs) {\n if (run.gap || run.bp >= minBp) {\n flush()\n held = 0\n out.push(run)\n continue\n }\n bucket.push(run)\n held += run.bp\n if (held >= minBp) {\n flush()\n held = 0\n }\n }\n flush()\n return out\n}\n\nfunction geneTree(box: GeneBox, y: number, barPx: number) {\n return el(\n 'g',\n { class: 'row-gene', 'data-testid': 'graph-walk-gene' },\n el('title', {}, box.name),\n ...box.exons.map(e =>\n el('rect', {\n x: e.x,\n y: y - barPx / 2,\n width: e.w,\n height: barPx,\n fill: GENE_INK,\n opacity: 0.35,\n }),\n ),\n el('rect', {\n x: box.x,\n y: box.y,\n width: box.w,\n height: box.h,\n fill: 'none',\n stroke: GENE_INK,\n 'stroke-width': 1.5,\n }),\n box.label &&\n el(\n 'text',\n {\n x: box.label.x,\n y: box.label.y,\n 'font-family': 'sans-serif',\n 'font-size': box.label.size,\n 'font-weight': 600,\n 'text-anchor': 'middle',\n fill: GENE_INK,\n stroke: 'white',\n 'stroke-width': 2.5,\n 'paint-order': 'stroke',\n },\n box.name,\n ),\n )\n}\n\n// Walk rows over a pane: each row's runs, unit separators, its genes, the\n// allele a repeat genotype called and its readout. `idPrefix` keeps gradient\n// ids apart where several panes share a document.\nexport function walkRowsTree(\n bars: WalkRowsWithCalls,\n frame: WalkRowsFrame,\n o: {\n ramp?: { start: number; end: number }\n rowGenes?: Map<string, RowGene[]>\n idPrefix?: string\n } = {},\n) {\n const X = (bp: number) => bp * frame.scaleX + frame.translateX\n const rowPx = frame.rowPx ?? ROW_HEIGHT_PX\n const barPx = frame.barPx ?? BAR_PX\n const Y = (row: number) => row * rowPx * frame.scaleY + frame.translateY\n const { origin, unit, reference, rows } = bars\n const along = (offset: number) => X(origin + offset)\n const prefix = o.idPrefix ?? 'walkrow'\n const rowTree = (\n row: WalkRow & { call?: ReadoutCall },\n i: number,\n ): El | undefined => {\n const y = Y(i)\n if (y < -barPx || y > frame.height + barPx) {\n return undefined\n }\n const runs = coalesceRuns(row.runs, frame.scaleX).flatMap(run => {\n const paint = runPaint(run, o.ramp)\n const h = run.gap ? Math.min(GAP_PX, barPx) : barPx\n const id = `${prefix}-${i}-${run.start}`\n const rect = el('rect', {\n x: along(run.start),\n y: y - h / 2,\n width: Math.max(1, run.bp * frame.scaleX),\n height: h,\n fill: 'stops' in paint ? `url(#${id})` : paint.fill,\n })\n return 'stops' in paint\n ? [\n el(\n 'linearGradient',\n { id },\n ...paint.stops.map((color, k) =>\n el('stop', {\n offset: k / (paint.stops.length - 1),\n 'stop-color': color,\n }),\n ),\n ),\n rect,\n ]\n : [rect]\n })\n const ticks = unit\n ? unitTicks(row.bp, unit, frame.scaleX).map(k =>\n el('line', {\n x1: along(k),\n x2: along(k),\n y1: y - barPx / 2,\n y2: y + barPx / 2,\n stroke: 'white',\n 'stroke-width': 1,\n }),\n )\n : []\n const { call } = row\n const text = walkRowReadout(\n row,\n i === 0 ? undefined : reference,\n unit,\n call,\n )\n const at = readoutPlacement(text, along(row.bp), frame.width)\n const readouts = frame.readouts ?? true\n return el(\n 'g',\n {\n 'data-testid': i === 0 ? 'graph-walk-reference' : 'graph-walk-row',\n },\n ...runs,\n ...ticks,\n ...rowGeneBoxes(o.rowGenes?.get(row.name) ?? [], along, y, {\n rowPx,\n barPx,\n }).map(box => geneTree(box, y, barPx)),\n call &&\n el('rect', {\n 'data-testid': 'graph-walk-call',\n x: along(call.bp) - 1,\n y: y - barPx / 2 - 3,\n width: 2,\n height: barPx + 6,\n fill: call.spanningReads === 0 ? UNBACKED_TICK : CALL_TICK,\n }),\n readouts &&\n el(\n 'text',\n {\n x: at.x,\n y: y + 4,\n 'font-family': 'sans-serif',\n 'font-size': 11,\n fill: call?.agrees === false ? DISAGREES : '#333',\n stroke: at.halo ? 'white' : undefined,\n 'stroke-width': at.halo ? 3 : undefined,\n 'paint-order': 'stroke',\n 'text-anchor': at.anchor,\n },\n text,\n ),\n )\n }\n return el('g', {}, ...[reference, ...rows].map(rowTree))\n}\n", "import { ROW_HEIGHT_PX } from './rowSpacing'\nimport { alongRow, boxesGenes } from './walkRowDraw'\nimport { el } from '../el'\nimport { LABEL_CHAR_PX } from '../overlayLabels'\nimport { pathOrigin } from '../pathAnchoring'\n\nimport type { GeneGaps, WalkRowsFrame } from './walkRowDraw'\nimport type { WalkRow, WalkRows } from './walkRows'\nimport type { Graph, GraphNode } from '../types'\n\n// Walk rows as a strip under a node layout, linked to it: a node's visits as\n// ticks on each bar, and the node under a point on a bar. Rows are small\n// multiples of one mapping, x the walk's own bp and colour whether it runs on\n// the reference's path, so the strip shows every row and sizes them to fit,\n// down to a dense overview, rather than asking which to show.\n\nconst PAD_PX = 6\n// the most height the strip takes, its rows shrinking to fit\nexport const WALK_STRIP_CEILING_PX = 260\nconst LABEL_FONT_PX = 11\nconst MIN_ROW_PX = 3\n// rows thinner than this go unlabelled, their names and readouts on hover\nexport const LABELLED_ROW_PX = 12\nconst READOUT_ROOM = 1.3\nconst MARK_INK = '#111'\nconst TICK_PX = 3\n\nexport interface StripMark {\n row: string\n start: number\n bp: number\n}\n\n// Where each row's walk passes `node`, as offsets along its bar: once per\n// visit, so a walk through a collapsed repeat gets a tick per pass\nexport function stripMarks(graph: Graph, rows: WalkRow[], node: GraphNode) {\n const visits = graph.pathVisits?.get(node.name) ?? []\n const marks: StripMark[] = []\n for (const row of rows) {\n const axis = row.axis\n if (!axis) {\n continue\n }\n const path = pathOrigin(row.name).name\n for (const v of visits) {\n if (v.path !== path) {\n continue\n }\n const { start, end } = alongRow(axis, v.start, v.start + node.length)\n if (end > 0 && start < row.bp) {\n marks.push({ row: row.name, start, bp: end - start })\n }\n }\n }\n return marks\n}\n\ninterface Spans {\n ids: string[]\n starts: number[]\n ends: number[]\n}\n\nconst spanCache = new WeakMap<Graph, Map<string, Spans | undefined>>()\n\n// A walk's node steps at their contig positions, in contig order; undefined\n// where a piece states no start\nfunction spansOf(graph: Graph, name: string) {\n let byName = spanCache.get(graph)\n if (!byName) {\n byName = new Map()\n spanCache.set(graph, byName)\n }\n if (byName.has(name)) {\n return byName.get(name)\n }\n const lengthOf = new Map(graph.nodes.map(n => [n.id, n.length]))\n const pieces = (graph.paths ?? []).filter(p => p.name === name)\n let spans: Spans | undefined\n if (pieces.every(p => p.start !== undefined)) {\n spans = { ids: [], starts: [], ends: [] }\n for (const piece of [...pieces].sort((a, b) => a.start! - b.start!)) {\n let pos = piece.start!\n for (const id of piece.nodeIds) {\n const len = lengthOf.get(id) ?? 0\n spans.ids.push(id)\n spans.starts.push(pos)\n spans.ends.push(pos + len)\n pos += len\n }\n }\n }\n byName.set(name, spans)\n return spans\n}\n\n// The node under a point on a row's bar, `offset` bp from its left end: of the\n// nodes within half a pixel either side, the longest, so a pointer at 100 bp a\n// pixel still lands on something that draws\nexport function segmentAt(\n graph: Graph,\n row: WalkRow,\n offset: number,\n pxPerBp: number,\n) {\n const axis = row.axis\n const spans = axis && spansOf(graph, row.name)\n if (!axis || !spans || offset < 0 || offset > row.bp) {\n return undefined\n }\n const at = axis.reversed ? axis.start - offset : axis.start + offset\n const tol = 0.5 / pxPerBp\n const bar = axis.reversed\n ? { start: axis.start - row.bp, end: axis.start }\n : { start: axis.start, end: axis.start + row.bp }\n const from = Math.max(at - tol, bar.start)\n const to = Math.min(at + tol, bar.end)\n let lo = 0\n let hi = spans.starts.length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (spans.ends[mid]! <= from) {\n lo = mid + 1\n } else {\n hi = mid\n }\n }\n let best: string | undefined\n let bestLen = -1\n let bestHolds = false\n for (let i = lo; i < spans.starts.length && spans.starts[i]! < to; i++) {\n const len = spans.ends[i]! - spans.starts[i]!\n const holds = spans.starts[i]! <= at && at < spans.ends[i]!\n if (len > bestLen || (len === bestLen && holds && !bestHolds)) {\n bestLen = len\n best = spans.ids[i]\n bestHolds = holds\n }\n }\n return best\n}\n\nexport interface StripFrame extends WalkRowsFrame {\n rowPx: number\n barPx: number\n readouts: boolean\n // whether rows are tall enough to carry their labels, and to box genes\n labelled: boolean\n boxesGenes: boolean\n}\n\n// The strip's frame: every row, the reference's first, at the pitch that fits\n// `maxHeight`, from the walk-rows layout's own down to MIN_ROW_PX, and x fitted\n// so the longest bar and its readout fill the width past the label column,\n// which by default fits the longest label\nexport function walkStripFrame(\n bars: WalkRows,\n o: { width: number; maxHeight?: number; labelPx?: number },\n): StripFrame {\n const maxHeight = o.maxHeight ?? WALK_STRIP_CEILING_PX\n const labelPx =\n o.labelPx ??\n Math.max(...[bars.reference, ...bars.rows].map(r => r.label.length)) *\n LABEL_CHAR_PX +\n 16\n const count = bars.rows.length + 1\n const rowPx = Math.max(\n MIN_ROW_PX,\n Math.min(ROW_HEIGHT_PX, Math.floor((maxHeight - 2 * PAD_PX) / count)),\n )\n const labelled = rowPx >= LABELLED_ROW_PX\n const readouts = rowPx >= 14\n const barPx = Math.max(2, Math.round(rowPx * 0.6))\n const longest = Math.max(bars.reference.bp, ...bars.rows.map(r => r.bp), 1)\n const left = labelled ? labelPx : PAD_PX\n const usable = Math.max(1, o.width - left - PAD_PX)\n const scaleX = usable / (longest * (readouts ? READOUT_ROOM : 1))\n return {\n scaleX,\n scaleY: 1,\n translateX: left - bars.origin * scaleX,\n translateY: PAD_PX + rowPx / 2,\n width: o.width,\n height: count * rowPx + 2 * PAD_PX,\n rowPx,\n barPx,\n readouts,\n labelled,\n boxesGenes: boxesGenes(rowPx, barPx),\n }\n}\n\n// The row under a pane point of the strip, as its index among the reference\n// row and the rows below it, and its bar offset, where the point is on its bar\nexport function stripRowAt(\n bars: WalkRows,\n frame: StripFrame,\n x: number,\n y: number,\n) {\n const i = Math.round((y - frame.translateY) / frame.rowPx)\n const row = [bars.reference, ...bars.rows][i]\n const offset = (x - frame.translateX) / frame.scaleX - bars.origin\n return row && i >= 0 && offset >= 0 && offset <= row.bp\n ? { index: i, row, offset }\n : undefined\n}\n\n// A node's visits as ticks above and below each bar, leaving its colour seen\nexport function walkMarksTree(\n bars: WalkRows,\n frame: StripFrame,\n marks: StripMark[],\n) {\n const index = new Map(\n [bars.reference, ...bars.rows].map((row, i) => [row.name, i]),\n )\n const X = (offset: number) =>\n (bars.origin + offset) * frame.scaleX + frame.translateX\n return el(\n 'g',\n { 'data-testid': 'graph-walk-marks' },\n ...marks.flatMap(m => {\n const i = index.get(m.row)\n if (i === undefined) {\n return []\n }\n const y = i * frame.rowPx + frame.translateY\n const x = X(m.start)\n const w = Math.max(2, m.bp * frame.scaleX)\n const tick = (top: number) =>\n el('rect', { x, y: top, width: w, height: TICK_PX, fill: MARK_INK })\n return [\n tick(y - frame.barPx / 2 - TICK_PX - 1),\n tick(y + frame.barPx / 2 + 1),\n ]\n }),\n )\n}\n\n// Each row's name in the label column, where rows are tall enough to carry\n// one; `bold` names the walks lifted into the drawing\nexport function walkStripLabelsTree(\n bars: WalkRows,\n frame: StripFrame,\n bold: ReadonlySet<string> = new Set(),\n) {\n return el(\n 'g',\n { 'font-family': 'sans-serif', 'font-size': LABEL_FONT_PX, fill: '#333' },\n ...(frame.labelled ? [bars.reference, ...bars.rows] : []).map((row, i) =>\n el(\n 'text',\n {\n x: 6,\n y: i * frame.rowPx + frame.translateY + 4,\n 'font-weight': bold.has(row.name) ? 600 : undefined,\n },\n row.label,\n ),\n ),\n )\n}\n\n// What the key says of the strip's genes: rows too close to box them leave\n// them out\nexport function stripGeneGaps(frame: StripFrame, gaps: GeneGaps | undefined) {\n return gaps && !frame.boxesGenes ? { ...gaps, crowded: true } : gaps\n}\n", "import {\n abgrToCssRgba,\n normalizedRgbToCssRgba,\n} from '@jbrowse/core/util/colorBits'\nimport { syncCanvasSize } from '@jbrowse/render-core/canvas2dUtils'\nimport { Canvas2DRenderingBackendBase } from '@jbrowse/render-core/renderingBackendBase'\n\nimport { brightenAbgr } from './colorBits'\n\nimport type {\n Arrowhead,\n RenderBatch,\n Renderer,\n TransformUniform,\n} from './types'\n\n// Everything is drawn as a handful of paths, one per distinct (colour, weight),\n// rather than one path per thing. A drawing is mostly runs of one colour, and\n// Canvas2D's cost is per path verb and per fill or stroke, not per pixel: the\n// same 15k-node graph took 414 ms a frame as one fill per mesh triangle and 11\n// ms as batched strokes (agent-docs/GRAPH_SCALE_AND_LOD.md). Highlighted things\n// are drawn last, in their own paths, so they sit on top of what they brighten.\n//\n// Extends the shared Canvas2D base rather than standing alone, which is how\n// `setErrorHandler` arrives \u2014 `useRenderingBackend` requires it. The base's\n// no-op implementation is the correct one here: Canvas2D allocates no GPU\n// resources, so there is no OOM channel to forward.\nexport class Canvas2DRenderer\n extends Canvas2DRenderingBackendBase\n implements Renderer\n{\n private transform: TransformUniform | null = null\n private batch: RenderBatch | null = null\n private nodeHighlights: ReadonlyMap<string, number> = new Map()\n private highlightedEdge: number | null = null\n private highlightFactor = 1\n\n // render-core's, not a local `width * devicePixelRatio`: it clamps the\n // backing store at MAX_CANVAS_DIM_PX, reads the ratio through `getDpr()` so\n // this agrees with the transform the model builds, and writes the css size\n // independently of the backing size.\n resize(width: number, height: number) {\n syncCanvasSize(this.ctx.canvas, width, height)\n }\n\n uploadGeometry(batch: RenderBatch) {\n this.batch = batch\n // A rebuild renumbers the strokes, so the old edge no longer addresses the\n // same run; the model re-applies the current hover against the new batch.\n this.highlightedEdge = null\n }\n\n setNodeHighlights(factors: ReadonlyMap<string, number>) {\n this.nodeHighlights = factors\n }\n\n setEdgeHighlight(edgeIndex: number | null, factor: number) {\n this.highlightedEdge = edgeIndex\n this.highlightFactor = factor\n }\n\n updateTransform(transform: TransformUniform) {\n this.transform = transform\n }\n\n render(clearColor: [number, number, number, number]) {\n const t = this.transform\n if (!t) {\n return\n }\n const ctx = this.ctx\n const { width, height } = ctx.canvas\n\n // a clear colour short of opaque leaves what is under the canvas showing\n if (clearColor[3] < 1) {\n ctx.clearRect(0, 0, width, height)\n }\n ctx.fillStyle = normalizedRgbToCssRgba(\n [clearColor[0], clearColor[1], clearColor[2]],\n clearColor[3],\n )\n ctx.fillRect(0, 0, width, height)\n\n const batch = this.batch\n if (!batch) {\n return\n }\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n this.renderEdges(batch)\n this.renderNodes(batch)\n this.renderArrows(batch)\n }\n\n private renderEdges(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n const hl =\n this.highlightedEdge === null\n ? undefined\n : batch.edgeCurveRuns.get(this.highlightedEdge)\n for (const { color, items } of groupByPaint(batch.edgeCurves, (e, i) => ({\n color: inRun(hl, i)\n ? brightenAbgr(e.color, this.highlightFactor)\n : e.color,\n weight: e.thickness,\n last: inRun(hl, i),\n }))) {\n ctx.strokeStyle = abgrToCssRgba(color)\n // thickness is the half-width in css px, so the stroke is twice it at\n // the device ratio the transform already carries\n ctx.lineWidth = items[0]!.thickness * 2 * t.dpr\n ctx.beginPath()\n for (const e of items) {\n const first = e.curves[0]!\n ctx.moveTo(\n first.x0 * t.scaleX + t.translateX,\n first.y0 * t.scaleY + t.translateY,\n )\n for (const c of e.curves) {\n ctx.bezierCurveTo(\n c.cx0 * t.scaleX + t.translateX,\n c.cy0 * t.scaleY + t.translateY,\n c.cx1 * t.scaleX + t.translateX,\n c.cy1 * t.scaleY + t.translateY,\n c.x1 * t.scaleX + t.translateX,\n c.y1 * t.scaleY + t.translateY,\n )\n }\n }\n ctx.stroke()\n }\n }\n\n private renderNodes(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n // stroke index -> brighten factor, for the nodes the model lifted\n const lifted = new Map<number, number>()\n for (const [nodeId, factor] of this.nodeHighlights) {\n const run = batch.nodeStrokeRuns.get(nodeId)\n if (run) {\n for (let i = run.start; i < run.start + run.count; i++) {\n lifted.set(i, factor)\n }\n }\n }\n for (const { color, items } of groupByPaint(batch.nodeStrokes, (s, i) => {\n const factor = lifted.get(i)\n return {\n color: factor === undefined ? s.color : brightenAbgr(s.color, factor),\n weight: s.thickness,\n last: factor !== undefined,\n }\n })) {\n ctx.strokeStyle = abgrToCssRgba(color)\n ctx.lineWidth = items[0]!.thickness * 2 * t.dpr\n ctx.beginPath()\n for (const s of items) {\n const p0 = s.points[0]!\n ctx.moveTo(\n p0.x * t.scaleX + t.translateX,\n p0.y * t.scaleY + t.translateY,\n )\n for (let i = 1, l = s.points.length; i < l; i++) {\n const p = s.points[i]!\n ctx.lineTo(\n p.x * t.scaleX + t.translateX,\n p.y * t.scaleY + t.translateY,\n )\n }\n }\n ctx.stroke()\n }\n }\n\n private renderArrows(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n const hl =\n this.highlightedEdge === null\n ? undefined\n : batch.arrowRuns.get(this.highlightedEdge)\n for (const { color, items } of groupByPaint(batch.arrows, (a, i) => ({\n color: inRun(hl, i)\n ? brightenAbgr(a.color, this.highlightFactor)\n : a.color,\n weight: 0,\n last: inRun(hl, i),\n }))) {\n ctx.fillStyle = abgrToCssRgba(color)\n ctx.beginPath()\n for (const a of items) {\n const [tip, left, notch, right] = arrowheadOutline(a, t)\n ctx.moveTo(tip.x, tip.y)\n ctx.lineTo(left.x, left.y)\n ctx.lineTo(notch.x, notch.y)\n ctx.lineTo(right.x, right.y)\n ctx.closePath()\n }\n ctx.fill()\n }\n }\n\n override dispose() {\n super.dispose()\n this.batch = null\n this.nodeHighlights = new Map()\n this.highlightedEdge = null\n }\n}\n\n// How far the back of the head is cut in toward the tip, as a fraction of its\n// length. The notch leaves the edge showing through the back of the head, so\n// the head reads as the end of the edge rather than a triangle laid over it.\nconst ARROW_NOTCH = 0.25\n\n// Tip, left barb, notch and right barb in backing-store px.\nexport function arrowheadOutline(a: Arrowhead, t: TransformUniform) {\n const ux = Math.cos(a.angle)\n const uy = Math.sin(a.angle)\n const tipX = a.x * t.scaleX + t.translateX\n const tipY = a.y * t.scaleY + t.translateY\n const length = a.length * t.dpr\n const halfWidth = a.halfWidth * t.dpr\n const backX = tipX - ux * length\n const backY = tipY - uy * length\n const notchBack = length * (1 - ARROW_NOTCH)\n return [\n { x: tipX, y: tipY },\n { x: backX - uy * halfWidth, y: backY + ux * halfWidth },\n { x: tipX - ux * notchBack, y: tipY - uy * notchBack },\n { x: backX + uy * halfWidth, y: backY - ux * halfWidth },\n ] as const\n}\n\nfunction inRun(run: { start: number; count: number } | undefined, i: number) {\n return run !== undefined && i >= run.start && i < run.start + run.count\n}\n\ninterface Paint {\n color: number\n weight: number\n // drawn after everything else, so a highlight lands on top of its neighbours\n last: boolean\n}\n\n// Items bucketed by what they are painted with, in first-seen order, with the\n// `last` ones after the rest.\nfunction groupByPaint<T>(\n items: T[],\n paintOf: (item: T, index: number) => Paint,\n) {\n const groups = new Map<string, { color: number; items: T[] }>()\n const deferred = new Map<string, { color: number; items: T[] }>()\n for (let i = 0, l = items.length; i < l; i++) {\n const item = items[i]!\n const { color, weight, last } = paintOf(item, i)\n const into = last ? deferred : groups\n const key = `${color}:${weight}`\n const group = into.get(key)\n if (group) {\n group.items.push(item)\n } else {\n into.set(key, { color, items: [item] })\n }\n }\n return [...groups.values(), ...deferred.values()]\n}\n", "import { packAbgr } from '@jbrowse/core/util/colorBits'\n\nimport {\n REFERENCE_RAMP_ALT_COLOR,\n REFERENCE_RAMP_MAX_HUE,\n hslToRgb,\n} from './renderer/GeometryBuilder'\n\n// How a lifted walk colours its lane, stated the way a grammar of graphics\n// states an encoding: a field, the quantity the walk has at each node it\n// visits, mapped through a scheme, the scale from that quantity to a colour.\n// Colour does one job at a time by default: a walk lifted alone shades light\n// to dark along itself, which follows it round a loop, and walks lifted\n// together each take one flat colour, which says which lane is which. The\n// reference takes grey, so no hue means anything but a haplotype.\n//\n// A session states it per walk, and leaves out whatever takes the default:\n//\n// \"walkLayers\": [\n// { \"walk\": \"GRCh38#0#chr6\" },\n// { \"walk\": \"HG00133#1#CM090050.1\", \"color\": { \"scheme\": \"purple\" } }\n// ]\n\nexport const WALK_FIELDS = [\n { value: 'progress', label: 'Progress along the walk' },\n { value: 'walk', label: 'One colour for the walk' },\n { value: 'reference', label: 'Reference position' },\n] as const\n\nexport type WalkField = (typeof WALK_FIELDS)[number]['value']\n\n// Each family runs from a light colour where the walk starts to a dark one of\n// another hue where it ends, so a walk reads in hue as well as lightness, and\n// no two families share a stretch of the colour wheel: one walk's gradient\n// never passes through another's colours. The reference takes grey to black, so\n// that no hue on a lane means anything but a haplotype. The rainbow is the\n// reference-position ramp, so it goes with that field only.\n//\n// A dichromat sees one axis, blue to yellow, so cyan to navy and yellow to red\n// stay apart for every reader and come first; pink and lime fold into them.\nexport const WALK_SCHEMES = [\n { value: 'grey', label: 'Grey to black', ends: ['#a3a3a3', '#151515'] },\n { value: 'blue', label: 'Cyan to navy', ends: ['#6ee0f0', '#0b2c7a'] },\n { value: 'red', label: 'Yellow to red', ends: ['#ffd24a', '#b3121b'] },\n { value: 'purple', label: 'Pink to plum', ends: ['#f5a3d8', '#5e1a6e'] },\n { value: 'green', label: 'Lime to forest', ends: ['#b6e36c', '#0b5d3b'] },\n { value: 'rainbow', label: 'Rainbow' },\n] as const\n\nexport type WalkScheme = (typeof WALK_SCHEMES)[number]['value']\n\nexport interface WalkEncoding {\n field: WalkField\n scheme: WalkScheme\n}\n\nexport interface WalkLayer {\n walk: string\n color?: Partial<WalkEncoding>\n}\n\n// the families lifted haplotypes take by default, in the order they were picked\nconst FAMILIES: WalkScheme[] = ['blue', 'red', 'purple', 'green']\n\nconst isField = (v: unknown): v is WalkField =>\n WALK_FIELDS.some(f => f.value === v)\nconst isScheme = (v: unknown): v is WalkScheme =>\n WALK_SCHEMES.some(s => s.value === v)\n\n// A stated field or scheme this build does not have reads as unset\nexport function resolveEncoding(\n layer: WalkLayer,\n reference: boolean,\n picked: number,\n alone = true,\n): WalkEncoding {\n const { field: askedField, scheme: askedScheme } = layer.color ?? {}\n const field = isField(askedField) ? askedField : alone ? 'progress' : 'walk'\n const family = reference ? 'grey' : FAMILIES[picked % FAMILIES.length]!\n const scheme = isScheme(askedScheme)\n ? askedScheme === 'rainbow' && field !== 'reference'\n ? family\n : askedScheme\n : family\n return { field, scheme }\n}\n\n// Where a flat lane sits on its family: halfway between its ends\nconst BASE_T = 0.5\n\nconst toLinear = (c: number) =>\n c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4\nconst toGamma = (c: number) =>\n c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055\n\n// sRGB hex to OKLab, where a straight line between two colours changes\n// lightness evenly, so a family's steps read alike from end to end\nfunction oklab(hex: string) {\n const [r, g, b] = [1, 3, 5].map(i =>\n toLinear(Number.parseInt(hex.slice(i, i + 2), 16) / 255),\n ) as [number, number, number]\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ]\n}\n\nfunction oklabToRgb([L, a, b]: number[]) {\n const l = (L! + 0.3963377774 * a! + 0.2158037573 * b!) ** 3\n const m = (L! - 0.1055613458 * a! - 0.0638541728 * b!) ** 3\n const s = (L! - 0.0894841775 * a! - 1.291485548 * b!) ** 3\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ].map(c => toGamma(Math.max(0, Math.min(1, c))) * 255)\n}\n\n// A scheme at t in [0, 1] as rgb 0-255\nexport function schemeRgb(scheme: WalkScheme, t: number) {\n const x = Math.max(0, Math.min(1, t))\n const family = WALK_SCHEMES.find(s => s.value === scheme)\n if (!family || !('ends' in family)) {\n return hslToRgb(x * REFERENCE_RAMP_MAX_HUE, 0.7, 0.5).map(c => c * 255)\n }\n const from = oklab(family.ends[0])\n const to = oklab(family.ends[1])\n return oklabToRgb(from.map((v, i) => v + (to[i]! - v) * x))\n}\n\nexport function schemeCss(scheme: WalkScheme, t: number) {\n const [r, g, b] = schemeRgb(scheme, t).map(Math.round)\n return `rgb(${r}, ${g}, ${b})`\n}\n\nexport function schemeColor(scheme: WalkScheme, t: number) {\n const [r, g, b] = schemeRgb(scheme, t).map(Math.round)\n return packAbgr(r!, g!, b!, 255)\n}\n\n// A field's value at a node through the walk's scheme; `t` undefined is a node\n// the field has nothing to say about, off the reference for `reference`\nexport function encodedColor({ field, scheme }: WalkEncoding, t?: number) {\n return field === 'walk'\n ? schemeColor(scheme, BASE_T)\n : t === undefined\n ? NO_VALUE_COLOR\n : schemeColor(scheme, t)\n}\n\nexport const NO_VALUE_COLOR = REFERENCE_RAMP_ALT_COLOR\n\n// A key's colours: the walk's one colour, or the scheme across the field\nexport function encodingStops({ field, scheme }: WalkEncoding) {\n return field === 'walk'\n ? [{ offset: 0, color: schemeCss(scheme, BASE_T) }]\n : Array.from({ length: 7 }, (_, i) => ({\n offset: i / 6,\n color: schemeCss(scheme, i / 6),\n }))\n}\n\n// A swatch for the legend: the walk's colour, or the scheme across the field\nexport function encodingSwatchCss(encoding: WalkEncoding) {\n const stops = encodingStops(encoding)\n return stops.length === 1\n ? stops[0]!.color\n : `linear-gradient(to right, ${stops.map(s => `${s.color} ${s.offset * 100}%`).join(', ')})`\n}\n", "import { formatBp, groupDigits } from './graphLabels'\n\nimport type { LiftedWalk } from './walkHighlight'\n\n// `contig:start-end (length)`\nexport function rangeText(\n contig: string | undefined,\n start: number,\n end: number,\n) {\n const s = Math.round(start)\n const e = Math.round(end)\n return `${contig ? `${contig}:` : ''}${groupDigits(s)}-${groupDigits(e)} (${formatBp(e - s)})`\n}\n\n// A lifted walk's key in words, for a legend row or a facet panel's title: its\n// length against the reference and the bp it runs reversed, and for a lane that\n// shades, the stretch its scale runs over as `contig:start-end (length)`, to\n// write under a short bar of that scale. A flat lane's stretch is `hover`.\nexport function walkKey(\n walk: LiftedWalk,\n reference?: { name?: string; start: number; end: number },\n) {\n const { field } = walk.encoding\n const own = walk.range\n ? rangeText(walk.range.contig, walk.range.start, walk.range.end)\n : undefined\n const scale =\n field === 'progress'\n ? own\n : field === 'reference' && reference\n ? rangeText(reference.name, reference.start, reference.end)\n : undefined\n // what the walk spans on its contig, which its pieces may not all show\n const spanned = walk.range ? walk.range.end - walk.range.start : walk.bp\n const outside = spanned - walk.bp\n return {\n delta:\n walk.referenceBp === undefined || spanned === walk.referenceBp\n ? ''\n : ` ${spanned > walk.referenceBp ? '+' : '\u2212'}${formatBp(Math.abs(spanned - walk.referenceBp))}`,\n outside: outside > 0 ? `, ${formatBp(outside)} outside the cut` : '',\n reversed:\n walk.reversedBp > 0 ? `, ${formatBp(walk.reversedBp)} reversed` : '',\n shades: field !== 'walk',\n scale,\n hover: scale === own ? undefined : own,\n }\n}\n\n// Where a node sits on a walk's own contig, from how far along the walk it\n// is: the stretch the walk's records give it at its first visit\nexport function walkPosition(walk: LiftedWalk, nodeId: string, length: number) {\n const progress = walk.progress.get(nodeId)\n const range = walk.range\n if (progress === undefined || !range) {\n return undefined\n }\n const start = Math.round(\n range.start + progress * (range.end - range.start) - length / 2,\n )\n return `${range.contig}:${groupDigits(start)}-${groupDigits(start + length)}`\n}\n", "import { abgrToCssRgba } from '@jbrowse/core/util/colorBits'\n\nimport { isBackbone } from './anchoredNodes'\nimport {\n LIFT_BACKDROP_CSS,\n computeColorSchemeRange,\n getNodeColor,\n} from './renderer/GeometryBuilder'\n\nimport type { ResolvedColorScheme } from './colorSchemes'\nimport type { ReferenceRamp } from './renderer/GeometryBuilder'\nimport type { Graph, NodeSegment } from './types'\n\n// A force or ordered drawing inside a linear view has no bp axis, so a strip\n// along the top of the track draws each reference segment at its bp there, in\n// the colour its node has in the graph below: the reference-position ramp's\n// hue, or while walks are lifted a row per walk in its lane's colours, pale\n// where that walk skips the segment. The strip leaves the linear view's\n// gridlines showing, so each block reads as a feature at its bp, and while the\n// blocks on screen are wide enough to read one by one, touching ones alternate\n// between two tiers, as a feature track stacks features that would touch. A\n// base-level cut splits the backbone at every SNP, and tiers there would be a\n// barcode that reads as data, so it draws as one band. The lit node gets a leader from its span on the strip\n// to where the graph drew it, as the variant matrix ties a column to its\n// variant.\n\nexport const REFERENCE_STRIP_PX = 12\n// the strip, and the gap under it the fit leaves the drawing\nexport const REFERENCE_STRIP_ZONE_PX = REFERENCE_STRIP_PX + 8\n// a triangle at a strip end, in a cap cleared of the strip: reference the\n// graph draws runs past that edge\nconst OVERHANG_PX = 8\nconst OVERHANG_CAP_PX = OVERHANG_PX + 3\n// the average width, in css px, the blocks on screen need to be staggered\nconst MIN_STAGGER_PX = 12\n\nexport interface StripBlock {\n node: string\n bp0: number\n bp1: number\n // top to bottom: a row per lifted walk, else the node's own colour\n colors: string[]\n // pale on some row\n faded: boolean\n}\n\nexport function referenceStripBlocks(\n graph: Graph,\n {\n colorScheme,\n referenceRamp,\n walks,\n }: {\n colorScheme: ResolvedColorScheme\n referenceRamp?: ReferenceRamp\n // the lifted walks' lane colours at the nodes each visits\n walks?: readonly { colors: ReadonlyMap<string, number> }[]\n },\n) {\n const range = { ...computeColorSchemeRange(graph), referenceRamp }\n const out: StripBlock[] = []\n graph.nodes.forEach((node, index) => {\n if (isBackbone(node)) {\n const colors = walks\n ? walks.map(w => {\n const lane = w.colors.get(node.id)\n return lane === undefined ? LIFT_BACKDROP_CSS : abgrToCssRgba(lane)\n })\n : [abgrToCssRgba(getNodeColor(node, index, colorScheme, range))]\n out.push({\n node: node.id,\n bp0: node.stable.start,\n bp1: node.stable.start + node.length,\n colors,\n faded: colors.includes(LIFT_BACKDROP_CSS),\n })\n }\n })\n return out.sort((a, b) => a.bp0 - b.bp0)\n}\n\n// bp to screen x, as the linear view places it; a reversed block's is negative\nexport interface BpFrame {\n scale: number\n translateX: number\n}\n\nfunction span(frame: BpFrame, bp0: number, bp1: number): [number, number] {\n const a = bp0 * frame.scale + frame.translateX\n const b = bp1 * frame.scale + frame.translateX\n return a < b ? [a, b] : [b, a]\n}\n\n// A SNP's block is a sliver, so the pointer gets a few px either side of one\nconst HIT_SLOP_PX = 2\n\n// How much backbone the graph draws past each edge of the window, in bp\nexport function stripOverhang(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n width: number,\n) {\n const a = -frame.translateX / frame.scale\n const b = (width - frame.translateX) / frame.scale\n const lo = Math.min(a, b)\n const hi = Math.max(a, b)\n let below = 0\n let above = 0\n for (const block of blocks) {\n below += Math.max(0, Math.min(block.bp1, lo) - block.bp0)\n above += Math.max(0, block.bp1 - Math.max(block.bp0, hi))\n }\n // a reversed window puts low bp on the right\n return frame.scale >= 0\n ? { left: Math.round(below), right: Math.round(above) }\n : { left: Math.round(above), right: Math.round(below) }\n}\n\nexport function stripBlockAt(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n sx: number,\n sy: number,\n) {\n if (sy < 0 || sy > REFERENCE_STRIP_PX + HIT_SLOP_PX) {\n return undefined\n }\n let hit: string | undefined\n let best = HIT_SLOP_PX\n for (const b of blocks) {\n const [x0, x1] = span(frame, b.bp0, b.bp1)\n const dist = Math.max(0, x0 - sx, sx - x1)\n if (dist <= best) {\n best = dist\n hit = b.node\n }\n }\n return hit\n}\n\n// Where the graph drew a node, in screen px: the middle of its polyline\nexport function nodeAnchor(\n segments: readonly NodeSegment[] | undefined,\n toScreen: (p: NodeSegment) => { x: number; y: number },\n) {\n const p = segments?.[Math.floor(segments.length / 2)]\n return p ? toScreen(p) : undefined\n}\n\nexport interface StripLit {\n start: number\n end: number\n anchor?: { x: number; y: number }\n}\n\n// Each block in whole device pixels, left to right, none sharing a pixel.\n// Fractional edges antialias: two opaque neighbours leave a pale seam between\n// them and two faded ones a dark seam where they overlap, stripes that read\n// as data. A block narrower than a pixel gets its pixel unless a block to its\n// left already has it.\nexport interface StripPixel {\n colors: string[]\n x0: number\n x1: number\n // the tier a staggered strip draws the block in\n tier?: number\n}\n\nexport function stripPixels(\n blocks: readonly StripBlock[],\n frame: BpFrame,\n width: number,\n dpr: number,\n): StripPixel[] {\n const placed = blocks\n .map(b => {\n const [x0, x1] = span(frame, b.bp0, b.bp1)\n const px0 = Math.round(x0 * dpr)\n return {\n colors: b.colors,\n x0,\n px0,\n px1: Math.max(Math.round(x1 * dpr), px0 + 1),\n }\n })\n .filter(b => b.px1 > 0 && b.px0 < width * dpr)\n .sort((a, b) => a.x0 - b.x0)\n const out: StripPixel[] = []\n let claimed = -Infinity\n // where each tier's last block ends, in device px\n const tierEnds = [-Infinity, -Infinity]\n for (const { colors, px0, px1 } of placed) {\n const start = Math.max(px0, claimed)\n if (px1 > start) {\n const tier = tierEnds[0]! < start ? 0 : 1\n tierEnds[tier] = px1\n out.push({ colors, x0: start / dpr, x1: px1 / dpr, tier })\n claimed = px1\n }\n }\n const covered = out.length > 0 ? out.at(-1)!.x1 - out[0]!.x0 : 0\n return covered >= out.length * MIN_STAGGER_PX\n ? out\n : out.map(({ tier: _, ...b }) => b)\n}\n\n// A triangle the strip's height with its tip at the strip's edge, pointing\n// off it, in a cap cleared of the strip; `inward` is +1 from the left edge and\n// -1 from the right\nfunction drawOverhang(\n ctx: CanvasRenderingContext2D,\n edgeX: number,\n inward: number,\n ink: string,\n) {\n const capX = inward > 0 ? edgeX : edgeX - OVERHANG_CAP_PX\n ctx.clearRect(capX, 0, OVERHANG_CAP_PX, REFERENCE_STRIP_PX)\n const tipX = edgeX + inward\n const baseX = tipX + inward * OVERHANG_PX\n ctx.beginPath()\n ctx.moveTo(tipX, REFERENCE_STRIP_PX / 2)\n ctx.lineTo(baseX, 0)\n ctx.lineTo(baseX, REFERENCE_STRIP_PX)\n ctx.closePath()\n ctx.fillStyle = ink\n ctx.fill()\n}\n\n// Rows split the strip's height at whole device pixels, as blocks split its\n// width\nfunction rowEdge(row: number, rows: number, dpr: number) {\n return Math.round((row * REFERENCE_STRIP_PX * dpr) / rows) / dpr\n}\n\nexport function drawReferenceStrip(\n ctx: CanvasRenderingContext2D,\n blocks: readonly StripBlock[],\n frame: BpFrame,\n {\n width,\n lit,\n darkMode,\n dpr = 1,\n }: {\n width: number\n lit?: StripLit\n darkMode?: boolean\n dpr?: number\n },\n) {\n const ink = darkMode ? '#ffffff' : '#18181c'\n ctx.clearRect(0, 0, width, REFERENCE_STRIP_ZONE_PX)\n for (const b of stripPixels(blocks, frame, width, dpr)) {\n // a row per lifted walk, else the block's tier, else the whole strip\n const { tier } = b\n const staggered = tier !== undefined && b.colors.length === 1\n const rows = staggered ? 2 : b.colors.length\n b.colors.forEach((color, i) => {\n const row = staggered ? tier : i\n const y0 = rowEdge(row, rows, dpr)\n ctx.fillStyle = color\n ctx.fillRect(b.x0, y0, b.x1 - b.x0, rowEdge(row + 1, rows, dpr) - y0)\n })\n }\n const overhang = stripOverhang(blocks, frame, width)\n if (overhang.left > 0) {\n drawOverhang(ctx, 0, 1, ink)\n }\n if (overhang.right > 0) {\n drawOverhang(ctx, width, -1, ink)\n }\n if (lit) {\n const [x0, x1] = span(frame, lit.start, lit.end)\n const w = Math.max(x1 - x0, 3)\n const left = (x0 + x1) / 2 - w / 2\n ctx.strokeStyle = ink\n ctx.lineWidth = 1.5\n ctx.strokeRect(left, 0.75, w, REFERENCE_STRIP_PX - 1.5)\n if (lit.anchor) {\n ctx.beginPath()\n ctx.moveTo(left + w / 2, REFERENCE_STRIP_PX)\n ctx.lineTo(lit.anchor.x, lit.anchor.y)\n ctx.lineWidth = 1\n ctx.stroke()\n }\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { formatBp } from '../graphLabels'\n\nimport type { TubeMapFrame } from './draw'\nimport type { Graph } from '../types'\nimport type { TubeMapLayout } from '@gmod/tubemap-core'\n\n// The reference's boxes carry bp, so a reference coordinate maps into tube x\n// through the box that holds it. On the own axis a box is log-scaled, so bp\n// per tube px varies box to box; on the reference axis frame.ts warps the\n// result back onto the linear view's bp.\n\nexport interface Box {\n bp0: number\n bp1: number\n x0: number\n x1: number\n}\n\n// keyed by the graph's refName, `GRCh38#0#chr6`\nexport function referenceBoxes(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const byRefName = new Map<string, Box[]>()\n // sparse: an unreached node has no entry\n layout.nodes.forEach(node => {\n const graphNode = nodeById.get(node.name)\n if (node.order >= 0 && graphNode && isBackbone(graphNode)) {\n const { refName, start } = graphNode.stable\n const boxes =\n byRefName.get(refName) ?? byRefName.set(refName, []).get(refName)!\n boxes.push({\n bp0: start,\n bp1: start + node.sequenceLength,\n x0: node.x,\n x1: node.x + node.pixelWidth,\n })\n }\n })\n for (const boxes of byRefName.values()) {\n boxes.sort((a, b) => a.bp0 - b.bp0)\n }\n return byRefName\n}\n\nexport type ReferenceBoxes = ReturnType<typeof referenceBoxes>\n\n// A bp the cut has no box for (a gap in the backbone) takes the next box's\n// left edge. A bp on a boundary between boxes belongs to the box after it, or\n// to the one before for the end of a span, so a span stays off the curves\n// between.\nexport function tubeX(boxes: readonly Box[], bp: number, isEnd = false) {\n let lo = 0\n let hi = boxes.length - 1\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n const bp1 = boxes[mid]!.bp1\n if (isEnd ? bp1 < bp : bp1 <= bp) {\n lo = mid + 1\n } else {\n hi = mid\n }\n }\n const box = boxes[lo]!\n if (bp <= box.bp0) {\n return box.x0\n }\n const span = box.bp1 - box.bp0\n return span > 0\n ? box.x0 + ((Math.min(bp, box.bp1) - box.bp0) / span) * (box.x1 - box.x0)\n : box.x0\n}\n\nexport function tubeSpan(boxes: readonly Box[], start: number, end: number) {\n const a = tubeX(boxes, start)\n const b = tubeX(boxes, end, true)\n return { x0: Math.min(a, b), x1: Math.max(a, b) }\n}\n\n// The refName most of the cut's reference lies on, for the ruler\nexport function rulerBoxes(byRefName: ReferenceBoxes) {\n let best: Box[] | undefined\n for (const boxes of byRefName.values()) {\n if (!best || boxes.length > best.length) {\n best = boxes\n }\n }\n return best\n}\n\nconst TICK_PX = 5\nconst LABEL_GAP_PX = 10\nconst TARGET_TICK_PX = 110\n// narrower than this a box draws as one tick rather than a bracket\nconst MIN_BRACKET_PX = 3\n\nexport function rulerInk(darkMode?: boolean) {\n return darkMode ? '#b0b0b8' : '#55555c'\n}\n\ninterface Label {\n text: string\n at: number\n width: number\n}\n\nfunction niceStep(raw: number) {\n const pow = 10 ** Math.floor(Math.log10(raw))\n const m = raw / pow\n return (m <= 1 ? 1 : m <= 2 ? 2 : m <= 5 ? 5 : 10) * pow\n}\n\n// In order, dropping any that would overlap one already placed\nfunction placeLabels(candidates: readonly Label[]) {\n const placed: Label[] = []\n for (const a of candidates) {\n if (\n placed.every(\n b => Math.abs(a.at - b.at) >= (a.width + b.width) / 2 + LABEL_GAP_PX,\n )\n ) {\n placed.push(a)\n }\n }\n return placed.map(({ text, at }) => ({ text, at }))\n}\n\nexport interface RulerMarks {\n // each box's span; the gaps between them stay blank, since the lane changes\n // drawn there cover no reference\n spans: { x0: number; x1: number }[]\n ticks: number[]\n labels: { text: string; at: number }[]\n}\n\n// On the reference axis the boxes sit on their bp, so one scale runs across\n// the ruler and its ticks fall on round positions.\nfunction scaleMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n): RulerMarks | undefined {\n const first = boxes[0]\n const last = boxes.at(-1)\n if (!first || !last) {\n return undefined\n }\n const from = x(first.x0)\n const to = x(last.x1)\n const bpSpan = last.bp1 - first.bp0\n if (to <= from || bpSpan <= 0) {\n return undefined\n }\n const step = niceStep((bpSpan / (to - from)) * TARGET_TICK_PX)\n const ticks: number[] = []\n const positions: Label[] = []\n for (\n let bp = Math.ceil(first.bp0 / step) * step;\n bp <= last.bp1;\n bp += step\n ) {\n const sx = Math.round(x(tubeX(boxes, bp))) + 0.5\n if (sx >= 0 && sx <= width) {\n ticks.push(sx)\n const text = bp.toLocaleString('en-US')\n positions.push({ text, at: sx, width: measure(text) })\n }\n }\n return {\n spans: [{ x0: from, x1: to }],\n ticks,\n labels: placeLabels(positions),\n }\n}\n\n// On the own axis every box is as wide as the log of its length (tubemap-core's\n// compressed widths: 2 bp is 8 px, 100 bp 56, 1 kb 84, 22 kb 121), so no box is\n// to scale and no one scale runs across them. The ruler treats every box alike:\n// a bracket under it, a tick at each end, its length where that fits inside it,\n// and positions at box boundaries. Labels are placed in that order after the\n// two ends of what is on screen.\nfunction boxMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n): RulerMarks | undefined {\n const shown = boxes\n .map(box => ({ box, s0: x(box.x0), s1: x(box.x1) }))\n .filter(({ s0, s1 }) => s1 >= 0 && s0 <= width)\n const first = shown[0]\n const last = shown.at(-1)\n if (!first || !last) {\n return undefined\n }\n const label = (text: string, at: number) => ({\n text,\n at,\n width: measure(text),\n })\n const position = (bp: number, at: number) =>\n label(bp.toLocaleString('en-US'), at)\n const spans: RulerMarks['spans'] = []\n const ticks: number[] = []\n const lengths: Label[] = []\n const boundaries: Label[] = []\n for (const { box, s0, s1 } of shown) {\n spans.push({ x0: s0, x1: s1 })\n ticks.push(Math.round(s0) + 0.5)\n if (s1 - s0 >= MIN_BRACKET_PX) {\n ticks.push(Math.round(s1) + 0.5)\n }\n const length = label(formatBp(box.bp1 - box.bp0), (s0 + s1) / 2)\n if (length.width <= s1 - s0) {\n lengths.push(length)\n }\n if (box !== first.box) {\n boundaries.push(position(box.bp0, s0))\n }\n }\n const ends = [\n position(first.box.bp0, first.s0),\n position(last.box.bp1, last.s1),\n ]\n return {\n spans,\n ticks,\n labels: placeLabels([...ends, ...lengths, ...boundaries]),\n }\n}\n\nexport function rulerMarks(\n boxes: readonly Box[],\n x: (tx: number) => number,\n width: number,\n measure: (text: string) => number,\n referenceAxis = false,\n) {\n return (referenceAxis ? scaleMarks : boxMarks)(boxes, x, width, measure)\n}\n\n// Reference bp along the tubes: round positions on the reference axis, a\n// bracket per box on the own axis\nexport function drawTubeMapRuler(\n ctx: CanvasRenderingContext2D,\n boxes: readonly Box[],\n frame: TubeMapFrame,\n top: number,\n referenceAxis = false,\n) {\n const { x, width, darkMode } = frame\n ctx.font = '10px sans-serif'\n const marks = rulerMarks(\n boxes,\n x,\n width,\n text => ctx.measureText(text).width,\n referenceAxis,\n )\n if (!marks) {\n return\n }\n const ink = rulerInk(darkMode)\n const y = top + 0.5\n ctx.strokeStyle = ink\n ctx.fillStyle = ink\n ctx.lineWidth = 1\n ctx.textAlign = 'center'\n ctx.textBaseline = 'top'\n ctx.beginPath()\n for (const { x0, x1 } of marks.spans) {\n ctx.moveTo(Math.max(0, x0), y)\n ctx.lineTo(Math.min(width, x1), y)\n }\n for (const sx of marks.ticks) {\n ctx.moveTo(sx, top)\n ctx.lineTo(sx, top + TICK_PX)\n }\n ctx.stroke()\n for (const { text, at } of marks.labels) {\n ctx.fillText(text, at, top + TICK_PX + 2)\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Graph } from '../types'\nimport type { TubeMapLayout } from '@gmod/tubemap-core'\n\n// On its own axis a tube map keeps none of the linear view's bp: a box is as\n// wide as log2 of its length, and the lane changes between boxes take px that\n// cover no reference at all. A band from each reference box up to the bp it\n// covers on the linear view's ruler ties the two back together, the way the\n// LD display ties its matrix columns to their variants.\n\nexport interface ReferenceNode {\n node: string\n bp0: number\n bp1: number\n x0: number\n x1: number\n}\n\n// A reference node's band, in screen px: its bp span along the top of the\n// zone, its box's span along the bottom\nexport interface Connector {\n node: string\n top0: number\n top1: number\n bottom0: number\n bottom1: number\n}\n\nexport function referenceNodes(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const out: ReferenceNode[] = []\n // sparse: an unreached node has no entry\n layout.nodes.forEach(node => {\n const graphNode = nodeById.get(node.name)\n if (node.order >= 0 && graphNode && isBackbone(graphNode)) {\n out.push({\n node: node.name,\n bp0: graphNode.stable.start,\n bp1: graphNode.stable.start + node.sequenceLength,\n x0: node.x,\n x1: node.x + node.pixelWidth,\n })\n }\n })\n return out\n}\n\n// The bands whose box is on screen. A band's top may still run off the side,\n// which is the linear view saying that box is outside its window.\nexport function tubeMapConnectors(\n nodes: readonly ReferenceNode[],\n bpToScreen: (bp: number) => number,\n tubeToScreen: (tx: number) => number,\n width: number,\n): Connector[] {\n const out: Connector[] = []\n for (const n of nodes) {\n const bottom0 = tubeToScreen(n.x0)\n const bottom1 = tubeToScreen(n.x1)\n if (bottom1 >= 0 && bottom0 <= width) {\n out.push({\n node: n.node,\n top0: bpToScreen(n.bp0),\n top1: bpToScreen(n.bp1),\n bottom0,\n bottom1,\n })\n }\n }\n return out\n}\n\n// A SNP's band is a sliver, so the pointer gets a few px either side of one\nconst HIT_SLOP_PX = 3\n\nexport function connectorAt(\n connectors: readonly Connector[],\n zoneBottom: number,\n sx: number,\n sy: number,\n) {\n if (zoneBottom <= 0 || sy < 0 || sy > zoneBottom) {\n return undefined\n }\n const t = sy / zoneBottom\n let hit: string | undefined\n let best = HIT_SLOP_PX\n for (const c of connectors) {\n const a = c.top0 + (c.bottom0 - c.top0) * t\n const b = c.top1 + (c.bottom1 - c.top1) * t\n const dist = Math.max(0, Math.min(a, b) - sx, sx - Math.max(a, b))\n if (dist < best) {\n best = dist\n hit = c.node\n }\n }\n return hit\n}\n\nfunction band(ctx: CanvasRenderingContext2D, c: Connector, bottom: number) {\n ctx.moveTo(c.top0, 0)\n ctx.lineTo(c.top1, 0)\n ctx.lineTo(c.bottom1, bottom)\n ctx.lineTo(c.bottom0, bottom)\n ctx.closePath()\n}\n\nfunction sides(ctx: CanvasRenderingContext2D, c: Connector, bottom: number) {\n ctx.moveTo(c.top0, 0)\n ctx.lineTo(c.bottom0, bottom)\n ctx.moveTo(c.top1, 0)\n ctx.lineTo(c.bottom1, bottom)\n}\n\n// One fill and one stroke for every band, so where hundreds of them converge\n// on the ruler the ink composites once rather than going black. The lit band\n// takes the tube map's own highlight.\nexport function drawTubeMapConnectors(\n ctx: CanvasRenderingContext2D,\n connectors: readonly Connector[],\n bottom: number,\n {\n highlightNode,\n darkMode,\n }: { highlightNode?: string | null; darkMode?: boolean },\n) {\n if (bottom <= 0 || connectors.length === 0) {\n return\n }\n const ink = darkMode ? '255,255,255' : '0,0,0'\n ctx.beginPath()\n for (const c of connectors) {\n band(ctx, c, bottom)\n }\n ctx.fillStyle = `rgba(${ink},0.07)`\n ctx.fill()\n ctx.beginPath()\n for (const c of connectors) {\n sides(ctx, c, bottom)\n }\n ctx.strokeStyle = `rgba(${ink},0.35)`\n ctx.lineWidth = 0.5\n ctx.stroke()\n const lit = connectors.find(c => c.node === highlightNode)\n if (lit) {\n ctx.beginPath()\n band(ctx, lit, bottom)\n ctx.fillStyle = 'rgba(255,192,203,0.5)'\n ctx.fill()\n ctx.beginPath()\n sides(ctx, lit, bottom)\n ctx.strokeStyle = '#ff0000'\n ctx.lineWidth = 1\n ctx.stroke()\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Deviation } from './coarsen'\nimport type { TubeMapFrame } from './draw'\nimport type { Graph } from '../types'\nimport type { LayoutNode, TubeMapLayout } from '@gmod/tubemap-core'\n\n// A folded variant on the tube of the walk that carries it, in tube\n// coordinates. Placed by bp through the box that holds it, since tubemap-core\n// may merge the coarsened nodes again and a box can stand for several.\nexport interface DeviationMark {\n x0: number\n x1: number\n y: number\n height: number\n}\n\ninterface Box {\n bp0: number\n bp1: number\n index: number\n node: LayoutNode\n}\n\nfunction boxesOf(graph: Graph, layout: TubeMapLayout) {\n const nodeById = new Map(graph.nodes.map(n => [n.id, n]))\n const boxes: Box[] = []\n layout.nodes.forEach((node, index) => {\n const g = nodeById.get(node.name)\n if (node.order >= 0 && g && isBackbone(g)) {\n const bp0 = g.stable.start\n boxes.push({ bp0, bp1: bp0 + node.sequenceLength, index, node })\n }\n })\n return boxes.sort((a, b) => a.bp0 - b.bp0)\n}\n\n// The box holding `bp` that this walk passes through; an insertion on a\n// boundary between two boxes sits on whichever the walk visits\nfunction boxAt(\n boxes: readonly Box[],\n bp: number,\n visited: Map<number, number>,\n) {\n let lo = 0\n let hi = boxes.length - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (boxes[mid]!.bp0 <= bp) {\n lo = mid\n } else {\n hi = mid - 1\n }\n }\n return [boxes[lo], boxes[lo - 1]].find(\n box => box && visited.has(box.index) && box.bp0 <= bp && bp <= box.bp1,\n )\n}\n\nexport function deviationMarks(\n graph: Graph,\n layout: TubeMapLayout,\n deviations: Map<string, Deviation[]>,\n) {\n const boxes = boxesOf(graph, layout)\n const marks: DeviationMark[] = []\n for (const track of layout.tracks) {\n const devs = track.name ? deviations.get(track.name) : undefined\n if (devs?.length) {\n // a walk that loops through a box twice marks its first pass\n const visited = new Map<number, number>()\n for (const segment of track.path) {\n if (\n segment.node !== null &&\n segment.y !== undefined &&\n !visited.has(segment.node)\n ) {\n visited.set(segment.node, segment.y)\n }\n }\n for (const d of devs) {\n const box = boxAt(boxes, d.start, visited)\n if (box) {\n const { node, bp0, bp1 } = box\n const at = (bp: number) =>\n node.x + ((Math.min(bp, bp1) - bp0) / (bp1 - bp0)) * node.pixelWidth\n marks.push({\n x0: at(d.start),\n x1: at(d.end),\n y: visited.get(box.index)!,\n height: track.width,\n })\n }\n }\n }\n }\n return marks\n}\n\n// Dark on light and light on dark, since the tubes themselves carry every hue\nexport function deviationInk(darkMode?: boolean) {\n return darkMode ? 'rgba(255,255,255,0.9)' : 'rgba(0,0,0,0.8)'\n}\n\n// A tick at least a px wide across the tube\nexport function drawDeviationMarks(\n ctx: CanvasRenderingContext2D,\n marks: readonly DeviationMark[],\n { x, y, width, darkMode }: TubeMapFrame,\n) {\n if (marks.length === 0) {\n return\n }\n ctx.fillStyle = deviationInk(darkMode)\n ctx.beginPath()\n for (const m of marks) {\n const left = x(m.x0)\n const right = Math.max(x(m.x1), left + 1)\n if (right >= 0 && left <= width) {\n const top = y(m.y)\n ctx.rect(left, top, right - left, y(m.y + m.height) - top)\n }\n }\n ctx.fill()\n}\n", "import { forward, getXCoordinateOfBaseWithinNode } from '@gmod/tubemap-core'\n\nimport type { Track, TubeMapLayout } from '@gmod/tubemap-core'\n\n// Where a read differs from the nodes it runs through, in tube coordinates:\n// the marks sequenceTubeMap's drawMismatches puts on a read's tube. Each mark\n// spans the bases it covers and sits on the tube of the read that carries it\n// (`y` is the tube's top, `height` its width), so a painter only maps them\n// through the frame. `nodeY` is the top of the node, which a hover draws a\n// leader up to.\ninterface MarkBase {\n readId: number\n y: number\n height: number\n nodeY: number\n}\n\nexport type TubeMapMismatch =\n | (MarkBase & {\n kind: 'insertion'\n x: number\n seq?: string\n // at the read's first or last base, where an aligner clips rather than\n // inserts; sequenceTubeMap hides these unless soft clips are shown\n softClip: boolean\n })\n | (MarkBase & { kind: 'deletion'; x0: number; x1: number })\n | (MarkBase & { kind: 'substitution'; x0: number; x1: number; seq: string })\n\nfunction readMismatches(read: Track, layout: TubeMapLayout) {\n const marks: TubeMapMismatch[] = []\n const entries = read.sequenceNew ?? []\n entries.forEach((entry, i) => {\n const nodeIndex = layout.nodeMap.get(forward(entry.nodeName))\n const node = nodeIndex === undefined ? undefined : layout.nodes[nodeIndex]\n // Node merging can drop a visit, so the segment for entry i is at or after\n // path[i], and the walk stops at the path's end.\n let pathIndex = i\n while (\n pathIndex < read.path.length &&\n read.path[pathIndex]!.node !== nodeIndex\n ) {\n pathIndex += 1\n }\n const y = read.path[pathIndex]?.y\n if (node && y !== undefined) {\n const base = { readId: read.id, y, height: read.width, nodeY: node.y }\n // A base past a merged node's end has no x.\n const at = (pos: number) => getXCoordinateOfBaseWithinNode(node, pos)\n for (const mm of entry.mismatches) {\n const x0 = at(mm.pos)\n if (x0 !== null) {\n if (mm.type === 'insertion') {\n const softClip =\n (i === 0 && mm.pos === read.firstNodeOffset) ||\n (i === entries.length - 1 && mm.pos === read.finalNodeCoverLength)\n marks.push({\n ...base,\n kind: 'insertion',\n x: x0,\n seq: mm.seq,\n softClip,\n })\n } else if (mm.type === 'deletion' && mm.length !== undefined) {\n const x1 = at(mm.pos + mm.length)\n if (x1 !== null) {\n marks.push({ ...base, kind: 'deletion', x0, x1 })\n }\n } else if (mm.type === 'substitution' && mm.seq !== undefined) {\n const x1 = at(mm.pos + mm.seq.length)\n if (x1 !== null) {\n marks.push({ ...base, kind: 'substitution', x0, x1, seq: mm.seq })\n }\n }\n }\n }\n }\n })\n return marks\n}\n\nexport function tubeMapMismatches(layout: TubeMapLayout) {\n return layout.reads.flatMap(read => readMismatches(read, layout))\n}\n", "import { curvePaths, nodeOutlinePath } from '@gmod/tubemap-core'\n\nimport { tubeMapMismatches } from './mismatches'\n\nimport type { TubeMapTransform } from './frame'\nimport type { TubeMapMismatch } from './mismatches'\nimport type { TrackType, TubeMapLayout } from '@gmod/tubemap-core'\n\n// Paints a tube map layout on a 2D canvas, in the order sequenceTubeMap's d3\n// drawing stacks it: haplotype tubes, their lane changes and turnarounds, then\n// reads the same way, then the node boxes over all of them. Everything goes\n// through `x` and `y`, tube coordinates to screen px, so the one painter serves\n// the tube map's own axis (an affine map) and the reference axis (warp.ts).\n//\n// The package states curves, corners and node outlines as SVG path data. They\n// are parsed once per layout into commands whose points can be mapped, since\n// a Path2D built from the string could only be transformed affinely.\n\ntype Command =\n | { op: 'M' | 'L'; x: number; y: number }\n | { op: 'Q'; x1: number; y1: number; x: number; y: number }\n | {\n op: 'C'\n x1: number\n y1: number\n x2: number\n y2: number\n x: number\n y: number\n }\n | { op: 'H'; x: number }\n | { op: 'V'; y: number }\n | { op: 'Z' }\n\n// The absolute M/L/H/V/Q/C/Z subset tubemap-core writes\nexport function parsePath(d: string): Command[] {\n const tokens = d.trim().split(/[\\s,]+/)\n const commands: Command[] = []\n let i = 0\n const num = () => Number(tokens[i++])\n while (i < tokens.length) {\n const op = tokens[i++]\n if (op === 'M' || op === 'L') {\n commands.push({ op, x: num(), y: num() })\n } else if (op === 'H') {\n commands.push({ op, x: num() })\n } else if (op === 'V') {\n commands.push({ op, y: num() })\n } else if (op === 'Q') {\n commands.push({ op, x1: num(), y1: num(), x: num(), y: num() })\n } else if (op === 'C') {\n commands.push({\n op,\n x1: num(),\n y1: num(),\n x2: num(),\n y2: num(),\n x: num(),\n y: num(),\n })\n } else if (op === 'Z') {\n commands.push({ op })\n }\n }\n return commands\n}\n\ninterface Filled {\n color: string\n // a haplotype tube's track id, which is its path's index in the graph\n track?: number\n alpha: number\n // tube x extent, for culling\n x0: number\n x1: number\n}\n\ninterface Rect extends Filled {\n y0: number\n y1: number\n}\n\ninterface Shape extends Filled {\n commands: Command[]\n}\n\ninterface Layer {\n rects: Rect[]\n shapes: Shape[]\n}\n\nexport interface TubeMapPicture {\n bounds: TubeMapLayout['bounds']\n layers: Layer[]\n nodes: { name: string; commands: Command[]; x0: number; x1: number }[]\n mismatches: TubeMapMismatch[]\n}\n\nfunction extentOf(commands: Command[]) {\n let x0 = Infinity\n let x1 = -Infinity\n for (const c of commands) {\n if ('x' in c) {\n x0 = Math.min(x0, c.x)\n x1 = Math.max(x1, c.x)\n }\n if ('x1' in c) {\n x0 = Math.min(x0, c.x1)\n x1 = Math.max(x1, c.x1)\n }\n }\n return { x0, x1 }\n}\n\nfunction shapeOf(\n d: string,\n color: string,\n alpha: number,\n track: number | undefined,\n): Shape {\n const commands = parsePath(d)\n return { commands, color, track, alpha, ...extentOf(commands) }\n}\n\nfunction layerOf(layout: TubeMapLayout, type: TrackType): Layer {\n const { shapes } = layout\n const trackOf = (s: { id: number }) =>\n type === 'haplotype' ? s.id : undefined\n const rects = [...shapes.rectangles, ...shapes.verticalRectangles]\n .filter(r => r.type === type)\n .map(r => ({\n color: r.color,\n track: trackOf(r),\n alpha: r.alpha ?? 1,\n x0: r.xStart,\n x1: r.xEnd + 1,\n y0: r.yStart,\n y1: r.yEnd + 1,\n }))\n const curves = curvePaths(shapes.curves, type).map(c =>\n shapeOf(c.path!, c.color, c.alpha ?? 1, trackOf(c)),\n )\n const corners = shapes.corners\n .filter(c => c.type === type)\n .map(c => shapeOf(c.path, c.color, 1, trackOf(c)))\n return { rects, shapes: [...curves, ...corners] }\n}\n\n// Everything a frame needs that does not depend on the transform\nexport function tubeMapPicture(layout: TubeMapLayout): TubeMapPicture {\n const nodes: TubeMapPicture['nodes'] = []\n layout.nodes.forEach(node => {\n if (node.order >= 0) {\n const commands = parsePath(nodeOutlinePath(node))\n nodes.push({ name: node.name, commands, ...extentOf(commands) })\n }\n })\n return {\n bounds: layout.bounds,\n layers: [layerOf(layout, 'haplotype'), layerOf(layout, 'read')],\n nodes,\n mismatches: tubeMapMismatches(layout).filter(\n m => m.kind !== 'insertion' || !m.softClip,\n ),\n }\n}\n\nfunction trace(\n ctx: CanvasRenderingContext2D,\n commands: Command[],\n x: (tx: number) => number,\n y: (ty: number) => number,\n) {\n let cx = 0\n let cy = 0\n for (const c of commands) {\n switch (c.op) {\n case 'M':\n cx = c.x\n cy = c.y\n ctx.moveTo(x(cx), y(cy))\n break\n case 'L':\n cx = c.x\n cy = c.y\n ctx.lineTo(x(cx), y(cy))\n break\n case 'H':\n cx = c.x\n ctx.lineTo(x(cx), y(cy))\n break\n case 'V':\n cy = c.y\n ctx.lineTo(x(cx), y(cy))\n break\n case 'Q':\n cx = c.x\n cy = c.y\n ctx.quadraticCurveTo(x(c.x1), y(c.y1), x(cx), y(cy))\n break\n case 'C':\n cx = c.x\n cy = c.y\n ctx.bezierCurveTo(x(c.x1), y(c.y1), x(c.x2), y(c.y2), x(cx), y(cy))\n break\n case 'Z':\n ctx.closePath()\n break\n }\n }\n}\n\nexport interface TubeMapFrame extends TubeMapTransform {\n width: number\n highlightNode?: string | null\n darkMode?: boolean\n // a haplotype tube's colour by track id, where it is not the layout's\n tubeColors?: readonly string[]\n // a box's tint by node name; a box with none is clear\n nodeColors?: ReadonlyMap<string, string>\n}\n\n// One fill per colour per layer: a layer's shapes do not overlap in a way the\n// order within it would decide, and a fill per shape costs a draw call each.\nfunction fillByColor<T extends Filled>(\n ctx: CanvasRenderingContext2D,\n items: T[],\n visible: (item: T) => boolean,\n colorOf: (item: T) => string,\n addTo: (item: T) => void,\n) {\n const byColor = new Map<string, T[]>()\n for (const item of items) {\n if (visible(item)) {\n const key = `${colorOf(item)}|${item.alpha}`\n const list = byColor.get(key)\n if (list) {\n list.push(item)\n } else {\n byColor.set(key, [item])\n }\n }\n }\n for (const list of byColor.values()) {\n ctx.fillStyle = colorOf(list[0]!)\n ctx.globalAlpha = list[0]!.alpha\n ctx.beginPath()\n for (const item of list) {\n addTo(item)\n }\n ctx.fill()\n }\n ctx.globalAlpha = 1\n}\n\n// Zoomed out, a box is a few px of outline and a cut's boxes ink over the\n// tubes between them, so an outline fades as its box narrows\nconst FULL_OUTLINE_PX = 12\nconst MIN_OUTLINE_ALPHA = 0.15\n\nfunction outlineAlpha(screenWidth: number) {\n return Math.max(MIN_OUTLINE_ALPHA, Math.min(1, screenWidth / FULL_OUTLINE_PX))\n}\n\n// A tinted box lets the tubes through it show, as the clear box's wash does\nconst NODE_TINT_ALPHA = 0.55\n\nexport function drawTubeMap(\n ctx: CanvasRenderingContext2D,\n picture: TubeMapPicture,\n frame: TubeMapFrame,\n) {\n const { x, y, width } = frame\n const visible = (item: { x0: number; x1: number }) =>\n x(item.x1) >= 0 && x(item.x0) <= width\n const colorOf = (item: Filled) =>\n (item.track !== undefined && frame.tubeColors?.[item.track]) || item.color\n for (const layer of picture.layers) {\n fillByColor(ctx, layer.rects, visible, colorOf, r => {\n const left = x(r.x0)\n const top = y(r.y0)\n ctx.rect(left, top, x(r.x1) - left, y(r.y1) - top)\n })\n fillByColor(ctx, layer.shapes, visible, colorOf, s => {\n trace(ctx, s.commands, x, y)\n })\n }\n const stroke = frame.darkMode ? '#d0d0d0' : '#000000'\n const fill = frame.darkMode ? 'rgba(40,40,40,0.4)' : 'rgba(255,255,255,0.4)'\n ctx.lineWidth = 2 * Math.max(0.25, Math.min(1, frame.yScale))\n for (const node of picture.nodes) {\n if (visible(node)) {\n const lit = node.name === frame.highlightNode\n ctx.beginPath()\n trace(ctx, node.commands, x, y)\n const tint = lit ? undefined : frame.nodeColors?.get(node.name)\n ctx.fillStyle = lit ? 'rgba(255,192,203,0.5)' : (tint ?? fill)\n ctx.strokeStyle = lit ? '#ff0000' : stroke\n ctx.globalAlpha = tint ? NODE_TINT_ALPHA : 1\n ctx.fill()\n ctx.globalAlpha = lit ? 1 : outlineAlpha(x(node.x1) - x(node.x0))\n ctx.stroke()\n ctx.globalAlpha = 1\n }\n }\n drawMismatches(ctx, picture.mismatches, frame)\n}\n\n// sequenceTubeMap's marks, which it draws at 12px and hides once zoomed out\n// below half size\nconst MISMATCH_FONT_PX = 12\nconst MIN_MISMATCH_FONT_PX = 6\n\nexport function mismatchesLegible(yScale: number) {\n return MISMATCH_FONT_PX * yScale >= MIN_MISMATCH_FONT_PX\n}\n\nexport function mismatchOnScreen(\n m: TubeMapMismatch,\n { x, width }: Pick<TubeMapFrame, 'x' | 'width'>,\n) {\n const [x0, x1] = m.kind === 'insertion' ? [m.x, m.x] : [m.x0, m.x1]\n return x(x1) >= 0 && x(x0) <= width\n}\n\nfunction drawMismatches(\n ctx: CanvasRenderingContext2D,\n marks: readonly TubeMapMismatch[],\n frame: TubeMapFrame,\n) {\n const { x, y, yScale, darkMode } = frame\n if (!mismatchesLegible(yScale) || marks.length === 0) {\n return\n }\n const fontPx = MISMATCH_FONT_PX * yScale\n ctx.fillStyle = 'grey'\n ctx.beginPath()\n for (const m of marks) {\n if (m.kind === 'deletion' && mismatchOnScreen(m, frame)) {\n const left = x(m.x0)\n const top = y(m.y)\n ctx.rect(left, top, x(m.x1) - left, y(m.y + m.height) - top)\n }\n }\n ctx.fill()\n ctx.font = `${fontPx}px monospace`\n ctx.textBaseline = 'alphabetic'\n ctx.fillStyle = darkMode ? '#ffffff' : '#000000'\n for (const m of marks) {\n if (!mismatchOnScreen(m, frame)) {\n continue\n }\n if (m.kind === 'substitution') {\n ctx.fillText(m.seq, x(m.x0) + 1, y(m.y + m.height))\n } else if (m.kind === 'insertion') {\n ctx.fillText('*', x(m.x) - 3, y(m.y + m.height))\n }\n }\n}\n", "import { tubeSpan } from './axis'\n\nimport type { ReferenceBoxes } from './axis'\nimport type { TubeMapFrame } from './draw'\nimport type { GeneModel } from '../genes/genePins'\n\n// A gene over the tube map, in tube x, mapped through the reference's boxes\n// (axis.ts)\nexport interface TubeMapGene {\n gene: GeneModel\n x0: number\n x1: number\n exons: { x0: number; x1: number }[]\n}\n\n// A gene takes the boxes of the refName it equals, as in genePins\nexport function tubeMapGenes(\n byRefName: ReferenceBoxes,\n genes: readonly GeneModel[],\n): TubeMapGene[] {\n const out: TubeMapGene[] = []\n for (const gene of genes) {\n const boxes = byRefName.get(gene.refName)\n const first = boxes?.[0]\n const last = boxes?.at(-1)\n if (!boxes || !first || !last) {\n continue\n }\n const start = Math.max(gene.start, first.bp0)\n const end = Math.min(gene.end, last.bp1)\n if (end <= start) {\n continue\n }\n out.push({\n gene,\n ...tubeSpan(boxes, start, end),\n exons: gene.exons\n .filter(e => e.end > start && e.start < end)\n .map(e =>\n tubeSpan(boxes, Math.max(e.start, start), Math.min(e.end, end)),\n ),\n })\n }\n return out\n}\n\nexport const GENE_ROW_PX = 22\nconst MAX_GENE_ROWS = 4\nconst EXON_PX = 8\nconst NAME_FONT = '11px sans-serif'\nconst NAME_GAP_PX = 8\n\n// The rows the genes need at least: as many as overlap at one point, which\n// holds at every zoom. A name wider than its gene can want more, and shares\n// the top row.\nexport function tubeMapGeneRows(genes: readonly TubeMapGene[]) {\n const edges = genes\n .flatMap(g => [\n { x: g.x0, step: 1 },\n { x: g.x1, step: -1 },\n ])\n .sort((a, b) => a.x - b.x || a.step - b.step)\n let depth = 0\n let most = 0\n for (const { step } of edges) {\n depth += step\n most = Math.max(most, depth)\n }\n return Math.min(MAX_GENE_ROWS, most)\n}\n\n// Rows of genes above the tubes, bottom row nearest them, packed so neither\n// glyphs nor names overlap. Packed per frame, since the reference axis warps x\n// by zoom. The rows go up to `top`; past that, genes share the top row.\nexport function drawTubeMapGenes(\n ctx: CanvasRenderingContext2D,\n genes: readonly TubeMapGene[],\n frame: TubeMapFrame,\n bottom: number,\n top = 0,\n) {\n const { x, width, darkMode } = frame\n const rows = Math.max(1, Math.floor((bottom - top) / GENE_ROW_PX))\n const rowEnds: number[] = []\n const ink = darkMode ? '#e0e0e6' : '#1c1c22'\n ctx.font = NAME_FONT\n ctx.textAlign = 'center'\n ctx.textBaseline = 'alphabetic'\n ctx.fillStyle = ink\n ctx.strokeStyle = ink\n ctx.lineWidth = 1\n const placed = genes\n .map(g => ({ g, s0: x(g.x0), s1: x(g.x1) }))\n .filter(({ s0, s1 }) => s1 >= 0 && s0 <= width)\n .sort((a, b) => a.s0 - b.s0)\n for (const { g, s0, s1 } of placed) {\n const arrow = g.gene.strand > 0 ? ' \u2192' : g.gene.strand < 0 ? '\u2190 ' : ''\n const label =\n g.gene.strand < 0 ? `${arrow}${g.gene.name}` : `${g.gene.name}${arrow}`\n const mid = (Math.max(s0, 0) + Math.min(s1, width)) / 2\n const half = ctx.measureText(label).width / 2\n const left = Math.min(s0, mid - half)\n const right = Math.max(s1, mid + half)\n let row = rowEnds.findIndex(end => end + NAME_GAP_PX <= left)\n if (row === -1) {\n row = Math.min(rowEnds.length, rows - 1)\n }\n rowEnds[row] = Math.max(rowEnds[row] ?? -Infinity, right)\n const glyphY = bottom - row * GENE_ROW_PX - EXON_PX / 2\n ctx.beginPath()\n ctx.moveTo(s0, glyphY + 0.5)\n ctx.lineTo(s1, glyphY + 0.5)\n ctx.stroke()\n for (const e of g.exons) {\n const e0 = x(e.x0)\n ctx.fillRect(e0, glyphY - EXON_PX / 2, Math.max(1, x(e.x1) - e0), EXON_PX)\n }\n ctx.fillText(label, mid, glyphY - EXON_PX / 2 - 2)\n }\n}\n", "import type { RepeatCall } from './repeatFeatures'\n\n// Which genotyped allele belongs to which haplotype walk. A VCF states a\n// sample's alleles in genotype order, which is the genotyper's own order and\n// carries no correspondence to the assembly's haplotype numbering \u2014 TRGTdb's\n// ABCA7 record, for one, lists them shortest first. So the walks and the\n// alleles are paired by length instead: the pairing whose total difference is\n// smallest, which for lengths on a line is the non-crossing one.\n//\n// A walk that does not reach both flanking reference nodes is measured whole\n// rather than across the array, and an allele no read spanned is a copy of the\n// one that was called. Neither states a length for this array, so they pair\n// last and take no verdict \u2014 pairing them first let a walk that stops inside\n// the array take the allele that fit one spanning it, and the spanning walk\n// then read as a disagreement.\n\nexport interface WalkCall extends RepeatCall {\n // undefined where the walk and the allele are not both measurements\n agrees?: boolean\n}\n\n// Every walk/call pair over the 94 HPRC samples TRGT genotyped at the ABCA7\n// VNTR is either within 8% of its allele or more than 14% from it, so any\n// threshold across that gap separates them the same way.\nexport const CALL_TOLERANCE = 0.1\n\nfunction agrees(walkBp: number, callBp: number) {\n return Math.abs(callBp - walkBp) <= CALL_TOLERANCE * Math.max(walkBp, callBp)\n}\n\n// Pair two ascending lists of lengths, leaving out the surplus of whichever is\n// longer. `out[i]` is the index in `calls` that `walks[i]` took.\nfunction nearest(walks: number[], calls: number[]) {\n const W = walks.length\n const C = calls.length\n const cost = Array.from({ length: W + 1 }, () => new Array(C + 1).fill(0))\n const paired = Array.from({ length: W + 1 }, () =>\n new Array<boolean>(C + 1).fill(false),\n )\n for (let i = W - 1; i >= 0; i--) {\n for (let j = C - 1; j >= 0; j--) {\n const together = Math.abs(walks[i]! - calls[j]!) + cost[i + 1]![j + 1]!\n const dropWalk = W - i > C - j ? cost[i + 1]![j]! : Infinity\n const dropCall = C - j > W - i ? cost[i]![j + 1]! : Infinity\n cost[i]![j] = Math.min(together, dropWalk, dropCall)\n paired[i]![j] = cost[i]![j] === together\n }\n }\n const out = new Array<number | undefined>(W).fill(undefined)\n for (let i = 0, j = 0; i < W && j < C;) {\n if (paired[i]![j]) {\n out[i++] = j++\n } else if (W - i > C - j) {\n i++\n } else {\n j++\n }\n }\n return out\n}\n\nfunction byLength<T>(items: T[], bp: (item: T) => number) {\n return items\n .map((item, i) => ({ item, i }))\n .sort((a, b) => bp(a.item) - bp(b.item))\n}\n\nconst backed = (call: RepeatCall) => call.spanningReads !== 0\n\nfunction scored(row: { bp: number; complete: boolean }, call: RepeatCall) {\n return {\n ...call,\n agrees: row.complete && backed(call) ? agrees(row.bp, call.bp) : undefined,\n }\n}\n\n// A phased genotype names its walk's haplotype outright. The rest go by\n// length: the walks that span the array against the alleles reads spanned,\n// then everything else against what those two left. That decides the\n// ambiguous cases on merit: which walk carries the one allele a lone spanning\n// read supports, and which allele a walk that stops inside the array is drawn\n// against.\nfunction pairSample<\n R extends { bp: number; complete: boolean; haplotype?: number },\n>(rows: R[], calls: RepeatCall[]) {\n const out = new Map<number, WalkCall>()\n const taken = new Set<RepeatCall>()\n rows.forEach((row, i) => {\n const call = calls.find(\n c => c.haplotype !== undefined && c.haplotype === row.haplotype,\n )\n if (call) {\n taken.add(call)\n out.set(i, scored(row, call))\n }\n })\n const unphased = calls.filter(call => call.haplotype === undefined)\n const ranked = byLength(rows, r => r.bp)\n const stages = [\n [ranked.filter(r => r.item.complete), unphased.filter(backed)],\n [ranked, unphased],\n ] as const\n for (const [pool, group] of stages) {\n const free = pool.filter(r => !out.has(r.i))\n const open = byLength(\n group.filter(call => !taken.has(call)),\n call => call.bp,\n )\n nearest(\n free.map(r => r.item.bp),\n open.map(c => c.item.bp),\n ).forEach((pick, k) => {\n if (pick !== undefined) {\n const row = free[k]!\n const call = open[pick]!.item\n taken.add(call)\n out.set(row.i, scored(row.item, call))\n }\n })\n }\n return out\n}\n\nexport function withCalls<\n R extends {\n sample: string\n bp: number\n complete: boolean\n haplotype?: number\n },\n>(rows: R[], calls: Record<string, RepeatCall[]> | undefined) {\n if (!calls) {\n return rows as (R & { call?: WalkCall })[]\n }\n const paired = new Map<R, WalkCall>()\n const bySample = new Map<string, R[]>()\n for (const row of rows) {\n bySample.set(row.sample, [...(bySample.get(row.sample) ?? []), row])\n }\n for (const [sample, group] of bySample) {\n const called = calls[sample]\n if (called) {\n for (const [i, call] of pairSample(group, called)) {\n paired.set(group[i]!, call)\n }\n }\n }\n return rows.map(row => ({ ...row, call: paired.get(row) }))\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6\"\n}), 'Settings');"],
5
+ "mappings": "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",
6
+ "names": ["require_SvgIcon", "__commonJSMin", "exports", "module", "require_colorBits", "__commonJSMin", "exports", "module", "require_mobx", "__commonJSMin", "exports", "module", "require_canvas2dUtils", "__commonJSMin", "exports", "module", "require_renderingBackendBase", "__commonJSMin", "exports", "module", "import_SvgIcon", "HALO_FACTOR", "LEGEND_INSET_PX", "ROUTE_STACK", "GENE_PIN_DROP_PX", "GENE_STACK", "TOPMOST_BASELINE", "geneCoverageNote", "pin", "length", "per", "unit", "amount", "bp", "geneLabelCandidates", "pins", "screen", "contigThickness", "byExtent", "x", "y", "note", "items", "extent", "a", "b", "layoutLabels", "m", "width", "height", "translateX", "translateY", "scaleX", "scaleY", "frame", "p", "reserved", "label", "rowLabelBox", "stripPx", "take", "occupancy", "onRows", "rowsTop", "r", "halo", "byBubble", "h", "bubbles", "placeLabels", "baseline", "genes", "sizeCandidates", "sizeLabelCandidates", "deletionLabels", "placeSizeLabels", "routes", "route", "nodeLabels", "nodeLabelBudget", "import_colorBits", "import_colorBits", "fadeAbgr", "c", "alpha", "brightenAbgr", "factor", "MAX_BACKBONE_HOPS", "buildNeighbors", "graph", "neighbors", "add", "from", "to", "list", "edge", "flankingSpan", "nodeId", "nodeById", "seen", "frontier", "minEnd", "maxStart", "hop", "next", "id", "adjacent", "neighborId", "neighbor", "isBackbone", "nodeReferenceSpan", "node", "referenceMidpoints", "midpoints", "span", "cache", "baseEdgeCurves", "nodePositions", "graph", "axis", "deletions", "version", "routes", "scaleX", "scaleY", "cached", "curves", "ei", "edge", "from", "to", "bypassed", "route", "routedEdgeCurves", "computeEdgeCurves", "selfLinkOf", "bypassedPoints", "EDGE_DEFAULT_COLOR", "EDGE_PATH_FALLBACK_COLOR", "EDGE_DELETION_COLOR", "DELETION_THICKNESS_FACTOR", "LIFT_BACKDROP_COLOR", "FADED_ALPHA", "LIFT_BACKDROP_CSS", "fadeAbgr", "DELETION_DASH_PX", "MIN_PATH_STRIPE_PX", "MIN_WALK_LANE_PX", "MIN_SCALE_FOR_OFFSET", "ARROW_LENGTH_BASE_PX", "ARROW_LENGTH_PER_EDGE_PX", "ARROW_HALF_WIDTH_BASE_PX", "ARROW_HALF_WIDTH_PER_EDGE_PX", "MIN_ARROW_FIT", "MIN_ARROW_SCALE", "ARROW_CURVE_STEPS", "ABUTTING_PX", "walkX", "walkY", "walkPx", "backFromEnd", "curves", "scale", "yToX", "untilPx", "most", "last", "n", "px", "ci", "k", "t", "u", "a", "b", "d", "e", "x", "y", "walkPointAt", "i", "from", "to", "f", "arrowheadFor", "edgeHalfWidth", "nodeHalfWidth", "color", "fromSegments", "toSegments", "length", "lengthPx", "inset", "room", "polylinePx", "fit", "tip", "base", "dx", "dy", "endTangent", "intoDirection", "segments", "first", "final", "pointNormalsOf", "rawPoints", "points", "p", "pointNormals", "nx", "ny", "len", "dx1", "dy1", "len1", "dx2", "dy2", "len2", "nx1", "ny1", "nx2", "ny2", "dot", "offsetPolyline", "normals", "distance", "packNorm", "r", "g", "hslToRgb", "h", "s", "l", "c", "m", "DEPTH_GRADIENT", "STABLE_RANK_GRADIENT", "NODE_LENGTH_GRADIENT", "sampleGradient", "stops", "pos", "hashColor", "str", "alpha", "nameHue", "PATH_SATURATION", "PATH_LIGHTNESS", "REFERENCE_RAMP_MAX_HUE", "REFERENCE_RAMP_SATURATION", "REFERENCE_RAMP_LIGHTNESS", "REFERENCE_RAMP_ALT_RGB", "REFERENCE_RAMP_ALT_COLOR", "REFERENCE_RAMP_ALT_CSS", "computeReferenceRamp", "graph", "domain", "midpoints", "referenceMidpoints", "min", "max", "mid", "computeColorSchemeRange", "minDepth", "maxDepth", "minLength", "maxLength", "maxRank", "getNodeColor", "node", "nodeIndex", "colorScheme", "range", "ramp", "frac", "hue", "into", "candidates", "isPolylineInBounds", "bounds", "hit", "isBezierInBounds", "cMinX", "cMaxX", "cMinY", "cMaxY", "paletteCache", "pathPaletteOf", "palette", "pathColorByIndex", "pathIndexByName", "paths", "pathHueAt", "slotsCache", "NO_SLOTS", "pathSlotsOf", "slots", "pathIdx", "path", "nodeId", "visits", "buildGeometry", "options", "nodePositions", "nodeById", "contigThickness", "connectorThickness", "drawPaths", "nodeWidth", "highlight", "axis", "linearLayout", "viewportBounds", "referenceRamp", "deletions", "deletionRoutes", "hiddenEdges", "version", "depthNorm", "meanDepth", "yToXOf", "sharedCurves", "baseEdgeCurves", "nodeStrokes", "nodeStrokeRuns", "arrows", "arrowRuns", "edgeCurves", "edgeCurveRuns", "colorRange", "nodeIndexMap", "pathCount", "pathColorByName", "name", "nodePathSlots", "showArrows", "ei", "edge", "baseCurves", "numPaths", "isDeletion", "onWalk", "edgeThickness", "ribbons", "plainEdgeColor", "edgeColor", "toNode", "intoHalfWidth", "nodeWidthPx", "buildSingleEdge", "offsetX", "offsetY", "arrowColor", "translateCurves", "dash", "dashCurves", "head", "edgeCurveStart", "arrowStart", "laneWalks", "fromNode", "width", "laneWidth", "worldPerScreenPx", "walk", "lane", "offset", "offsets", "pathRibbonOffsets", "arrowRibbon", "ribbonColor", "edgeCurveCount", "arrowCount", "faded", "own", "nodeThickness", "start", "slotWidth", "drawable", "lanes", "laneColor", "slot", "stripe", "count", "rampHueCss", "hue", "rampHue", "bp", "domain", "frac", "REFERENCE_RAMP_MAX_HUE", "HUE_STOP_DEG", "rampStops", "start", "reversed", "low", "high", "n", "_", "RAMP_GRADIENT_CSS", "i", "nodeOwnLocation", "node", "isAnchored", "anchoredNodeLocation", "panSNSample", "panSNHaplotype", "panSNContig", "widestCluster", "spans", "maxGap", "sorted", "a", "b", "best", "current", "span", "groupKey", "loc", "contributingAssemblies", "graph", "groups", "key", "group", "bySample", "candidate", "existing", "resolveLocationAssembly", "resolve", "resolveContributors", "contributors", "c", "sample", "locString", "locLabel", "import_jsx_runtime", "CompareArrows_default", "_jsx", "import_jsx_runtime", "HighlightAlt_default", "_jsx", "import_jsx_runtime", "LineStyle_default", "_jsx", "LINEAR_LABEL", "SYNTENY_LABEL", "NO_SYNTENY_TRACK", "oneOrMany", "label", "icon", "entries", "entryLabel", "onSelect", "disabledHelpText", "nameTheOnlyEntry", "first", "empty", "entry", "graphLaunchMenuItems", "contributors", "syntenyTracks", "onShowLinear", "onShowSynteny", "widest", "LineStyle_default", "c", "locLabel", "CompareArrows_default", "track", "nodeLaunchMenuItems", "own", "reference", "highlight", "onHighlight", "HighlightAlt_default", "target", "canonicalStrand", "node", "stepName", "strand", "stepStarts", "lengths", "total", "starts", "head", "i", "tail", "j", "mismatchesByStep", "record", "byStep", "stepAt", "pos", "atEnd", "s", "start", "length", "add", "mismatch", "list", "last", "op", "parseCs", "at", "left", "take", "tubeMapReads", "graph", "records", "firstId", "nodeByName", "n", "reads", "nodes", "step", "kept", "first", "mismatches", "names", "lastLength", "k", "FORWARD_READ_COLORS", "REVERSE_READ_COLORS", "readColor", "read", "colors", "parsePanSN", "refName", "parts", "haplotype", "collectRun", "seed", "byId", "adj", "claimed", "members", "anchors", "queue", "id", "node", "neighborId", "neighbor", "anchorSpan", "a", "sorted", "b", "first", "last", "start", "end", "runDistances", "entry", "m", "dist", "member", "reach", "known", "runOffsets", "ordered", "rank", "i", "offsets", "offset", "projectAlleles", "graph", "isAnchored", "n", "buildNeighbors", "alleles", "span", "max", "MIN_DELETION_BP", "MIN_ALLELE_SPAN_FRACTION", "placeOffReference", "graph", "span", "rowY", "positions", "minSpan", "alleles", "projectAlleles", "byId", "n", "alleleDeletions", "allele", "skipped", "drawn", "scale", "nodeId", "i", "node", "offset", "even", "at", "bp", "y", "neighbors", "buildNeighbors", "queue", "fromId", "fromEnd", "nextId", "next", "rankRows", "graph", "present", "node", "a", "b", "r", "i", "anchoredLayout", "region", "backbone", "backboneNodes", "span", "referenceSpan", "rows", "nodePositions", "backbonePositions", "alleleDeletions", "placeOffReference", "rowLabels", "rank", "row", "GUTTER", "LANE_PX", "nodeWidth", "node", "compare", "a", "b", "compareKeys", "UNREACHED", "referenceOrder", "graph", "byId", "n", "key", "orderKeys", "placeByWalks", "backbone", "isNode", "startOf", "next", "prev", "i", "head", "insertAfter", "at", "id", "after", "insertBefore", "before", "path", "rising", "falling", "last", "start", "ids", "from", "leading", "lead", "placed", "placeBySearch", "adjacent", "queue", "ka", "kb", "nb", "searched", "sided", "seed", "run", "farHalf", "hops", "deadEnd", "touchesLeft", "other", "k", "e", "isBackbone", "rank", "layerGraph", "order", "isRef", "preds", "succs", "link", "layerOf", "l", "p", "s", "minSucc", "t", "lo", "hi", "layers", "orderedLayout", "layer", "layerWidth", "w", "layerX", "x", "y", "free", "ideal", "taken", "lane", "d", "nodePositions", "cx", "ny", "contributingSamples", "graph", "keep", "carried", "node", "isOffReference", "sample", "parsePanSN", "held", "row", "a", "b", "sampleRowLayout", "region", "backbone", "backboneNodes", "samples", "span", "referenceSpan", "rowOf", "s", "i", "nodePositions", "backbonePositions", "spareRow", "alleleDeletions", "placeOffReference", "stable", "rowLabels", "emptyTrackShapes", "DEBUG", "DEFAULT_TRACK_COLORS", "configFrom", "options", "track", "config", "bed", "nodes", "tracks", "reads", "nodeMap", "nodesPerOrder", "nodeOrders", "assignments", "extraLeft", "extraRight", "maxOrder", "shapes", "emptyTrackShapes", "imageBounds", "trackForRuler", "coarsenedEdgeMeta", "layoutTubeMap", "inputNodes", "inputTracks", "inputReads", "copies", "deepCopy", "node", "i", "hadInputReads", "filterReads", "t", "straightenTrack", "generateNodeMap", "generateTrackIndexSequences", "generateNodeWidth", "generateNodeSuccessors", "generateNodeOrder", "reverseReversedReads", "mergeNodes", "generateNodeDegree", "getMaxOrder", "switchNodeOrientation", "coarsenHaplotypes", "rulerIndex", "refIndex", "altHaplotypes", "_", "bands", "buildCoarsenedSyntheticBands", "calculateTrackWidth", "generateLaneAssignment", "addTrackFeatures", "drawCoarsenedReads", "generateReadOnlyNodeAttributes", "placeReads", "generateNodeXCoords", "generateSVGShapesFromPath", "getImageDimensions", "generateTrackColor", "highlight", "generateTrackAlpha", "val", "isReverse", "nodeName", "s", "forward", "reverse", "flip", "isForwardIndex", "n", "index", "nodesToInvert", "currentSequence", "cur", "firstForwardIndex", "j", "orderY", "setMapToMax", "step", "map", "key", "value", "existing", "READ_WIDTH", "assignReadsToNodes", "read", "idx", "firstNode", "element", "pathIdx", "elemNode", "generateBasicPathsForReads", "sortedNodes", "compareNodesByOrder", "readsBySource", "source", "bucket", "allSources", "b", "topMargin", "placeReadSet", "bottomY", "calculateBottomY", "elementsWithoutNode", "len", "prevSegment", "lastValidPrev", "seg", "nextSegment", "firstValidNext", "previousVisitToNode", "nextVisitToNode", "betweenCycleReverseTraversal", "compareNoNodeReads", "segment", "readIDs", "toPlace", "incomingReads", "readID", "outgoingReads", "internalReads", "startY", "incomingKeys", "entry", "path", "decisionStep", "foundY", "k", "y", "compareIncomingReadKeys", "currentY", "occupiedUntil", "readElement", "setOccupiedUntil", "maxY", "compareReadOutgoingSegmentsByGoingTo", "occupiedFrom", "firstNodeOffset", "occUntil", "incReadElementIndices", "incRead", "incReadPathElement", "compareInternalReads", "readIdx", "currentRead", "finalNodeCoverLength", "heightIncrease", "adjustVertically3", "a", "pathIndex", "readA", "readB", "segmentA", "segmentB", "prevNodeA", "prevNodeB", "aPrev", "bPrev", "readIndexA", "pathIndexA", "readIndexB", "pathIndexB", "previousValidYA", "previousValidYB", "lastPathIndexA", "lastPathIndexB", "initialNodeA", "initialNodeB", "nodeA", "nodeB", "nextNodeIdx", "beginDiff", "compareTrackByInitialOrdering", "trackA", "trackB", "pathA", "pathB", "AIndex", "BIndex", "trackAOrder", "trackBOrder", "idxA", "idxB", "aFirst", "bFirst", "aLast", "bLast", "currentNodeIndex", "currentNodeIsForward", "currentNode", "previousNode", "previousNodeIsForward", "pos", "sequenceNew", "reverseMismatches", "nodeByName", "temp", "firstLen", "lastLen", "mismatches", "sequenceLength", "mm", "getReverseComplement", "COMPLEMENT", "result", "tracksOrReads", "rawNodeName", "fwdIdx", "switched", "nodeIndex", "NODE_HORIZONTAL_SLACK", "bounds", "successorSets", "predecessorSets", "addEdges", "current", "follower", "generateNodeOrderOfSingleTrack", "sequence", "forwardOrder", "backwardOrder", "minOrder", "order", "increaseOrderForAllNodes", "generateNodeOrderTrackBeginning", "anchorIndex", "anchorSeqVal", "currentOrder", "increment", "UNREACHABLE_ORDER", "fillUnassignedOrders", "orders", "filled", "modifiedSequence", "rightIndex", "leftIndex", "tracksAndReads", "reachability", "uninvert", "increaseOrderForSuccessors", "isSuccessor", "finalOrders", "first", "second", "scratch", "stamp", "generation", "stack", "childIndex", "max", "v", "amount", "startingNode", "tabuNode", "newOrder", "increasedOrders", "queue", "head", "currentNodeOrder", "successor", "predecessor", "pivotPath", "countPaths", "switchNodeOrientationForPaths", "paths", "toSwitch", "pivotNames", "prevOrder", "nextOrder", "addToSwitchScore", "score", "scores", "delta", "currentX", "nextX", "extra", "calculateExtraSpace", "leftSideEdges", "rightSideEdges", "fallAngleAdjustment", "prevSeg", "yDifference", "segmentNumber", "prevSegmentPerOrderPerTrack", "assignmentByOrderAndNode", "trackNo", "addToAssignment", "generateSingleLaneAssignment", "segmentID", "assignment", "getIdealLanesAndCoords", "currentLane", "potentialAdjustmentValues", "prevNameIsNull", "prevTrack", "compareByIdealLane", "adjustVertically", "verticalAdjustment", "minAdjustmentCost", "moveBy", "getVerticalAdjustmentCost", "adjustBy", "assignmentNode", "aNode", "nodeStart", "nodeEnd", "feature", "line", "allAreFour", "NARROW_WIDTH", "WIDE_WIDTH", "getReadXStart", "offset", "clampedXCoordinateOfBaseWithinNode", "getReadXEnd", "cover", "getXCoordinateOfBaseWithinNode", "base", "nodeLeftX", "nodeRightX", "nodePixelCoordinatesInX", "clamped", "xStart", "xEnd", "yStart", "yEnd", "trackColor", "trackAlpha", "dummy", "reversalFlag", "orderStartX", "orderEndX", "colorHighlight", "colorForCurrentHighlight", "createFeatureRectangle", "generateTurnaround", "COARSENED_ID_BASE", "buildCoarsenedSyntheticBands", "source", "unit", "coarsenedEdgeMeta", "edges", "ignoreStrand", "config", "item", "seq", "i", "sSigned", "dSigned", "sIdx", "dIdx", "srcNode", "nodes", "dstNode", "key", "a", "b", "existing", "maxEdgeCount", "e", "BAND_MIN_WIDTH", "READ_WIDTH", "BAND_MAX_WIDTH", "widthForCount", "c", "synthetic", "edge", "id", "COARSENED_ID_BASE", "label", "srcLen", "tallestName", "tallestH", "n", "DEBUG", "createFeatureRectangle", "node", "nodeXStart", "nodeXEnd", "highlight", "track", "rectXStart", "yStart", "trackColor", "reversalFlag", "currentHighlight", "co", "featureXStart", "featureXEnd", "nodeWidth", "feature", "generateTrackColor", "shapes", "MIN_BEND_WIDTH", "generateTurnaround", "dir", "x", "yEnd", "trackWidth", "trackID", "order", "type", "trackName", "extra", "extraRight", "extraLeft", "offset", "apex", "radius", "stem", "yTop", "yBottom", "stubNear", "stubFar", "horizontal", "segTop", "inner", "outer", "d", "nodePixelCoordinatesInX", "nodeLeftX", "nodeRightX", "generateNodeWidth", "charWidth", "len", "mergeNodes", "nodeName", "nodeName2", "predSets", "succSets", "tracksAndReads", "reads", "tracks", "isReverse", "forward", "nodeMap", "reverse", "pred", "s", "succ", "sortedNodes", "compareNodesByOrder", "mergeOffset", "mergeOrigin", "offsetOf", "originOf", "origin", "predecessor", "mergeableWithPred", "nodeByName", "read", "sequenceNew", "mergeableWithSucc", "predLength", "mismatch", "firstEntry", "firstOffset", "donor", "nodeIndex", "index", "predecessorIndex", "successor", "successorIndex", "filterReads", "readList", "focusNames", "groupBy", "items", "key", "groups", "item", "k", "group", "compareCurvesByXYStartValue", "a", "b", "curvePaths", "curves", "type", "groupedCurves", "curve", "curveGroup", "adjustValue", "adjustIncrement", "xAdjusted", "xNextAdjusted", "d", "groupKeys", "aParts", "bParts", "a0", "a1", "b0", "b1", "flattenedGroups", "nodeOutlinePath", "node", "x", "y", "visitStrands", "graph", "strands", "segment", "visits", "visit", "key", "list", "tubeMapTracks", "nodeById", "n", "paths", "path", "index", "reference", "p", "pathOrigin", "visitName", "seen", "sequence", "id", "node", "k", "strand", "canonicalStrand", "stepName", "tubeMapNodes", "hasTubeMapPaths", "STACK_PX", "MIN_TUBE_PX", "MAX_TUBE_PX", "tubeWidth", "trackCount", "runTubeMap", "tracks", "reads", "tubeMapReads", "tubeColor", "pathGreyCssColor", "pathCssColor", "pathColors", "_", "layout", "layoutTubeMap", "track", "readColor", "drawnNodes", "nodes", "centreline", "yOffset", "x0", "x1", "y", "extentOf", "tubeMapLayout", "run", "nodePositions", "minY", "maxY", "tubeMapColumns", "byOrder", "columns", "end", "order", "a", "b", "bp0", "bp1", "graphNode", "isBackbone", "first", "c", "column", "pathsReachBackbone", "tubeMapReferenceLayout", "sampleOf", "path", "panSNSample", "labelOf", "stepsOf", "pieces", "lengthOf", "steps", "end", "piece", "bp", "sum", "id", "stepSpans", "spans", "pos", "len", "sliceBetween", "span", "region", "flanked", "i0", "i1", "bestEnd", "bestStart", "i", "s", "ids", "walkRows", "graph", "unit", "paths", "isReference", "p", "pathOrigin", "referencePieces", "a", "first", "others", "walks", "n", "referenceStart", "cut", "rowOf", "ordered", "b", "complete", "from", "to", "contig", "panSNContig", "axis", "runs", "offReferenceBp", "gapBp", "step", "last", "at", "forward", "backward", "READOUT_ROOM", "walkRowsExtent", "walks", "longest", "row", "walkRowLayout", "graph", "region", "backbone", "backboneNodes", "walkRows", "overWindow", "n", "overlapsWindow", "nodePositions", "backbonePositions", "rowLabels", "FORCE_LAYOUT_LABEL", "hasBackbone", "graph", "isBackbone", "hasWalks", "hasTubeMapBackbone", "hasTubeMapPaths", "hasAlleles", "isOffReference", "LAYOUT_MODES", "anchoredLayout", "sampleRowLayout", "walkRowLayout", "orderedLayout", "tubeMapLayout", "tubeMapReferenceLayout", "LAYOUT_MODE_VALUES", "m", "modeUsesLayoutEngine", "value", "layoutModeByValue", "REPEAT_GROWTH", "coveringRepeat", "b", "repeats", "span", "growth", "r", "SUPERBUBBLE_SEGMENTS", "formatBp", "bp", "formatBpRange", "shortest", "longest", "low", "high", "unit", "routes", "count", "SATURATED_PATH_COUNT", "classifyBubble", "c", "classifyShape", "refSpan", "pathCount", "segmentCount", "inversion", "range", "repeat", "name", "alleles", "BUBBLE_KIND_COLORS", "BUBBLE_KIND_NAMES", "bubbleSegmentIds", "facetGrid", "count", "bounds", "pixelRows", "width", "room", "columns", "tile", "across", "rows", "panelWidth", "panelRoom", "fitX", "fitY", "scale", "drawn", "height", "best", "grid", "facetSettingOf", "facet", "raw", "domainFirst", "names", "domain", "rank", "name", "i", "listed", "a", "b", "facetCells", "by", "wrapOrder", "wrap", "places", "sample", "haplotype", "contig", "n", "p", "cells", "el", "tag", "attrs", "children", "c", "escapeText", "s", "escapeAttr", "serializeEl", "node", "v", "k", "ON_REFERENCE", "OFF_REFERENCE", "OUTSIDE_CUT", "GENE_INK", "BAR_PX", "GAP_PX", "MIN_TILE_PX", "kb", "bp", "units", "unit", "walkRowReadout", "row", "reference", "call", "gapBp", "complete", "delta", "against", "outside", "called", "readoutPlacement", "text", "endX", "width", "x", "unitTicks", "pxPerBp", "ticks", "k", "runPaint", "run", "ramp", "REFERENCE_RAMP_ALT_CSS", "stops", "rampStops", "rampHueCss", "alongRow", "axis", "start", "end", "rowSpan", "placeRowGenes", "rows", "byRow", "placed", "genes", "clip", "g", "e", "a", "b", "GENE_FONT_PX", "GENE_CHAR_PX", "MIN_LETTERED_BAR_PX", "genePad", "rowPx", "barPx", "boxesGenes", "rowGeneBoxes", "X", "y", "pad", "boxes", "taken", "named", "w", "half", "at", "box", "rowsText", "n", "walkRowsKey", "bars", "o", "entries", "CALL_TICK", "UNBACKED_TICK", "DISAGREES", "walkRowsKeyTree", "parts", "swatch", "el", "coalesceRuns", "runs", "minBp", "out", "bucket", "flush", "on", "off", "r", "onReference", "lead", "held", "geneTree", "walkRowsTree", "frame", "Y", "origin", "along", "offset", "prefix", "rowTree", "i", "paint", "h", "id", "rect", "color", "readouts", "PAD_PX", "WALK_STRIP_CEILING_PX", "LABEL_FONT_PX", "MIN_ROW_PX", "LABELLED_ROW_PX", "READOUT_ROOM", "MARK_INK", "TICK_PX", "stripMarks", "graph", "rows", "node", "visits", "marks", "row", "axis", "path", "pathOrigin", "v", "start", "end", "alongRow", "spanCache", "spansOf", "name", "byName", "lengthOf", "n", "pieces", "p", "spans", "piece", "a", "b", "pos", "id", "len", "segmentAt", "offset", "pxPerBp", "at", "tol", "bar", "from", "to", "lo", "hi", "mid", "best", "bestLen", "bestHolds", "i", "holds", "walkStripFrame", "bars", "o", "maxHeight", "labelPx", "r", "count", "rowPx", "labelled", "readouts", "barPx", "longest", "left", "scaleX", "boxesGenes", "stripRowAt", "frame", "x", "y", "walkMarksTree", "index", "X", "el", "m", "w", "tick", "top", "walkStripLabelsTree", "bold", "stripGeneGaps", "gaps", "import_colorBits", "import_canvas2dUtils", "import_renderingBackendBase", "Canvas2DRenderer", "width", "height", "batch", "factors", "edgeIndex", "factor", "transform", "clearColor", "ctx", "t", "hl", "color", "items", "groupByPaint", "e", "i", "inRun", "brightenAbgr", "first", "c", "lifted", "nodeId", "run", "s", "p0", "l", "p", "a", "tip", "left", "notch", "right", "arrowheadOutline", "ARROW_NOTCH", "ux", "uy", "tipX", "tipY", "length", "halfWidth", "backX", "backY", "notchBack", "paintOf", "groups", "deferred", "item", "weight", "last", "into", "key", "group", "import_colorBits", "WALK_FIELDS", "WALK_SCHEMES", "FAMILIES", "isField", "v", "f", "isScheme", "s", "resolveEncoding", "layer", "reference", "picked", "alone", "askedField", "askedScheme", "field", "family", "scheme", "BASE_T", "toLinear", "c", "toGamma", "oklab", "hex", "r", "g", "b", "i", "l", "m", "oklabToRgb", "L", "a", "schemeRgb", "x", "hslToRgb", "REFERENCE_RAMP_MAX_HUE", "from", "to", "schemeCss", "schemeColor", "encodedColor", "t", "NO_VALUE_COLOR", "REFERENCE_RAMP_ALT_COLOR", "encodingStops", "_", "encodingSwatchCss", "encoding", "stops", "rangeText", "contig", "start", "end", "s", "e", "groupDigits", "formatBp", "walkKey", "walk", "reference", "field", "own", "scale", "spanned", "outside", "walkPosition", "nodeId", "length", "progress", "range", "import_colorBits", "REFERENCE_STRIP_PX", "REFERENCE_STRIP_ZONE_PX", "OVERHANG_PX", "OVERHANG_CAP_PX", "MIN_STAGGER_PX", "referenceStripBlocks", "graph", "colorScheme", "referenceRamp", "walks", "range", "computeColorSchemeRange", "out", "node", "index", "isBackbone", "colors", "w", "lane", "LIFT_BACKDROP_CSS", "getNodeColor", "b", "span", "frame", "bp0", "bp1", "a", "HIT_SLOP_PX", "stripOverhang", "blocks", "width", "lo", "hi", "below", "above", "block", "stripBlockAt", "sx", "sy", "hit", "best", "x0", "x1", "dist", "nodeAnchor", "segments", "toScreen", "p", "stripPixels", "dpr", "placed", "px0", "claimed", "tierEnds", "px1", "start", "tier", "_", "drawOverhang", "ctx", "edgeX", "inward", "ink", "capX", "tipX", "baseX", "rowEdge", "row", "rows", "drawReferenceStrip", "lit", "darkMode", "staggered", "color", "i", "y0", "overhang", "left", "referenceBoxes", "graph", "layout", "nodeById", "n", "byRefName", "node", "graphNode", "isBackbone", "refName", "start", "boxes", "a", "b", "tubeX", "bp", "isEnd", "lo", "hi", "mid", "bp1", "box", "span", "tubeSpan", "end", "rulerBoxes", "best", "TICK_PX", "LABEL_GAP_PX", "TARGET_TICK_PX", "MIN_BRACKET_PX", "rulerInk", "darkMode", "niceStep", "raw", "pow", "m", "placeLabels", "candidates", "placed", "text", "at", "scaleMarks", "x", "width", "measure", "first", "last", "from", "to", "bpSpan", "step", "ticks", "positions", "sx", "boxMarks", "shown", "s0", "s1", "label", "position", "spans", "lengths", "boundaries", "length", "formatBp", "ends", "rulerMarks", "referenceAxis", "drawTubeMapRuler", "ctx", "frame", "top", "marks", "ink", "y", "x0", "x1", "referenceNodes", "graph", "layout", "nodeById", "n", "out", "node", "graphNode", "isBackbone", "tubeMapConnectors", "nodes", "bpToScreen", "tubeToScreen", "width", "bottom0", "bottom1", "HIT_SLOP_PX", "connectorAt", "connectors", "zoneBottom", "sx", "sy", "t", "hit", "best", "c", "a", "b", "dist", "band", "ctx", "bottom", "sides", "drawTubeMapConnectors", "highlightNode", "darkMode", "ink", "lit", "boxesOf", "graph", "layout", "nodeById", "n", "boxes", "node", "index", "g", "isBackbone", "bp0", "a", "b", "boxAt", "bp", "visited", "lo", "hi", "mid", "box", "deviationMarks", "deviations", "marks", "track", "devs", "segment", "d", "bp1", "at", "deviationInk", "darkMode", "drawDeviationMarks", "ctx", "x", "y", "width", "m", "left", "right", "top", "readMismatches", "read", "layout", "marks", "entries", "entry", "i", "nodeIndex", "forward", "node", "pathIndex", "y", "base", "at", "pos", "getXCoordinateOfBaseWithinNode", "mm", "x0", "softClip", "x1", "tubeMapMismatches", "parsePath", "d", "tokens", "commands", "i", "num", "op", "extentOf", "x0", "x1", "c", "shapeOf", "color", "alpha", "track", "layerOf", "layout", "type", "shapes", "trackOf", "s", "rects", "r", "curves", "curvePaths", "corners", "tubeMapPicture", "nodes", "node", "nodeOutlinePath", "tubeMapMismatches", "m", "trace", "ctx", "x", "y", "cx", "cy", "fillByColor", "items", "visible", "colorOf", "addTo", "byColor", "item", "key", "list", "FULL_OUTLINE_PX", "MIN_OUTLINE_ALPHA", "outlineAlpha", "screenWidth", "NODE_TINT_ALPHA", "drawTubeMap", "picture", "frame", "width", "layer", "left", "top", "stroke", "fill", "lit", "tint", "drawMismatches", "MISMATCH_FONT_PX", "MIN_MISMATCH_FONT_PX", "mismatchesLegible", "yScale", "mismatchOnScreen", "marks", "darkMode", "fontPx", "tubeMapGenes", "byRefName", "genes", "out", "gene", "boxes", "first", "last", "start", "end", "tubeSpan", "e", "GENE_ROW_PX", "MAX_GENE_ROWS", "EXON_PX", "NAME_FONT", "NAME_GAP_PX", "tubeMapGeneRows", "edges", "g", "a", "b", "depth", "most", "step", "drawTubeMapGenes", "ctx", "frame", "bottom", "top", "x", "width", "darkMode", "rows", "rowEnds", "ink", "placed", "s0", "s1", "arrow", "label", "mid", "half", "left", "right", "row", "glyphY", "e0", "agrees", "walkBp", "callBp", "nearest", "walks", "calls", "W", "C", "cost", "paired", "i", "j", "together", "dropWalk", "dropCall", "out", "byLength", "items", "bp", "item", "a", "b", "backed", "call", "scored", "row", "pairSample", "rows", "taken", "c", "unphased", "ranked", "r", "stages", "pool", "group", "free", "open", "pick", "k", "withCalls", "bySample", "sample", "called", "import_jsx_runtime", "Settings_default", "_jsx"]
7
7
  }