mermaid
Version:
Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.
8 lines • 171 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../src/rendering-util/layout-algorithms/elk/render.ts", "../../../src/rendering-util/layout-algorithms/elk/find-common-ancestor.ts", "../../../src/rendering-util/layout-algorithms/elk/lineHops.ts", "../../../src/rendering-util/layout-algorithms/elk/elkOptionCatalogue.ts", "../../../src/rendering-util/layout-algorithms/elk/geometry.ts"],
"sourcesContent": ["import {\n createCommonLayoutRenderer,\n defaultMeasureLayout,\n type CommonLayoutRenderContext,\n} from '../common/index.js';\nimport type { LayoutData } from '../../types.js';\nimport { setConfig } from '../../../diagram-api/diagramAPI.js';\n// @ts-ignore TODO: Investigate D3 issue\nimport { curveLinear } from 'd3';\nimport ELK from 'elkjs/lib/elk.bundled.js';\nimport { type TreeData, findCommonAncestor } from './find-common-ancestor.js';\nimport { applyElkLineJumps } from './lineHops.js';\nimport { clusterPaintsTitle } from '../../rendering-elements/clusters.js';\nimport { markerOffsets, markerOffsets2 } from '../../../utils/lineWithOffset.js';\nimport {\n EDGE_ROUTING_OPTIONS,\n PLACEMENT_OPTIONS,\n ROOT_EXPERIMENT_OVERRIDES,\n SUBGRAPH_EXPERIMENT_OVERRIDES,\n} from './elkOptionCatalogue.js';\n\nimport {\n type P,\n type RectLike,\n outsideNode,\n computeNodeIntersection,\n outlineAttachPoint,\n replaceEndpoint,\n onBorder,\n} from './geometry.js';\n\ntype Node = LayoutData['nodes'][number];\ntype Edge = LayoutData['edges'][number];\n\ninterface LabelData {\n width: number;\n height: number;\n wrappingWidth?: number;\n}\n\ninterface ElkNodeOffset {\n posX: number;\n posY: number;\n x: number;\n y: number;\n depth: number;\n width: number;\n height: number;\n}\n\ninterface NodeWithVertex {\n id: string;\n dir?: string;\n height?: number;\n intersect?: (point: P) => P | null;\n isGroup?: boolean;\n /**\n * Where ELK put this container, kept when `evenGroupFrames` moves the drawn\n * frame. Edge sections resolve against this, never against the moved frame.\n */\n elkOrigin?: { posX: number; posY: number };\n padding?: number;\n parentId?: string;\n shape?: string;\n width?: number;\n x?: number;\n y?: number;\n [key: string]: any;\n children?: NodeWithVertex[];\n labelData?: LabelData;\n labels?: { text?: string; width: number; height: number }[];\n layoutOptions?: Record<string, unknown>;\n offset?: ElkNodeOffset;\n}\n\ninterface ElkSubgraphConfig {\n mergeEdges?: boolean;\n straightenEdges?: boolean;\n preset?: string;\n layeringStrategy?: string;\n layeringLayerBound?: number;\n nodePlacementAlignment?: string;\n nodePlacementStrategy?: string;\n cycleBreakingStrategy?: string;\n}\n\ninterface ElkPreparedLayout {\n algorithm?: string;\n}\n\ninterface ElkLayoutContext {\n algorithm?: string;\n /**\n * Extra root-graph `layoutOptions`, merged last over\n * {@link createRootElkGraph}'s defaults.\n *\n * NOT user-facing config: nothing in `config.schema.yaml` writes it and\n * production `render()` never sets it. It exists so the DDLT configuration\n * sweep can try ELK options that are currently hardcoded here \u2014 spacings,\n * edge routing, node placement \u2014 WITHOUT forking the layout pipeline. A\n * sweep that reimplemented `createRootElkGraph` would be measuring a graph\n * the browser never builds, which is the exact failure the single-pipeline\n * rule exists to prevent.\n *\n * Promote a winning option to a real default in `createRootElkGraph`, or to\n * a `config.elk.*` key if it should be author-controlled. Do not reach for\n * this from product code.\n */\n rootLayoutOptions?: Record<string, unknown>;\n common: { lineBreakRegex: RegExp };\n getConfig: () => any;\n interpolateToCurve: (interpolate: string | undefined, defaultCurve: unknown) => unknown;\n log: {\n debug: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n };\n}\n\ninterface ElkLayoutState {\n elkGraph: any;\n nodeDb: Record<string, NodeWithVertex>;\n parentLookupDb: TreeData;\n}\n\ninterface ElkLayoutResult {\n children?: any[];\n edges?: any[];\n}\n\ntype Side = 'start' | 'end';\n\nconst MIN_END_MARKER_SEGMENT_LENGTH = 8;\n\n/**\n * How far `getLineFunctionsWithOffset` pulls a path end back along its last\n * segment to make room for this marker. Read from the same tables the edge\n * drawing uses, so a marker added there cannot be missed here \u2014 a missed one\n * leaves a stub shorter than the offset, and the pull-back then runs past the\n * previous point and flips the marker around.\n */\nconst markerPathOffset = (arrowType: unknown): number => {\n if (typeof arrowType !== 'string') {\n return 0;\n }\n return Math.max(\n (markerOffsets as Record<string, number>)[arrowType] ?? 0,\n (markerOffsets2 as Record<string, number>)[arrowType] ?? 0\n );\n};\n\n/**\n * `[arrowTypeStart, arrowTypeEnd]` per edge type.\n *\n * \"No arrowhead on this end\" is spelled `none`, which `addEdgeMarker` treats as\n * a deliberate absence. Spelling it `arrow_open` \u2014 the edge *type* meaning \"no\n * arrowheads\" \u2014 made it warn `Unknown arrow type: arrow_open` once per edge,\n * because that string is not a marker name. Diagrams whose db sets\n * `arrowTypeStart` itself (flowchart) never reached this fallback; state\n * diagrams, which do not, warned on every edge.\n */\nconst ARROW_MAP: Record<string, [string, string]> = {\n arrow_open: ['none', 'none'],\n arrow_cross: ['none', 'arrow_cross'],\n double_arrow_cross: ['arrow_cross', 'arrow_cross'],\n arrow_point: ['none', 'arrow_point'],\n double_arrow_point: ['arrow_point', 'arrow_point'],\n arrow_circle: ['none', 'arrow_circle'],\n double_arrow_circle: ['arrow_circle', 'arrow_circle'],\n};\n\n/**\n * Margin reserved at the ends of each side of a node, so that a port cannot be\n * placed on a corner. `alignDegenerateNodeToAnchor` treats a side shorter than\n * twice this as having no usable anchor span, so the option string below is\n * built from it \u2014 the two must not be able to disagree.\n */\nconst PORTS_SURROUNDING_MARGIN = 12;\n/** The margin spelled as an ELK margin, because `spacing.portsSurrounding` takes one. */\nconst PORTS_SURROUNDING = `[top=${PORTS_SURROUNDING_MARGIN},left=${PORTS_SURROUNDING_MARGIN},bottom=${PORTS_SURROUNDING_MARGIN},right=${PORTS_SURROUNDING_MARGIN}]`;\n/** Padding between a subgraph frame and its children. ELK's own default is 12. */\nconst SUBGRAPH_PADDING = 24;\n/**\n * Default `spacing.baseValue` for a subgraph that has no algorithm of its own.\n *\n * Every unset spacing derives from this, which is why it used to be 50: the\n * gap ELK derives for an edge approaching a node comes out at roughly half,\n * and below about 40 the approach ran shorter than the 10px arrowhead, so the\n * turn read as happening underneath it.\n *\n * Paying for that approach out of the base value overcharged everything else.\n * An edge routed down the inside of a frame claims a lane the same width, so a\n * group with a couple of them was pushed 50px clear of its own border on that\n * side and nowhere else \u2014 visible as a subgraph padded on one side only, for\n * no reason a reader can see.\n *\n * The two are now set separately: this stays tight, and\n * `elk.layered.spacing.edgeNodeBetweenLayers` buys the approach on its own.\n * An earlier note here claimed ELK ignored an explicit edge-node spacing \"in\n * every key form\"; it does honour the layered-scoped key, and the attempt that\n * failed had used `elk.layered.spacing.edgeEdgeBetweenLayers`, which is\n * edge-to-edge and a different quantity.\n */\nconst DEFAULT_SUBGRAPH_SPACING_BASE_VALUE = 24;\n/**\n * Gap between two sibling nodes in a subgraph.\n *\n * Also used to derive from `spacing.baseValue`, so lowering that pulled a\n * group's nodes together until they tripped the validator's\n * `node-node-padding` rule \u2014 three fixtures went invalid on it. 50 is what the\n * old base value yielded, restored here so the base value is free to be small.\n *\n * Deliberately spelled the same way as the `elk.rectpacking` override in\n * `RECTPACKING_OPTIONS`: ELK reads `spacing.nodeNode` and `elk.spacing.nodeNode`\n * as the same option, so using both forms would leave a rectpacking container\n * carrying two values for it and no say in which one won.\n */\nconst DEFAULT_SUBGRAPH_NODE_SPACING = 50;\n/** Inner padding reserved around a container that runs its own algorithm. */\nconst CONTAINER_PADDING = 15;\n/** Same, for `elk.rectpacking`, which packs tighter. */\nconst RECTPACKING_CONTAINER_PADDING = 10;\n\n/**\n * Shared layout options for elk.rectpacking \u2014 applied at both root level\n * and per-group level to reduce wasted space.\n * trybox: attempt box-like packing first for tighter results.\n * SCANLINE: width approximation scans node sizes instead of using a fixed target.\n * EQUAL_BETWEEN_STRUCTURES: distributes remaining whitespace evenly between children.\n */\nconst RECTPACKING_OPTIONS: Record<string, string | number> = {\n 'spacing.baseValue': 15,\n 'spacing.nodeNode': 15,\n 'elk.aspectRatio': '1.6',\n 'elk.expandNodes': 'true',\n 'elk.rectpacking.trybox': 'true',\n 'elk.rectpacking.packing.compaction.rowHeightReevaluation': 'true',\n 'elk.rectpacking.packing.compaction.iterations': 10,\n 'elk.rectpacking.whiteSpaceElimination.strategy': 'EQUAL_BETWEEN_STRUCTURES',\n 'elk.rectpacking.widthApproximation.strategy': 'SCANLINE',\n};\n\n/**\n * Every option `buildSubgraphLayoutOptions` sets or overrides *because* a\n * container asked for its own algorithm. When cross-boundary edges force the\n * container back onto the inherited algorithm, all of these have to go \u2014 they\n * are not inert under `elk.layered`, so leaving them behind produced a hybrid\n * rather than the documented fallback. `nodeSize.*` is then re-applied with the\n * plain-subgraph title floor by the caller (`groupTitleSizeOptions`).\n */\nconst CONTAINER_ALGORITHM_OVERRIDES = [\n 'nodeSize.constraints',\n 'nodeSize.minimum',\n 'elk.algorithm',\n 'elk.aspectRatio',\n 'elk.contentAlignment',\n 'elk.expandNodes',\n 'elk.padding',\n ...Object.keys(RECTPACKING_OPTIONS),\n];\n\n/**\n * Undo the algorithm-scoped options on a container, restoring the values a\n * plain subgraph would have had.\n */\nexport function clearContainerAlgorithmOptions(layoutOptions: Record<string, unknown>): void {\n for (const key of CONTAINER_ALGORITHM_OVERRIDES) {\n delete layoutOptions[key];\n }\n // `spacing.baseValue` and `spacing.nodeNode` are base options that the\n // rectpacking overrides stomp on, so restore the defaults rather than leaving\n // them unset. Missing the second one would silently hand the container back\n // ELK's own node spacing instead of ours.\n layoutOptions['spacing.baseValue'] = DEFAULT_SUBGRAPH_SPACING_BASE_VALUE;\n layoutOptions['spacing.nodeNode'] = DEFAULT_SUBGRAPH_NODE_SPACING;\n}\n\n/**\n * ELK algorithm ids a container may select through `@{ algorithm: \u2026 }`.\n *\n * The value comes from user-authored diagram metadata and would otherwise be\n * handed to ELK verbatim; an id ELK doesn't know aborts the whole layout and\n * blanks the diagram. Anything outside this list is ignored with a warning, so\n * a typo degrades to the default layout instead of losing the render.\n */\nconst CONTAINER_ALGORITHMS = new Set([\n 'elk.layered',\n 'elk.box',\n 'elk.rectpacking',\n 'elk.stress',\n 'elk.force',\n 'elk.mrtree',\n 'elk.radial',\n 'elk.sporeOverlap',\n]);\n\n/**\n * Resolve a container's requested layout algorithm, or `undefined` when the\n * request is absent, not a string, or not a supported ELK algorithm.\n */\nexport function resolveContainerAlgorithm(\n requested: unknown,\n log?: ElkLayoutContext['log']\n): string | undefined {\n if (typeof requested !== 'string') {\n return undefined;\n }\n if (!CONTAINER_ALGORITHMS.has(requested)) {\n log?.warn(\n `Unknown container layout algorithm \"${requested}\". Supported values: ${[...CONTAINER_ALGORITHMS].join(', ')}. Falling back to the diagram's layout algorithm.`\n );\n return undefined;\n }\n return requested;\n}\n\nexport function dir2ElkDirection(dir: unknown): 'RIGHT' | 'LEFT' | 'DOWN' | 'UP' {\n switch (dir) {\n case 'LR':\n return 'RIGHT';\n case 'RL':\n return 'LEFT';\n case 'TB':\n case 'TD': // TD is an alias for TB in Mermaid\n return 'DOWN';\n case 'BT':\n return 'UP';\n default:\n return 'DOWN';\n }\n}\n\ninterface GroupTitleNode {\n shape?: string;\n labelData?: LabelData;\n labels?: { width?: number }[];\n padding?: number;\n}\n\n/**\n * The width the frame painter needs for the group's title, or 0 for cluster\n * shapes that paint no title (a note group's label is the note's text, which\n * the note node inside it paints; reserving it would size the frame for text\n * that never appears there).\n */\nfunction groupTitleWidth(node: GroupTitleNode): number {\n if (!clusterPaintsTitle(node.shape)) {\n return 0;\n }\n // Match the frame painter's label width plus total horizontal padding.\n return (node.labelData?.width ?? node.labels?.[0]?.width ?? 0) + (node.padding ?? 0);\n}\n\n/**\n * ELK options reserving the painted title width before routing. Empty for\n * cluster shapes that paint no title, so they keep ELK's default sizing.\n */\nfunction groupTitleSizeOptions(node: GroupTitleNode): Record<string, string> {\n if (!clusterPaintsTitle(node.shape)) {\n return {};\n }\n return {\n 'nodeSize.constraints': '[MINIMUM_SIZE, NODE_LABELS]',\n 'nodeSize.minimum': `(${groupTitleWidth(node)}, 0)`,\n };\n}\n\nexport function buildSubgraphLayoutOptions(\n node: {\n dir?: string;\n shape?: string;\n padding?: number;\n labelData?: LabelData;\n metadata?: { algorithm?: unknown } & Record<string, unknown>;\n },\n elkConfig: ElkSubgraphConfig | undefined,\n algorithm: string | undefined,\n log?: ElkLayoutContext['log']\n): Record<string, unknown> {\n // Every group gets its painted title width as a floor via\n // `groupTitleSizeOptions` below. Containers that run their own algorithm\n // override that floor with this wider, label-plus-both-paddings minimum.\n const labelW = node.labelData?.width ?? 0;\n const pad = node.padding ?? 0;\n const minWidth = labelW + 2 * pad;\n const labelH = node.labelData?.height ?? 0;\n // Hoisted, as in `createRootElkGraph`: the two functions resolve the same\n // preset for the same reasons, and reading alike is most of what keeps them\n // from drifting apart again.\n const preset = resolveElkPreset(elkConfig?.preset);\n\n const layoutOptions: Record<string, unknown> = {\n // Reserve the painted title width before routing. Enlarging a frame after\n // ELK has placed its ports leaves those ports inside the painted border.\n ...groupTitleSizeOptions(node),\n 'spacing.baseValue': DEFAULT_SUBGRAPH_SPACING_BASE_VALUE,\n // The straight run an edge gets before the node it enters, bought on its\n // own rather than out of `spacing.baseValue` \u2014 see the note there. This is\n // the layered-scoped key; the unscoped `spacing.edgeNodeBetweenLayers` is\n // not an ELK id at all and setting it does nothing.\n //\n // 30, which is where the approach run stops improving: 40 measured the same\n // 30px shortest approach and only widened the lane this value also pays\n // for. That lane used to be the reason to go lower \u2014 the value is charged\n // TWICE against a group with an edge routed inside its frame, once between\n // the nodes and the lane and again between the lane and the frame, so the\n // group's extra width came out at exactly `36 + 2x`. `evenGroupFrames` now\n // pulls the frame in past the lane regardless, so a wider lane no longer\n // shows as lopsided padding and the only cost left is overall diagram size.\n //\n // Do NOT lower it further on that reasoning. Over the DDLT corpus this is\n // not monotonic: 30 and 40 leave one fixture invalid (the deliberate\n // merge-edge counterexample), while 20 leaves two and 25 leaves three \u2014\n // `right-angles-not-curves` starts tripping `edge-parallel-segment-too-close`\n // because this spacing also separates edges running alongside each other in\n // the layer gap. 30 is the lowest value that keeps the corpus clean.\n 'elk.layered.spacing.edgeNodeBetweenLayers': 30,\n // Separation between edges sharing a lane. Also raised off the base value,\n // so that lowering the base does not leave parallel edges touching.\n 'elk.spacing.edgeEdge': 20,\n // Node separation, likewise bought on its own \u2014 see the note on the constant.\n 'spacing.nodeNode': DEFAULT_SUBGRAPH_NODE_SPACING,\n // Breathing room between a frame and its children. Set explicitly rather\n // than left to ELK's default of 12. The top gets the same value as the\n // rest: ELK reserves the subgraph's own title strip on top of whatever is\n // given here, so adding the label height again double-counts it.\n 'elk.padding': `[top=${SUBGRAPH_PADDING},left=${SUBGRAPH_PADDING},bottom=${SUBGRAPH_PADDING},right=${SUBGRAPH_PADDING}]`,\n 'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]',\n\n 'elk.layered.mergeEdges': elkConfig?.mergeEdges,\n 'elk.layered.nodePlacement.bk.fixedAlignment':\n elkConfig?.nodePlacementAlignment ?? preset.alignment,\n // The preset resolves child placement separately from root placement.\n // Named presets retain their previous strategies; explicit options win.\n //\n // ONE key, fully qualified. ELK reads `nodePlacement.strategy` and\n // `elk.layered.nodePlacement.strategy` as the same option, so listing both\n // \u2014 as this did \u2014 leaves the container holding two values for it with no\n // say in which wins, and quietly ignores an explicit `nodePlacementStrategy`.\n 'elk.layered.nodePlacement.strategy':\n elkConfig?.nodePlacementStrategy ?? preset.containerPlacement,\n // Resolved here as well as at the root, because a container laid out on its\n // own never sees the root's value and falls back to ELK's default, GREEDY.\n // That reverses a different edge than the preset asked for, so a composite\n // containing a loop opens on whichever node greedy promoted to a source\n // rather than on its own start node. Same key and same resolution as the\n // root, so `legacy` reproduces the old rendering inside frames too.\n 'elk.layered.cycleBreaking.strategy': elkConfig?.cycleBreakingStrategy ?? preset.cycleBreaking,\n // PORT_POSITION lets a node shift so an edge can leave straight rather than\n // bending immediately off the port.\n 'elk.layered.nodePlacement.networkSimplex.nodeFlexibility': 'PORT_POSITION',\n // Keep a frame's ports off its own corners. See the note in\n // `createRootElkGraph`; a container is where this bites hardest, because a\n // cross-boundary edge attaches to the frame rather than to a node inside it.\n 'elk.spacing.portsSurrounding': PORTS_SURROUNDING,\n };\n\n // Apply per-group algorithm from metadata (e.g. @{algorithm: elk.box}).\n // SEPARATE_CHILDREN is required so the subgraph's algorithm actually\n // runs instead of being swallowed by the root INCLUDE_CHILDREN policy.\n const algo = resolveContainerAlgorithm(node.metadata?.algorithm, log);\n if (algo) {\n // These algorithms also need a minimum height for their title strip.\n const padTop = labelH + CONTAINER_PADDING;\n layoutOptions['nodeSize.constraints'] = '[MINIMUM_SIZE, NODE_LABELS]';\n // The minimum has to clear the whole reserved strip \u2014 the label plus the\n // padding above and below it \u2014 not just the label height, or a container\n // whose children are shorter than its own chrome comes out too short.\n layoutOptions['nodeSize.minimum'] = `(${minWidth}, ${padTop + CONTAINER_PADDING})`;\n layoutOptions['elk.algorithm'] = algo;\n layoutOptions['elk.hierarchyHandling'] = 'SEPARATE_CHILDREN';\n layoutOptions['elk.aspectRatio'] = '2.0';\n layoutOptions['elk.contentAlignment'] = 'H_CENTER V_TOP';\n layoutOptions['elk.expandNodes'] = 'true';\n // Reserve top padding for the label so children don't overlap it\n layoutOptions['elk.padding'] =\n `[top=${padTop},left=${CONTAINER_PADDING},bottom=${CONTAINER_PADDING},right=${CONTAINER_PADDING}]`;\n\n // Tighter spacing for rectpacking \u2014 uses smaller padding for nested containers.\n if (algo === 'elk.rectpacking') {\n const rectPadTop = labelH + RECTPACKING_CONTAINER_PADDING;\n Object.assign(layoutOptions, RECTPACKING_OPTIONS, {\n 'elk.padding': `[top=${rectPadTop},left=${RECTPACKING_CONTAINER_PADDING},bottom=${RECTPACKING_CONTAINER_PADDING},right=${RECTPACKING_CONTAINER_PADDING}]`,\n 'nodeSize.minimum': `(${minWidth}, ${rectPadTop + RECTPACKING_CONTAINER_PADDING})`,\n });\n }\n } else if (node.dir) {\n // Directional subgraph without explicit algorithm \u2014 run the parent layered\n // algorithm in the subgraph's own coordinate system.\n layoutOptions['elk.algorithm'] = algorithm;\n layoutOptions['elk.direction'] = dir2ElkDirection(node.dir);\n layoutOptions['elk.hierarchyHandling'] = 'SEPARATE_CHILDREN';\n }\n\n // Container-scoped experiments. Spacing, padding and label placement only\n // bite here \u2014 an option set on the root never reaches inside a frame.\n Object.assign(layoutOptions, SUBGRAPH_EXPERIMENT_OVERRIDES);\n\n return layoutOptions;\n}\n\n/**\n * Identify the entry node of each recursive flow so it can be pinned to the top.\n *\n * `elk.layered` must break cycles before it can rank nodes, and its default\n * cycle-breaking heuristic is purely degree-based \u2014 it has no notion of an\n * \"entry point\". So as soon as a flow loops back on itself (recursion), the\n * first-declared node can be ranked in the middle of the layout, scrambling the\n * reading order and hiding where the flow starts.\n *\n * For each container (grouped by `parentId`) we look only at edges internal to\n * that container and find its weakly-connected components. A component with no\n * natural source \u2014 no node with in-degree 0 once self-loops are ignored \u2014 must\n * contain a cycle. For such components we break cycles greedily in edge\n * declaration order: an edge that would close a directed cycle is treated as a\n * back-edge and skipped, and the entry is the first node in declaration order\n * that is a source of the remaining forward edges. Raw in-degree alone cannot\n * find it \u2014 a back-edge feeding the true entry hides it, and nominating by\n * node declaration order instead scrambles the layout (#79). Acyclic\n * components always have a source and nominate nothing, leaving their layout\n * untouched. The caller pins each nominee to the first layer with\n * `elk.layered.layering.layerConstraint = FIRST`.\n *\n * @param nodes - layout nodes in declaration order\n * @param edges - layout edges referencing node ids via `source`/`target`\n * @returns the ids of nodes to constrain to the first layer\n */\nexport function findCyclicEntryNodes(\n nodes: { id: string; parentId?: string }[],\n edges: { source?: string | number; target?: string | number }[]\n): Set<string> {\n const entries = new Set<string>();\n\n // Group node ids by container, preserving declaration order within each group.\n const groups = new Map<string | undefined, string[]>();\n for (const { id, parentId } of nodes) {\n const group = groups.get(parentId);\n if (group) {\n group.push(id);\n } else {\n groups.set(parentId, [id]);\n }\n }\n\n for (const ids of groups.values()) {\n const idSet = new Set(ids);\n const inDegree = new Map<string, number>(ids.map((id) => [id, 0]));\n // Undirected adjacency, used only to find weakly-connected components.\n const neighbors = new Map<string, string[]>(ids.map((id) => [id, []]));\n // Container-internal directed edges in declaration order, for the\n // cycle-breaking fallback below.\n const internalEdges: [string, string][] = [];\n\n for (const edge of edges) {\n const source = edge.source == null ? undefined : String(edge.source);\n const target = edge.target == null ? undefined : String(edge.target);\n // Restrict to edges internal to this container; ignore self-loops.\n if (!source || !target || source === target) {\n continue;\n }\n if (!idSet.has(source) || !idSet.has(target)) {\n continue;\n }\n inDegree.set(target, (inDegree.get(target) ?? 0) + 1);\n neighbors.get(source)!.push(target);\n neighbors.get(target)!.push(source);\n internalEdges.push([source, target]);\n }\n\n // Label weakly-connected components.\n const component = new Map<string, number>();\n let componentCount = 0;\n for (const id of ids) {\n if (component.has(id)) {\n continue;\n }\n const stack = [id];\n component.set(id, componentCount);\n while (stack.length > 0) {\n const current = stack.pop()!;\n for (const next of neighbors.get(current)!) {\n if (!component.has(next)) {\n component.set(next, componentCount);\n stack.push(next);\n }\n }\n }\n componentCount++;\n }\n\n // A component with no in-degree-0 node necessarily contains a cycle.\n const hasSource = new Array<boolean>(componentCount).fill(false);\n for (const id of ids) {\n if ((inDegree.get(id) ?? 0) === 0) {\n hasSource[component.get(id)!] = true;\n }\n }\n if (!hasSource.includes(false)) {\n continue;\n }\n\n // Recover each source-less component's entry by breaking cycles greedily\n // in edge declaration order: skip any edge that would close a directed\n // cycle (a back-edge). The surviving forward edges are acyclic, so every\n // component regains at least one source; nominate the first one in\n // declaration order.\n const forward = new Map<string, string[]>(ids.map((id) => [id, []]));\n const residualInDegree = new Map<string, number>(ids.map((id) => [id, 0]));\n const reaches = (from: string, to: string): boolean => {\n const seen = new Set<string>([from]);\n const stack = [from];\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (current === to) {\n return true;\n }\n for (const next of forward.get(current)!) {\n if (!seen.has(next)) {\n seen.add(next);\n stack.push(next);\n }\n }\n }\n return false;\n };\n for (const [source, target] of internalEdges) {\n if (reaches(target, source)) {\n continue;\n }\n forward.get(source)!.push(target);\n residualInDegree.set(target, (residualInDegree.get(target) ?? 0) + 1);\n }\n\n const nominated = new Array<boolean>(componentCount).fill(false);\n for (const id of ids) {\n const c = component.get(id)!;\n if (!hasSource[c] && !nominated[c] && residualInDegree.get(id) === 0) {\n entries.add(id);\n nominated[c] = true;\n }\n }\n }\n\n return entries;\n}\n\n/**\n * When `elk.keepEntryNodeOnTop` is enabled, pin each recursive flow's entry node\n * to the first layer so the diagram reads from its entry instead of an arbitrary\n * point in the loop. No-op when the option is off or the graph is acyclic, so\n * existing ELK diagrams are unaffected unless they opt in.\n */\nfunction applyCyclicEntryConstraint(\n data4Layout: LayoutData,\n nodeDb: Record<string, NodeWithVertex>\n): void {\n if (!data4Layout.config.elk?.keepEntryNodeOnTop) {\n return;\n }\n\n const entryNodeIds = findCyclicEntryNodes(\n data4Layout.nodes,\n data4Layout.edges.map((edge) => ({ source: edge.start, target: edge.end }))\n );\n\n for (const id of entryNodeIds) {\n const elkNode = nodeDb[id];\n if (elkNode) {\n elkNode.layoutOptions = {\n ...elkNode.layoutOptions,\n 'elk.layered.layering.layerConstraint': 'FIRST',\n };\n }\n }\n}\n\nexport function prepareLayoutForElk(\n data4Layout: LayoutData,\n context: CommonLayoutRenderContext<ElkPreparedLayout>\n): ElkPreparedLayout {\n const elkContext = getElkLayoutContext(context);\n syncHostConfig(elkContext);\n applyElkEdgeRenderData(data4Layout, elkContext);\n return { algorithm: elkContext.algorithm };\n}\n\nexport async function runElkLayoutCore(\n data4Layout: LayoutData,\n context: CommonLayoutRenderContext<ElkPreparedLayout>\n): Promise<ElkLayoutResult> {\n const elkContext = getElkLayoutContext(context);\n const layoutState = buildElkGraphFromLayoutData(data4Layout, elkContext);\n\n // @ts-ignore - ELK is not typed\n const elk = new ELK();\n elkContext.log.info('Drawing flowchart using v4 renderer', elk);\n\n const graph = await runElkLayout(elk, layoutState.elkGraph, elkContext.log);\n applyElkLayoutResult(data4Layout, graph, layoutState, elkContext.log);\n orderNodesForElkPaint(data4Layout.nodes);\n return graph;\n}\n\nexport function buildElkGraphFromLayoutData(\n data4Layout: LayoutData,\n elkContext: ElkLayoutContext\n): ElkLayoutState {\n const nodeDb: Record<string, NodeWithVertex> = {};\n const elkGraph = createRootElkGraph(\n data4Layout,\n elkContext.algorithm,\n elkContext.rootLayoutOptions\n );\n\n const dir = (data4Layout as { direction?: string }).direction ?? 'DOWN';\n elkGraph.layoutOptions['elk.direction'] = dir2ElkDirection(dir);\n\n const parentLookupDb = addSubGraphs(data4Layout.nodes, elkContext.log);\n addVertices(data4Layout.nodes, elkGraph, nodeDb, elkContext);\n addEdgesToElkGraph(data4Layout, elkGraph, nodeDb, elkContext);\n configureSubgraphNodes(data4Layout, nodeDb, parentLookupDb, elkContext);\n configureCrossHierarchyEdges(elkGraph, nodeDb, parentLookupDb, elkContext.log);\n applyCyclicEntryConstraint(data4Layout, nodeDb);\n\n return { elkGraph, nodeDb, parentLookupDb };\n}\n\nexport const render = createCommonLayoutRenderer<ElkLayoutResult, ElkPreparedLayout>({\n afterPaint: applyElkLineJumps,\n prepareLayout: prepareLayoutForElk,\n // ELK derives a compound node's minimum size from the measured cluster label,\n // so the label has to be measured the way `insertCluster` paints it \u2014\n // unwrapped \u2014 rather than at the 200px flowchart wrapping width. Requested\n // here rather than sniffed for in core: core has no business knowing which\n // layout it is running.\n measureLayout: (data4Layout, context) =>\n defaultMeasureLayout(data4Layout, context, { unwrapGroupLabels: true }),\n runLayoutCore: runElkLayoutCore,\n paintOptions: {\n skipIntersect: true,\n },\n});\n\n/**\n * Copy the host's config into whichever config module this bundle is using \u2014\n * except the secure keys, which `setConfig` strips.\n *\n * When this file is compiled into `@mermaid-js/layout-elk` the bundle carries\n * its own copy of the config module, and that copy never sees the host's\n * `initialize()` \u2014 so it reads schema defaults and paints, for example, edge\n * markers without `arrowMarkerAbsolute`. `setConfig` here resolves to the local\n * copy while `context.getConfig` comes from the host, so this repairs it.\n *\n * `setConfig` runs `sanitize()`, which deletes every key in\n * `['secure', ...siteConfig.secure]` \u2014 `securityLevel`, `startOnLoad`,\n * `maxTextSize`, `maxEdges`, `suppressErrorRendering` \u2014 so those never\n * propagate through this call. That is the safe direction: the plugin's local\n * copy stays at the schema default `strict` rather than inheriting a looser\n * host value. If the host's `securityLevel` ever genuinely needs to reach the\n * plugin's copy, `setSiteConfig` is the call that survives sanitization.\n *\n * Compiled into mermaid itself the two are the same module and this is a no-op.\n */\nfunction syncHostConfig(elkContext: ElkLayoutContext): void {\n setConfig(elkContext.getConfig());\n}\n\nfunction orderNodesForElkPaint(nodes: LayoutData['nodes']): void {\n const nodeById = new Map(nodes.map((node) => [node.id, node]));\n\n nodes.sort((a, b) => {\n if (a.isGroup !== b.isGroup) {\n return a.isGroup ? -1 : 1;\n }\n\n if (a.isGroup && b.isGroup) {\n return getGroupDepth(a, nodeById) - getGroupDepth(b, nodeById);\n }\n\n return 0;\n });\n}\n\nfunction getGroupDepth(\n node: LayoutData['nodes'][number],\n nodeById: Map<string, LayoutData['nodes'][number]>\n): number {\n let depth = 0;\n const visited = new Set<string>();\n let parentId = node.parentId;\n\n while (parentId && !visited.has(parentId)) {\n visited.add(parentId);\n const parent = nodeById.get(parentId);\n if (!parent?.isGroup) {\n break;\n }\n depth++;\n parentId = parent.parentId;\n }\n\n return depth;\n}\n\nfunction getElkLayoutContext(\n context: CommonLayoutRenderContext<ElkPreparedLayout>\n): ElkLayoutContext {\n const helpers = context.helpers;\n if (!helpers) {\n throw new Error('ELK layout requires Mermaid internal helpers');\n }\n\n return {\n algorithm:\n context.preparedLayout?.algorithm ??\n (context.options as { algorithm?: string } | undefined)?.algorithm,\n rootLayoutOptions: (\n context.options as { rootLayoutOptions?: Record<string, unknown> } | undefined\n )?.rootLayoutOptions,\n common: helpers.common,\n getConfig: helpers.getConfig,\n interpolateToCurve: helpers.interpolateToCurve as (\n interpolate: string | undefined,\n defaultCurve: unknown\n ) => unknown,\n log: helpers.log,\n };\n}\n\n/**\n * Scratch overrides for local experimentation. MUST be empty on `develop`.\n *\n * Spread last into the root graph's `layoutOptions`, so anything here wins over\n * the defaults above \u2014 including the keys wired to `config.elk.*`. That is the\n * point: edit one line, let the dev server rebuild, and compare renders without\n * touching a diagram's frontmatter or the config schema.\n *\n * It is also why this must not ship. An entry here silently disables the\n * matching user-facing option for every diagram, and the symptom \u2014 \"this config\n * key does nothing\" \u2014 gives no hint where to look. `elk.cycleBreakingStrategy`\n * was dead this way, and it took a bisect against the raw ELK option to notice.\n */\n/**\n * Presets supply layout choices only when the caller leaves them unspecified.\n * Root and container placement are independent, while both use the preset's\n * Brandes-Koepf alignment. Named non-default presets retain their earlier layout.\n */\nconst ELK_PRESETS: Record<\n string,\n {\n layering: string;\n placement: string;\n containerPlacement: string;\n alignment: string;\n cycleBreaking: string;\n }\n> = {\n // Balanced Brandes-Koepf centers simple branches and composite-state entries.\n // Layering and cycle breaking retain the release defaults.\n default: {\n layering: 'NETWORK_SIMPLEX',\n placement: 'BRANDES_KOEPF',\n containerPlacement: 'BRANDES_KOEPF',\n alignment: 'BALANCED',\n cycleBreaking: 'DEPTH_FIRST',\n },\n // Reproduce the layout before presets, including ELK's own greedy cycle\n // breaking rather than the greedy-model-order value advertised by the schema.\n legacy: {\n layering: 'NETWORK_SIMPLEX',\n placement: 'BRANDES_KOEPF',\n containerPlacement: 'BRANDES_KOEPF',\n alignment: 'NONE',\n cycleBreaking: 'GREEDY',\n },\n modelOrder: {\n layering: 'NETWORK_SIMPLEX',\n placement: 'NETWORK_SIMPLEX',\n containerPlacement: 'BRANDES_KOEPF',\n alignment: 'NONE',\n cycleBreaking: 'GREEDY_MODEL_ORDER',\n },\n // Preserve the previous default recipe for callers selecting it by name.\n depthFirst: {\n layering: 'NETWORK_SIMPLEX',\n placement: 'NETWORK_SIMPLEX',\n containerPlacement: 'BRANDES_KOEPF',\n alignment: 'NONE',\n cycleBreaking: 'DEPTH_FIRST',\n },\n};\n\n/**\n * Resolve a preset name, falling back to `default` for an unknown one.\n *\n * `Object.hasOwn` rather than a plain lookup: the schema's enum only guards the\n * config path, and a directive or a programmatic config can still put anything\n * here. `ELK_PRESETS['__proto__']` is truthy, so an indexed lookup would return\n * `Object.prototype` and every strategy read off it would come back `undefined`\n * \u2014 a silently strategy-less layout rather than the documented fallback.\n */\nexport function resolveElkPreset(name: string | undefined) {\n return name !== undefined && Object.hasOwn(ELK_PRESETS, name)\n ? ELK_PRESETS[name]\n : ELK_PRESETS.default;\n}\n\nfunction createRootElkGraph(\n data4Layout: LayoutData,\n algorithm: string | undefined,\n rootLayoutOptions?: Record<string, unknown>\n): any {\n const preset = resolveElkPreset(data4Layout.config.elk?.preset);\n const graph = {\n id: 'root',\n layoutOptions: {\n 'elk.hierarchyHandling': 'INCLUDE_CHILDREN',\n 'elk.algorithm': algorithm,\n 'elk.layered.nodePlacement.strategy':\n data4Layout.config.elk?.nodePlacementStrategy ?? preset.placement,\n 'elk.layered.nodePlacement.bk.fixedAlignment':\n data4Layout.config.elk?.nodePlacementAlignment ?? preset.alignment,\n 'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,\n 'elk.direction': 'DOWN',\n 'spacing.baseValue': 40,\n\n 'elk.layered.crossingMinimization.forceNodeModelOrder':\n data4Layout.config.elk?.forceNodeModelOrder,\n 'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,\n 'elk.layered.unnecessaryBendpoints': true,\n 'elk.layered.cycleBreaking.strategy':\n data4Layout.config.elk?.cycleBreakingStrategy ?? preset.cycleBreaking,\n 'elk.layered.layering.strategy': data4Layout.config.elk?.layeringStrategy ?? preset.layering,\n // Only COFFMAN_GRAHAM reads this; the others ignore it.\n 'elk.layered.layering.coffmanGraham.layerBound': data4Layout.config.elk?.layeringLayerBound,\n\n // 'spacing.nodeNode': 120,\n // 'spacing.nodeNodeBetweenLayers': 25,\n // 'spacing.edgeNode': 20,\n // 'spacing.edgeNodeBetweenLayers': 10,\n // 'spacing.edgeEdge': 10,\n // 'spacing.edgeEdgeBetweenLayers': 20,\n // 'spacing.nodeSelfLoop': 20,\n\n // Tweaking options\n 'elk.layered.wrapping.multiEdge.improveCuts': true,\n 'elk.layered.wrapping.multiEdge.improveWrappedEdges': true,\n 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY',\n 'elk.layered.mergeHierarchyEdges': true,\n // Reserve a margin at the ends of every side so a port cannot land on a\n // corner. ELK's default is 0, which permits it \u2014 and a corner is the one\n // boundary point with no side to leave from, so the edge came out of the\n // vertex and then ran ALONG the box's own edge before turning away. It\n // showed up on subgraphs first because a cross-boundary edge attaches to\n // the frame, which is large enough for the corner to be visible.\n //\n // Chosen at 12 by measurement, not taste: it is the smallest value that\n // clears the corner on the `elk-edge-cases` corpus. 30 was tried and\n // reorders layers, so this is not a free parameter \u2014 raising it changes\n // more than clearance.\n 'elk.spacing.portsSurrounding': PORTS_SURROUNDING,\n },\n children: [],\n edges: [],\n };\n\n // Optimize spacing when rectpacking is the root algorithm.\n if (algorithm === 'elk.rectpacking') {\n Object.assign(graph.layoutOptions, RECTPACKING_OPTIONS, {\n 'elk.contentAlignment': 'H_CENTER V_TOP',\n 'elk.padding': '[top=15,left=15,bottom=15,right=15]',\n });\n }\n\n // Last, so a sweep override beats every default above. See\n // `ElkLayoutContext.rootLayoutOptions` for why this exists.\n if (rootLayoutOptions) {\n Object.assign(graph.layoutOptions, rootLayoutOptions);\n }\n\n // Hand-run experiments from `elkOptionCatalogue.ts`, last so an option\n // switched on there wins over the preset, `config.elk.*` and the sweep.\n // MUST all be commented out on `develop` \u2014 this is the production path.\n Object.assign(\n graph.layoutOptions,\n PLACEMENT_OPTIONS,\n EDGE_ROUTING_OPTIONS,\n ROOT_EXPERIMENT_OVERRIDES\n );\n\n return graph;\n}\n\nfunction addSubGraphs(nodeArr: Node[], log: ElkLayoutContext['log']): TreeData {\n const parentLookupDb: TreeData = { parentById: {}, childrenById: {} };\n const subgraphs = nodeArr.filter((node) => node.isGroup);\n log.info('Subgraphs - ', subgraphs);\n subgraphs.forEach((subgraph) => {\n const children = nodeArr.filter((node) => node.parentId === subgraph.id);\n children.forEach((node) => {\n parentLookupDb.parentById[node.id] = subgraph.id;\n parentLookupDb.childrenById[subgraph.id] ??= [];\n parentLookupDb.childrenById[subgraph.id].push(node.id);\n });\n });\n\n return parentLookupDb;\n}\n\nfunction addVertices(\n nodeArr: Node[],\n graph: { children: NodeWithVertex[] },\n nodeDb: Record<string, NodeWithVertex>,\n elkContext: ElkLayoutContext,\n parentId?: string\n): { children: NodeWithVertex[] } {\n const siblings = nodeArr.filter((node) => node?.parentId === parentId);\n elkContext.log.info('addVertices APA12', siblings, parentId);\n\n siblings.forEach((node) => {\n addVertex(graph, nodeArr, node, nodeDb, elkContext);\n });\n return graph;\n}\n\nfunction addVertex(\n graph: { children: NodeWithVertex[] },\n nodeArr: Node[],\n node: Node,\n nodeDb: Record<string, NodeWithVertex>,\n elkContext: ElkLayoutContext\n): void {\n const child = createElkNode(node);\n graph.children.push(child);\n nodeDb[node.id] = child;\n\n if (node.isGroup) {\n child.children = [];\n addVertices(nodeArr, child as { children: NodeWithVertex[] }, nodeDb, elkContext, node.id);\n child.labelData = getMeasuredLabelData(node, elkContext.getConfig());\n }\n}\n\nfunction createElkNode(node: Node): NodeWithVertex {\n const child = { ...node } as NodeWithVertex;\n delete (child as { domId?: unknown }).domId;\n\n if (node.isGroup) {\n child.children = [];\n } else {\n child.width = node.width ?? 0;\n child.height = node.height ?? 0;\n if (node.spreadPorts) {\n // ELK packs a fixed-size node's implicit ports (the attachment points of\n // port-less edges) tightly around the middle of a side; CENTER alignment\n // spreads them as far as the side allows. That is the whole lever: the\n // node-level `elk.spacing.portPort` and `elk.spacing.portsSurrounding`\n // options are ignored for implicit ports (measured with elkjs 0.9.3), and\n // the root's corner margin still bounds the spread \u2014 a 38px side holds\n // three ports about 8px apart.\n child.layoutOptions = {\n ...child.layoutOptions,\n 'elk.portAlignment.default': 'CENTER',\n };\n }\n }\n\n return child;\n}\n\nfunction getMeasuredLabelData(node: Node, config: any): LabelData {\n const existing = (node as unknown as { labelData?: LabelData }).labelData;\n if (existing) {\n return existing;\n }\n\n if (node.labelBBox) {\n return {\n width: node.labelBBox.width,\n height: Math.max(0, node.labelBBox.height - 2),\n wrappingWidth: node.wrappingWidth ?? config.flowchart?.wrappingWidth,\n };\n }\n\n return {\n width: 0,\n height: 0,\n wrappingWidth: node.wrappingWidth ?? config.flowchart?.wrappingWidth,\n };\n}\n\nfunction addEdgesToElkGraph(\n dataForLayout: LayoutData,\n graph: { edges: any[] },\n nodeDb: Record<string, NodeWithVertex>,\n elkContext: ElkLayoutContext\n): { edges: any[] } {\n elkContext.log.info('abc78 DAGA edges = ', dataForLayout);\n const linkIdCnt: Record<string, number> = {};\n\n dataForLayout.edges.forEach((edge) => {\n const linkIdBase = edge.id;\n linkIdCnt[linkIdBase] = (linkIdCnt[linkIdBase] ?? -1) + 1;\n const linkId = linkIdBase;\n edge.id = linkId;\n elkContext.log.info(\n 'abc78 new link id to be used is',\n linkIdBase,\n linkId,\n linkIdCnt[linkIdBase]\n );\n\n const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, nodeDb);\n elkContext.log.debug('abc78 source and target', source, target);\n\n graph.edges.push({\n ...edge,\n sources: [source],\n targets: [target],\n sourceId,\n targetId,\n labels: [\n {\n width: edge.width ?? 0,\n height: edge.height ?? 0,\n orgWidth: edge.width ?? 0,\n orgHeight: edge.height ?? 0,\n text: edge.label ?? '',\n layoutOptions: {\n 'edgeLabels.inline': 'true',\n 'edgeLabels.placement': 'CENTER',\n },\n },\n ],\n });\n });\n\n return graph;\n}\n\nfunction getEdgeStartEndPoint(edge: Edge, nodeDb: Record<string, NodeWithVertex>) {\n const sourceId = edge.start;\n const targetId = edge.end;\n const source = sourceId;\n const target = targetId;\n\n const startNode = sourceId ? nodeDb[sourceId] : undefined;\n const endNode = targetId ? nodeDb[targetId] : undefined;\n\n if (!startNode || !endNode) {\n return { source, target };\n }\n\n return { source, target, sourceId, targetId };\n}\n\nfunction configureSubgraphNodes(\n data4Layout: LayoutData,\n nodeDb: Record<string, NodeWithVertex>,\n parentLookupDb: TreeData,\n elkContext: ElkLayoutContext\n): void {\n data4Layout.nodes.forEach((n) => {\n const node = nodeDb[n.id];\n if (!node || parentLookupDb.childrenById[node.id] === undefined) {\n return;\n }\n\n node.labels = [\n {\n text: node.label,\n width: node?.labelData?.width ?? 50,\n height: node?.labelData?.height ?? 50,\n },\n ];\n elkContext.log.debug('UIO node label', node?.labelData?.width, node.padding);\n node.layoutOptions = buildSubgraphLayoutOptions(\n node,\n data4Layout.config.elk,\n elkContext.algorithm,\n elkContext.log\n );\n delete node.x;\n delete node.y;\n delete node.width;\n delete node.height;\n });\n}\n\nfunction configureCrossHierarchyEdges(\n elkGraph: { edges: any[] },\n nodeDb: Record<string, NodeWithVertex>,\n parentLookupDb: TreeData,\n log: ElkLayoutContext['log']\n): void {\n log.debug('APA01 processing edges, count:', elkGraph.edges.length);\n elkGraph.edges.forEach((edge: any, index: number) => {\n log.debug('APA01 processing edge', index, ':', edge);\n const source = edge.sources[0];\n const target = edge.targets[0];\n log.debug('APA01 source:', source, 'target:', target);\n log.debug('APA01 nodeDb[source]:', nodeDb[source]);\n log.debug('APA01 nodeDb[target]:', nodeDb[target]);\n\n if (nodeDb[source] && nodeDb[target] && nodeDb[source].parentId !== nodeDb[target].parentId) {\n const ancestorId = findCommonAncestor(source, target, parentLookupDb);\n setIncludeChildrenPolicy(nodeDb, source, ancestorId, log);\n setIncludeChildrenPolicy(nodeDb, target, ancestorId, log);\n }\n });\n}\n\nfunction setIncludeChildrenPolicy(\n nodeDb: Record<string, NodeWithVertex>,\n nodeId: string,\n ancestorId: string,\n log: ElkLayoutContext['log']\n): void {\n const node = nodeDb[nodeId];\n\n if (!node) {\n return;\n }\n node.layoutOptions ??= {};\n\n // If this node has a user-specified custom algorithm (e.g. elk.box) with\n // SEPARATE_CHILDREN, clear it \u2014 cross-boundary edges are incompatible with\n // isolated layout algorithms. Nodes using the default layered algorithm\n // (set via the dir branch) keep theirs so they still lay out correctly.\n if (\n node.layoutOptions['elk.hierarchyHandling'] === 'SEPARATE_CHILDREN' &&\n resolveContainerAlgorithm(node.metadata?.algorithm)\n ) {\n log.debug('Dropping explicit algorithm for node', node.id, 'due to cross-boundary edges');\n clearContainerAlgorithmOptions(node.layoutOptions);\n Object.assign(node.layoutOptions, groupTitleSizeOptions(node));\n }\n\n node.layoutOptions['elk.hierarchyHandling'] = 'INCLUDE_CHILDREN';\n if (node.id !== ancestorId && node.parentId) {\n setIncludeChildrenPolicy(nodeDb, node.parentId, ancestorId, log);\n }\n}\n\nasync function runElkLayout(\n elk: { layout: (graph: any) => Promise<ElkLayoutResult> },\n elkGraph: any,\n log: ElkLayoutContext['log']\n): Promise<ElkLayoutResult> {\n // Time the actual external elkjs call (\"layoutCore\") separately from our\n // wrapper. The render profiler isn't importable from this external package, so\n // read the single shared instance off the global \u2014 present only in\n // dev/profiling builds, undefined (and thus a no-op) otherwise.\n const profiler = (\n globalThis as typeof globalThis & {\n __mermaidProfiler?: { begin(name: string): void; end(): void };\n }\n ).__mermaidProfiler;\n try {\n // Time the actual external elkjs call (\"layoutCore\") on its own.\n profiler?.begin('layoutCore');\n let graph: ElkLayoutResult;\n try {\n graph = await elk.layout(elkGraph);\n } finally {\n profiler?.end();\n }\n log.debug('APA01 after - success');\n // Pass the object, not a pre-serialised string: `JSON.stringify` of the\n // whole laid-out graph ran on every render regardless of log level.\n log.debug('APA01 layout result:', graph);\n return graph;\n } catch (error) {\n log.error('ELK layout error:', error);\n throw error;\n }\n}\n\nfunction applyElkLayoutResult(\n data4Layout: LayoutData,\n graph: ElkLayoutResult,\n layoutState: ElkLayoutState,\n log: ElkLayoutContext['log']\n): void {\n const nodeById = new Map(data4Layout.nodes.map((node) => [node.id, node]));\n applyElkNodePositions(graph.children ?? [], layoutState, nodeById, 0, 0, 0, log);\n // Between positions and edges on purpose: `boundsFor` reads the box set\n // above, and `cutter2` clips an edge that ends on a group against it, so an\n // edge attaching to a frame follows the frame when it moves.\n evenGroupFrames(graph.children ?? [], layoutState, nodeById, graph);\n applyElkEdgeLayout(data4Layout, graph, layoutState, log);\n}\n\n/**\n * Sit each group's frame an even distance from its own contents.\n *\n * ELK sizes a container around everything it put inside, edges included. An\n * edge that runs against the flow of the layout gets routed back around the\n * outside, and when that happens inside a frame the frame grows to hold the\n * lane \u2014 on one side only, since that is where the edge leaves. The result is a\n * group with 76px of space on the right and 24px on the left, which reads as a\n * mistake because nothing visible occupies it.\n *\n * The lane is real and the edge still needs it, so the fix is not to reclaim\n * the space but to stop drawing the frame around it. The frame is pulled in to\n * `SUBGRAPH_PADDING` from the children on the left, right and bottom, and the\n * edge keeps its lane just outside \u2014 which is what an edge routed around a\n * group should look like anyway.\n *\n * The top is left exactly as ELK set it. It carries the subgraph's title strip,\n * and there is no way from here to tell how much of that padding is the label\n * and how much is spare, so tightening it risks clipping the title.\n *\n * Runs deepest-first, so a parent measures against children that have already\n * been pulled in rather than against their original boxes.\n */\nexport function collectDescendantIds(elkNode: any, into = new Set<string>()): Set<string> {\n for (const child of elkNode.children ?? []) {\n into.add(child.id);\n collectDescendantIds(child, into);\n }\n return into;\n}\n\n/**\n * Absolute points of every edge ELK routed INSIDE this group \u2014 meaning both of\n * its endpoints are descendants of the group \u2014 plus the attachment point of\n * every edge that terminates ON the group itself.\n *\n * An edge with one endpoint outside is the case this whole pass exists for: its\n * lane belongs to the layout around the group, not to the group, so the frame\n * should not be drawn around it. An edge with both endpoints inside is the\n * opposite \u2014 its lane is part of the group's interior, and a frame pulled in\n * past it would leave the edge running outside a group it never leaves.\n *\n * An edge whose endpoint IS the group (a state diagram's `[*] --> Composite`,\n * a flowchart's `node --> subgraph`) sits between the two: ELK anchored it on\n * the frame's border, so the border must not be pulled past that anchor. When\n * it was, the stale anchor ended up floating outside the drawn frame, the\n * on-border check in `sanitizeElkEdgePoints` no longer recognised it, and\n * `cutter2` re-clipped the edge along a ray to the group's centre \u2014 painting a\n * long shallow diagonal that hugged the frame's side. Only the terminal point\n * on the group is added, never the edge's other points: those belong to the\n * layout outside the frame.\n */\nfunction internalEdgePoints(\n graph: ElkLayoutResult,\n descendants: Set<string>,\n layoutState: ElkLayoutState,\n groupId: string\n): P[] {\n const points: P[] = [];\n for (const edge of graph.edges ?? []) {\n const source = edge.sources?.[0] ?? edge.start;\n const target = edge.targets?.[0] ?? edge.end;\n const isInternal = descendants.has(source) && descendants.has(target);\n const attachesAtStart = source === groupId;\n const attachesAtEnd = target === groupId;\n if (!isInternal && !attachesAtStart && !attachesAtEnd) {\n continue;\n }\n const offset = calcOffset(source, target, layoutState.parentLookupDb, layoutState.nodeDb);\n for (const section of edge.sections ?? []) {\n const sectionPoints = isInternal\n ? [section.startPoint, ...(section.bendPoints ?? []), section.endPoint]\n : [attachesAtStart ? section.startPoint : null, attachesAtEnd ? section.endPoint : null];\n for (const p of sectionPoints) {\n if (p) {\n points.push({ x: p.x + offset.x, y: p.y + offset.y });\n }\n }\n }\n }\n return points;\n}\n\nexport function evenGroupFrames(\n elkNodes: any[],\n layoutState: ElkLayoutState,\n nodeById: Map<string, Node>,\n graph: ElkLayoutResult = {}\n): void {\n for (const elkNode of elkNodes) {\n if (!elkNode?.isGroup) {\n continue;\n }\n const children = elkNode.children ?? [];\n evenGroupFrames(children, layoutState, nodeById, graph);\n\n const group = layoutState.nodeDb[elkNode.id];\n const boxes = children\n .map((child: { id: string }) => layoutState.nodeDb[child.id])\n .filter((child: NodeWithVertex | undefined) => child?.offset && child.width && child.height);\n if (!group?.offset || boxes.length === 0) {\n continue;\n }\n\n const lane = internalEdgePoints(graph, collectDescendantIds(elkNode), layoutState, elkNode.id);\n const xs = [\n ...boxes.map((b: NodeWithVertex) => b.offset!.posX),\n ...boxes.map((b: NodeWithVertex) => b.offset!.posX + b.width!),\n ...lane.map((p) => p.x),\n ];\n const ys = [\n ...boxes.map((b: NodeWithVertex) => b.offset!.posY),\n ...boxes.map((b: NodeWithVertex) => b.offset!.posY + b.height!),\n ...lane.map((p) => p.y),\n ];\n\n // Only ever pull a frame IN. ELK sized it to hold everything it put there,\n // so growing one would mean this pass had measured something ELK had not \u2014\n // more likely a bug here than a gap there.\n const origin = group.offset;\n const left = Math.max(origin.posX, Math.min(...xs) - SUBGRAPH_PADDING);\n const right = Math.min(origin.posX + group.width!, Math.max(...xs) + SUBGRAPH_PADDING);\n const bottom = Math.min(origin.posY + group.height!, Math.max(...ys) + SUBGRAPH_PADDING);\n const top = origin.posY;\n\n // Keep the same title-plus-padding floor reserved before ELK routing and\n // used by clipping and painting.\n const labelFloor = groupTitleWidth(elkNode);\n let x = left;\n let width = right - left;\n if (width < labelFloor) {\n x -= (labelFloor - width) / 2;\n width = labelFloor;\n // Still only ever pull IN. A title wider than the frame ELK sized is a\n // frame ELK did not reserve for its own title, and widening it here would\n // paper over that while breaking the one guarantee this pass makes.\n const origRight = origin.posX + group.width!;\n x = Math.max(origin.posX, Math.min(x, origRight - width));\n width = Math.min(width, group.width!);\n }\n const height = bottom - top;\n if (height <= 0 || width <= 0) {\n continue;\n }\n\n // `calcOffset` resolves an edge's section against the origin of the\n // container that owns it, so moving a frame would drag every edge routed\n // inside it. Keep what ELK chose and let `calcOffset` read that instead.\n group.elkOrigin ??= { posX: origin.posX, posY: origin.posY };\n group.offset.posX = x;\n group.offset.width = width;\n group.offset.height = height;\n group.width = width;\n group.height = height;\n group.x = x + width / 2;\n group.y = top + height / 2;\n\n const layoutNode = nodeById.get(elkNode.id);\n if (layoutNode) {\n layoutNode.x = group.x;\n layoutNode.y = group.y;\n // The title floor was reserved before routing; never grow past ELK's\n // frame here, which could cover a neighbouring node or route.\n layoutNode.width = width;\n layoutNode.height = height;\n }\n }\n}\n\nfunction applyElkNodePositions(\n nodeArray: any[],\n layoutState: ElkLayoutState,\n nodeById: Map<string, Node>,\n relX: number,\n relY: number,\n depth: number,\n log: ElkLayoutContext['log']\n): void {\n nodeArray.forEach((node) => {\n if (!node) {\n return;\n }\n\n const graphNode = layoutState.nodeDb[node.id] ?? node;\n const width = Math.max(node.width, node.labels ? node.labels[0]?.width || 0 : 0);\n const offset = {\n posX: node.x + relX,\n posY: node.y + relY,\n x: relX,\n y: relY,\n depth,\n width,\n height: node.height,\n };\n graphNode.offset = offset;\n graphNode.x = offset.posX + node.width / 2;\n graphNode.y = offset.posY + node.height / 2;\n graphNode.width = node.width;\n graphNode.height = node.height;\n\n const layoutNode = nodeById.get(node.id);\n if (layoutNode) {\n layoutNode.x = graphNode.x;\n layoutNode.y = graphNode.y;\n layoutNode.width = node.isGroup\n ? Math.max(node.width, node.labelData?.width ?? 0)\n : node.width;\n layoutNode.height = node.height;\n const layoutNodeLabels = layoutNode as unknown as {\n labelData?: LabelData;\n labels?: unknown[];\n };\n layoutNodeLabels.labelData = node.labelData;\n layoutNodeLabels.labels = node.labels;\n }\n\n if (node.isGroup) {\n log.debug('Id abc88 subgraph = ', node.id, node.x, node.y, node.labelData);\n applyElkNodePositions(\n node.children ?? [],\n layoutState,\n nodeById,\n offset.posX,\n offset.posY,\n depth + 1,\n log\n );\n } else {\n log.info(\n 'Id NODE = ',\n node.id,\n node.x,\n node.y,\n relX,\n relY,\n `translate(${graphNode.x}, ${graphNode.y})`\n );\n }\n });\n}\n\n/**\n * Largest port-to-channel jog worth collapsing.\n *\n * ELK layered spreads an edge's port evenly along the node's side, then routes\n * the edge down an inter-layer channel whose row rarely lines up with that port\n * exactly. The leftover is a staircase right at the border: leave the port, run\n * a few pixels, step perpendicular onto the channel, carry on. With rounded\n * corners the two micro-bends sit on top of each other and read as a glitch.\n *\n * A step this close to the border can only be that connector \u2014 a genuine\n * obstacle dodge bends much further out \u2014 so moving the terminal onto the\n * channel row cannot introduce an overlap. The rest of the route is untouched.\n */\nconst TERMINAL_JOG_MAX = 16;\n\n/**\n * How far from the node the step may sit and still count as the connector.\n *\n * Size alone does not identify a port-to-channel step: a small step a long way\n * down the route is a routing decision, and collapsing it drags the port along\n * for no reason. On the sample corpus every genuine connector turns 20\u201325 from\n * the border, while the ones worth leaving alone turn at 48, 112 and 173 \u2014 one\n * of which slid a port 15px into an occupied row and produced a crossing that\n * was not there before.\n */\nconst TERMINAL_RUN_MAX = 30;\n\n/**\n * Tolerance for deciding whether a segment counts as axis-aligned, and whether\n * a step is a step at all.\n *\n * Deliberately much smaller than the shared `EPS` of 1, which exists for \"is\n * this point on a border\" and is far too coarse here: ELK routinely leaves a\n * sub-pixel step between the port row and the channel row, and at `EPS` those\n * are not even recognised as segments. They still paint as two rounded corners\n * stacked on each other, which is the artefact this pass removes \u2014 an\n * `infra -> auth` edge stepped 0.858 and rendered exactly that way.\n */\nconst JOG_EPS = 0.01;\n\n/** Axis of an axis-aligned segment: `h`, `v`, or undefined when diagonal. */\nfunction axisOf(a: P, b: P): 'h' | 'v' | undefined {\n const dx = Math.abs(b.x - a.x);\n const dy = Math.abs(b.y - a.y);\n if (dx > JOG_EPS && dy <= JOG_EPS) {\n return 'h';\n }\n if (dy > JOG_EPS && dx <= JOG_EPS) {\n return 'v';\n }\n return undefined;\n}\n\n/**\n * Straighten the port-to-channel staircase at either end of a clipped route,\n * leaving both ports where they are.\n *\n * Returns the original array when nothing applies, so callers can compare by\n * identity.\n */\nexport function straightenTerminalJogs(points: P[]): P[] {\n let pts = straightenFront(points) ?? points;\n const reversed = [...pts].reverse();\n const fixedEnd = straightenFront(reversed);\n if (fixedEnd) {\n pts = fixedEnd.reverse();\n }\n return pts;\n}\n\n/**\n * Straighten the staircase at the front of `pts`, or return null when it does\n * not apply.\n *\n * The step is removed by pulling the CHANNEL onto the port's row, never by\n * pulling the port onto the channel's. Moving the port slides the attachment\n * along the node border, and a node whose other edges are still at their spread\n * positions then looks lopsided \u2014 the reason this was rewritten. Moving the\n * channel instead keeps every port exactly where ELK placed it, at the cost of\n * displacing one run, which is why the caller checks the result for crossings.\n *\n * The run is only moved when the point after it is not the far terminal, since\n * that would move the other end's port and reintroduce the same problem there.\n */\nfunction straightenFront(pts: P[]): P[] | null {\n if (pts.length < 5) {\n return null;\n }\n const [p0, p1, p2, p3] = pts;\n const axis = axisOf(p0, p1);\n if (!axis || axisOf(p2, p3) !== axis || axisOf(p1, p2) !== (axis === 'h' ? 'v' : 'h')) {\n return null;\n }\n // The step has to be next to the node to be the port-to-channel connector.\n if (Math.hypot(p1.x - p0.x, p1.y - p0.y) > TERMINAL_RUN_MAX) {\n return null;\n }\n const jog = axis === 'h' ? Math.abs(p2.y - p1.y) : Math.abs(p2.x - p1.x);\n if (jog < JOG_EPS || jog > TERMINAL_JOG_MAX) {\n return null;\n }\n // The route has to keep travelling the same way after the step, otherwise\n // this is a real turn rather than a connector.\n const forward =\n axis === 'h'\n ? Math.sign(p1.x - p0.x) === Math.sign(p3.x - p2.x)\n : Math.sign(p1.y - p0.y) === Math.sign(p3.y - p2.y);\n if (!forward) {\n return null;\n }\n // The WHOLE run has to move, not just its first segment: the channel carries\n // on past p3 until the route turns, and shifting only part of it leaves a\n // diagonal where the moved and unmoved halves meet.\n let last = 3;\n while (last + 1 < pts.length && axisOf(pts[last], pts[last + 1]) === axis) {\n last++;\n }\n // The far terminal must not be inside the run \u2014 moving it would drag the\n // other end's port, which is the thing this avoids.\n if (last === pts.length - 1) {\n return null;\n }\n\n // No border check is needed: the port is untouched, so it stays exactly where\n // ELK put it, and the run moves onto that same row \u2014 which the first segment\n // already occupied on its way out of the node.\n const moved = [...pts];\n for (let i = 2; i <= last; i++) {\n moved[i] = axis === 'h' ? { x: pts[i].x, y: p0.y } : { x: p0.x, y: pts[i].y };\n }\n // p1 and p2 are now collinear with p0 and the rest of the run.\n moved.splice(1, 2);\n return moved;\n}\n\n/** Do two axis-aligned segments cross at a point interior to both? */\nfunction segmentsCrossStrict(a1: P, a2: P, b1: P, b2: P): boolean {\n const side = (o: P, p: P, q: P) => (p.x - o.x) * (q.y - o.y) - (p.y - o.y) * (q.x - o.x);\n const d1 = side(b1, b2, a1);\n const d2 = side(b1, b2, a2);\n const d3 = side(a1, a2, b1);\n const d4 = side(a1, a2, b2);\n return ((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0));\n}\n\n/** How many times one polyline crosses another. */\nfunction crossingCount(a: P[], b: P[]): number {\n let n = 0;\n for (let i = 0; i < a.length - 1; i++) {\n for (let j = 0; j < b.length - 1; j++) {\n if (segmentsCrossStrict(a[i], a[i + 1], b[j], b[j + 1])) {\n n++;\n }\n }\n }\n return n;\n}\n\n/**\n * Straighten the port-to-channel step on every edge that has one, but only\n * where doing so does not buy a crossing.\n *\n * Runs once over the finished layout rather than per edge, because the decision\n * needs the other edges: the step is removed by displacing one of this edge's\n * runs onto the port's row, and that run can land in a lane something else\n * already occupies. Trading a barely-visible step for a new crossing is a bad\n * deal, so an edge that would cause one is left exactly as ELK routed it.\n */\nfunction straightenEdgeTerminals(edges: Edge[]): void {\n const routes = edges.map((edge) => (edge as { points?: P[] }).points ?? []);\n\n for (const [index, edge] of edges.entries()) {\n const original = routes[index];\n if (original.length < 5) {\n continue;\n }\n const candidate = straightenTerminalJogs(original);\n if (candidate === original) {\n continue;\n }\n\n let before = 0;\n let after = 0;\n for (const [other, route] of routes.entries()) {\n if (other === index || route.length < 2) {\n continue;\n }\n before += crossingCount(original, route);\n after += crossingCount(candidate, route);\n }\n if (after > before) {\n continue;\n }\n\n (edge as { points?: P[] }).points = candidate;\n routes[index] = candidate;\n }\n}\n\nfunction applyElkEdgeLayout(\n data4Layout: LayoutData,\n graph: ElkLayoutResult,\n layoutState: ElkLayoutState,\n log: ElkLayoutContext['log']\n): void {\n const edgeById = new Map(data4Layout.edges.map((edge) => [edge.id, edge]));\n // Opt-out rather than opt-in: the step this removes is never intentional.\n const straightenEdges = data4Layout.config.elk?.straightenEdges !== false;\n\n // Alignment pre-pass: move degenerately-anchored small nodes onto their routed\n // lines BEFORE any edge points are built, so every edge \u2014 whichever side of the\n // node it attaches to, in whatever order the loop visits it \u2014 sees the node at\n // its final position. See `alignDegenerateNodeToAnchor` for why the node moves\n // and the edge does not.\n const layoutNodeById = new Map(data4Layout.nodes.map((node) => [node.id, node]));\n const alignedNodes = new Set<string>();\n graph.edges?.forEach((edge) => {\n if (!edge.sections?.length) {\n return;\n }\n const startNode = layoutState.nodeDb[edge.sources?.[0] ?? edge.start];\n const endNode = layoutState.nodeDb[edge.targets?.[0] ?? edge.end];\n if (!startNode || !endNode) {\n return;\n }\n const sourceId = edge.start ?? edge.sourceId ?? edge.sources?.[0];\n const targetId = edge.end ?? edge.targetId ?? edge.targets?.[0];\n const offset = calcOffset(sourceId, targetId, layoutState.parentLookupDb, layoutState.nodeDb);\n const section = edge.sections[0];\n if (startNode.shape !== 'rect33') {\n alignDegenerateNodeToAnchor(\n startNode,\n { x: section.startPoint.x + offset.x, y: section.startPoint.y + offset.y },\n layoutNodeById,\n alignedNodes\n );\n }\n if (endNode.shape !== 'rect33') {\n alignDegenerateNodeToAnchor(\n endNode,\n { x: section.endPoint.x + offset.x, y: section.endPoint.y + offset.y },\n layoutNodeById,\n alignedNodes\n );\n }\n });\n\n graph.edges?.forEach((edge) => {\n const layoutEdge = edgeById.get(edge.id);\n if (!layoutEdge) {\n return;\n }\n\n const startId = edge.sources?.[0] ?? edge.start;\n const endId = edge.targets?.[0] ?? edge.end;\n const startNode = layoutState.nodeDb[startId];\n const endNode = layoutState.nodeDb[endId];\n if (!startNode || !endNode) {\n return;\n }\n\n // `elk.box` and `elk.rectpacking` place nodes but never route edges, so ELK\n // returns no sections. Guarded on length, not presence: an empty array would\n // otherwise skip the fallback and hand `undefined` to\n // `createEdgePointsFromSection`, which dereferences `section.startPoint`.\n // `points` is not optional downstream \u2014 the paint step\n // filters it \u2014 so fall back to a straight line between the two node centres\n // rather than leaving the edge unlaid. The centres are then clipped back to\n // the node borders: this renderer paints with `skipIntersect`, so nothing\n // downstream would do it, and an unclipped line runs under both nodes with\n // its end marker buried inside the target.\n if (!edge.sections?.length) {\n const centre = (node: NodeWithVertex) => ({\n x: (node.offset?.posX ?? node.x ?? 0) + (node.width ?? 0) / 2,\n y: (node.offset?.posY ?? node.y ?? 0) + (node.height ?? 0) / 2,\n });\n const from = centre(startNode);\n const to = centre(endNode);\n startNode.x = from.x;\n startNode.y = from.y;\n endNode.x = to.x;\n endNode.y = to.y;\n const straightPoints = sanitizeElkEdgePoints([from, to], startNode, endNode, log);\n layoutEdge.points = straightPoints;\n layoutEdge.curve = 'linear';\n // No routing means no label position either: ELK only fills in\n // `edge.labels[*].x/y` for edges it laid out. `positionEdgeLabel` reads\n // `edge.x` / `edge.y` straight into a `translate(...)`, so leaving them\n // unset emits `translate(undefined, NaN)` \u2014 dropped by the browser, which\n // parks the label at the group origin. Put it on the line's midpoint.\n const lineStart = straightPoints[0];\n const lineEnd = straightPoints[straightPoints.length - 1];\n layoutEdge.x = (lineStart.x + lineEnd.x) / 2;\n layoutEdge.y = (lineStart.y + lineEnd.y) / 2;\n log.debug('APA18 no edge sections, using a straight line', edge.id, layoutEdge.points);\n return;\n }\n\n const sourceId = edge.start ?? edge.sourceId ?? startId;\n const targetId = edge.end ?? edge.targetId ?? endId;\n const offset = calcOffset(sourceId, targetId, layoutState.parentLookupDb, layoutState.nodeDb);\n log.debug('APA18 offset', offset, sourceId, ' ==> ', targetId, 'edge:', edge, startNode);\n\n const section = edge.sections[0];\n const points = createEdgePointsFromSection(section, offset);\n startNode.x = startNode.offset!.posX + startNode.width! / 2;\n startNode.y = startNode.offset!.posY + startNode.height! / 2;\n endNode.x = endNode.offset!.posX + endNode.width! / 2;\n endNode.y = endNode.offset!.posY + endNode.height! / 2;\n\n if (startNode.shape !== 'rect33') {\n points.unshift({ x: startNode.x, y: startNode.y });\n }\n\n if (endNode.shape !== 'rect33') {\n points.push({ x: endNode.x, y: endNode.y });\n }\n\n const clipped = sanitizeElkEdgePoints(points, startNode, endNode, log);\n layoutEdge.points = ensureStartMarkerSegmentLength(\n ensureEndMarkerSegmentLength(\n clipped,\n boundsFor(endNode),\n getEndMarkerPathOffset(layoutEdge),\n log\n ),\n boundsFor(startNode),\n getStartMarkerPathOffset(layoutEdge),\n log\n );\n layoutEdge.curve = 'rounded';\n\n const label = edge.labels?.[0];\n if (label) {\n layoutEdge.x = label.x + offset.x + label.width / 2;\n layoutEdge.y = label.y + offset.y + label.height / 2;\n }\n });\n\n if (straightenEdges) {\n straightenEdgeTerminals(data4Layout.edges);\n }\n}\n\n/**\n * ELK reserves `PORTS_SURROUNDING_MARGIN` at both ends of a node side before\n * distributing edge anchors along it (see `PORTS_SURROUNDING`). On a side\n * shorter than twice that margin the usable span is negative and ELK's clamping\n * parks the anchor off-centre \u2014 a 14px start/end state circle gets its edge\n * attached ~3px off the dot's centre, and no node-level option overrides it\n * (the spacing is only read per hierarchy level).\n *\n * The anchor's position along the side is garbage, but its LINE is not: with\n * `nodeFlexibility: PORT_POSITION` ELK has already placed the node so this\n * clamped port sits on a straight route \u2014 so it is the node that stands\n * off-centre, not the edge. An earlier fix repointed the edge at the node's\n * centre, which bent ELK's straight verticals visibly diagonal (every fixture\n * with `[*] --> SomeState` showed it). Move the NODE instead: shift it along\n * the side's axis until its centre sits on the anchor line. The edge stays\n * exactly as routed, and the appended centre point is collinear with it.\n *\n * The shift is at most half the side (\u22724px on a state dot), first anchor wins\n * (later anchors on a degenerate side land on the same clamped point), and both\n * the nodeDb entry and the layout node move so painting and clipping agree.\n */\nfunction alignDegenerateNodeToAnchor(\n node: NodeWithVertex,\n anchor: P,\n layoutNodeById: Map<string, Node>,\n alignedNodes: Set<string>\n): void {\n const width = node.width ?? 0;\n const height = node.height ?? 0;\n const top = node.offset!.posY;\n const bottom = top + height;\n // ELK puts the anchor exactly on the border; the slack only absorbs float\n // error from the offset arithmetic. Same tolerance `onBorder` uses.\n const tol = 0.5;\n // An anchor on the top or bottom border spreads along the width; one on the\n // left or right border spreads along the height.\n const alongWidth = Math.abs(anchor.y - top) <= tol || Math.abs(anchor.y - bottom) <= tol;\n if ((alongWidth ? width : height) >= 2 * PORTS_SURROUNDING_MARGIN) {\n return;\n }\n if (alignedNodes.has(node.id)) {\n return;\n }\n alignedNodes.add(node.id);\n\n const delta = alongWidth\n ? anchor.x - (node.offset!.posX + width / 2)\n : anchor.y - (node.offset!.posY + height / 2);\n if (Math.abs(delta) < 0.01) {\n return;\n }\n if (alongWidth) {\n node.offset!.posX += delta;\n node.x = node.offset!.posX + width / 2;\n } else {\n node.offset!.posY += delta;\n node.y = node.offset!.posY + height / 2;\n }\n const layoutNode = layoutNodeById.get(node.id);\n if (layoutNode) {\n layoutNode.x = node.offset!.posX + width / 2;\n layoutNode.y = node.offset!.posY + height / 2;\n }\n}\n\nfunction createEdgePointsFromSection(section: any, offset: { x: number; y: number }): P[] {\n const src = section.startPoint;\n const dest = section.endPoint;\n const segments = section.bendPoints ? section.bendPoints : [];\n const segPoints = segments.map((segment: { x: number; y: number }) => ({\n x: segment.x + offset.x,\n y: segment.y + offset.y,\n }));\n\n return [\n { x: src.x + offset.x, y: src.y + offset.y },\n ...segPoints,\n { x: dest.x + offset.x, y: dest.y + offset.y },\n ];\n}\n\nfunction calcOffset(\n src: string,\n dest: string,\n parentLookupDb: TreeData,\n nodeDb: Record<string, NodeWithVertex>\n): { x: number; y: number } {\n const ancestor = findCommonAncestor(src, dest, parentLookupDb);\n if (ancestor === undefined || ancestor === 'root') {\n return { x: 0, y: 0 };\n }\n\n // `elkOrigin` when present: `evenGroupFrames` may have moved the frame, but a\n // section's coordinates are relative to where ELK put the container, not to\n // where the frame is now drawn.\n const node = nodeDb[ancestor];\n const ancestorOffset = node?.elkOrigin ?? node?.offset;\n return {\n x: ancestorOffset?.posX ?? 0,\n y: ancestorOffset?.posY ?? 0,\n };\n}\n\nexport function sanitizeElkEdgePoints(\n points: P[],\n startNode: NodeWithVertex,\n endNode: NodeWithVertex,\n log: ElkLayoutContext['log']\n): P[] {\n const prevPoints = Array.isArray(points) ? [...points] : [];\n const endBounds = boundsFor(endNode);\n log.debug(\n 'PPP cutter2: Points before cutter2:',\n JSON.stringify(points),\n 'endBounds:',\n endBounds,\n onBorder(endBounds, points[points.length - 1])\n );\n\n let clippedPoints: P[];\n {\n const startBounds = boundsFor(startNode);\n const endBounds = boundsFor(endNode);\n\n const startIsGroup = !!startNode?.isGroup;\n const endIsGroup = !!endNode?.isGroup;\n\n const { candidate: startCandidate, centerApprox: startCenterApprox } = getCandidateBorderPoint(\n prevPoints,\n startNode,\n 'start'\n );\n const { candidate: endCandidate, centerApprox: endCenterApprox } = getCandidateBorderPoint(\n prevPoints,\n endNode,\n 'end'\n );\n\n let skipStart = startIsGroup && onBorder(startBounds, startCandidate);\n let skipEnd = endIsGroup && onBorder(endBounds, endCandidate);\n\n dropAutoCenterPoint(prevPoints, 'start', skipStart && startCenterApprox);\n dropAutoCenterPoint(prevPoints, 'end', skipEnd && endCenterApprox);\n\n // If a frame changed, remove its obsolete interior terminals and intersect\n // the actual crossing segment. A ray to the group centre changes the\n // approach direction and leaves a spurious segment along the border.\n if (startIsGroup && !skipStart) {\n skipStart = clipGroupEndpoint(prevPoints, startBounds, 'start');\n }\n if (endIsGroup && !skipEnd) {\n skipEnd = clipGroupEndpoint(prevPoints, endBounds, 'end');\n }\n\n if (skipStart || skipEnd) {\n if (!skipStart) {\n applyStartIntersectionIfNeeded(prevPoints, startNode, startBounds, log);\n }\n if (!skipEnd) {\n applyEndIntersectionIfNeeded(prevPoints, endNode, endBounds, log);\n }\n\n log.debug('PPP cutter2: skipping cutter2 due to on-border group endpoint(s)', {\n skipStart,\n skipEnd,\n startCenterApprox,\n endCenterApprox,\n startCandidate,\n endCandidate,\n });\n clippedPoints = prevPoints;\n } else {\n clippedPoints = cutter2(startNode, endNode, prevPoints, log);\n }\n }\n\n log.debug('PPP cutter2: Points after cutter2:', JSON.stringify(clippedPoints));\n if (!Array.isArray(clippedPoints) || clippedPoints.length < 2 || hasInvalidPoint(clippedPoints)) {\n log.warn('POI cutter2: Invalid points from cutter2, falling back to prevPoints', clippedPoints);\n const cleaned = prevPoints.filter((p) => Number.isFinite(p?.x) && Number.isFinite(p?.y));\n clippedPoints = cleaned.length >= 2 ? cleaned : prevPoints;\n }\n\n log.debug('UIO cutter2: Points after cutter2 (sanitized):', clippedPoints);\n return dedupeConsecutivePoints(clippedPoints, log);\n}\n\nfunction hasInvalidPoint(points: P[]): boolean {\n return points?.some((point) => !Number.isFinite(point?.x) || !Number.isFinite(point?.y));\n}\n\nfunction dedupeConsecutivePoints(points: P[], log: ElkLayoutContext['log']): P[] {\n const deduped = points.filter((point, index, arr) => {\n if (index === 0) {\n return true;\n }\n const prev = arr[index - 1];\n return Math.abs(point.x - prev.x) > 1e-6 || Math.abs(point.y - prev.y) > 1e-6;\n });\n\n if (deduped.length !== points.length) {\n log.debug('UIO cutter2: removed consecutive duplicate points', {\n before: points,\n after: deduped,\n });\n }\n return deduped;\n}\n\nfunction getEndMarkerPathOffset(edge: Edge): number {\n return markerPathOffset((edge as { arrowTypeEnd?: unknown }).arrowTypeEnd);\n}\n\nfunction getStartMarkerPathOffset(edge: Edge): number {\n return markerPathOffset((edge as { arrowTypeStart?: unknown }).arrowTypeStart);\n}\n\n/**\n * Mirror of `ensureEndMarkerSegmentLength` for the start of the path: the\n * start marker's pull-back walks forward along the first segment, so a short\n * on-border stub there flips the start marker the same way.\n */\nexport function ensureStartMarkerSegmentLength(\n points: P[],\n startBounds: RectLike,\n markerOffset: number,\n log: { debug: (...args: unknown[]) => void }\n): P[] {\n if (markerOffset <= 0 || points.length < 3) {\n return points;\n }\n\n const start = points[0];\n const exit = points[1];\n const segmentLength = Math.hypot(exit.x - start.x, exit.y - start.y);\n if (segmentLength >= Math.max(MIN_END_MARKER_SEGMENT_LENGTH, markerOffset * 2)) {\n return points;\n }\n\n if (!onBorder(startBounds, exit, 1)) {\n return points;\n }\n\n const adjusted = [start, ...points.slice(2)];\n log.debug('UIO cutter2: removed short start marker segment', {\n before: points,\n after: adjusted,\n markerOffset,\n segmentLength,\n });\n return adjusted;\n}\n\nexport function ensureEndMarkerSegmentLength(\n points: P[],\n endBounds: RectLike,\n markerOffset: number,\n log: { debug: (...args: unknown[]) => void }\n): P[] {\n if (markerOffset <= 0 || points.length < 3) {\n return points;\n }\n\n const end = points[points.length - 1];\n const entry = points[points.length - 2];\n const segmentLength = Math.hypot(end.x - entry.x, end.y - entry.y);\n if (segmentLength >= Math.max(MIN_END_MARKER_SEGMENT_LENGTH, markerOffset * 2)) {\n return points;\n }\n\n if (!onBorder(endBounds, entry, 1)) {\n return points;\n }\n\n const adjusted = [...points.slice(0, -2), end];\n log.debug('UIO cutter2: removed short end marker segment', {\n before: points,\n after: adjusted,\n markerOffset,\n segmentLength,\n });\n return adjusted;\n}\n\nfunction applyElkEdgeRenderData(data4Layout: LayoutData, elkContext: ElkLayoutContext): void {\n const defaultInterpolate = (data4Layout.edges as unknown as { defaultInterpolate?: unknown })\n .defaultInterpolate;\n const defaultStyle = (data4Layout.edges as unknown as { defaultStyle?: string[] }).defaultStyle;\n const conf = elkContext.getConfig();\n\n data4Layout.edges.forEach((edge) => {\n const edgeData = buildEdgeData(\n edge,\n {\n defaultStyle,\n defaultInterpolate,\n confCurve: conf.curve,\n },\n elkContext\n );\n Object.assign(edge, edgeData);\n });\n}\n\nfunction buildFallbackEdgeClasses(edge: Edge): string | undefined {\n if (edge.classes !== undefined) {\n return edge.classes;\n }\n if (edge.start && edge.end) {\n return `flowchart-link LS_${edge.start} LE_${edge.end}`;\n }\n return undefined;\n}\n\nfunction computeStroke(\n stroke: string | undefined,\n defaultStyle?: string[],\n defaultLabelStyle?: string[]\n) {\n let thickness = 'normal';\n let pattern = 'solid';\n let style: string[] = [];\n let labelStyle: string[] = [];\n\n if (stroke === 'dotted') {\n pattern = 'dotted';\n style = ['fill:none', 'stroke-width:2px', 'stroke-dasharray:3'];\n } else if (stroke === 'thick') {\n thickness = 'thick';\n style = ['stroke-width: 3.5px', 'fill:none'];\n } else {\n style = defaultStyle ?? ['fill:none'];\n if (defaultLabelStyle !== undefined) {\n labelStyle = defaultLabelStyle;\n }\n }\n return { thickness, pattern, style, labelStyle };\n}\n\nfunction getCurve(edgeInterpolate: unknown, edgesDefaultInterpolate: unknown, confCurve: unknown) {\n if (edgeInterpolate !== undefined) {\n return edgeInterpolate;\n }\n if (edgesDefaultInterpolate !== undefined) {\n return edgesDefaultInterpolate;\n }\n return confCurve;\n}\n\nfunction buildEdgeData(\n edge: Edge,\n defaults: {\n defaultStyle?: string[];\n defaultLabelStyle?: string[];\n defaultInterpolate?: unknown;\n confCurve: unknown;\n },\n elkContext: ElkLayoutContext\n) {\n const edgeData: any = {};\n edgeData.minlen = edge.minlen ?? edge.length ?? 1;\n edgeData.text = edge.text ?? edge.label;\n\n edgeData.arrowhead = edge.arrowhead ?? (edge.type === 'arrow_open' ? 'none' : 'normal');\n\n const arrowMap = ARROW_MAP[edge.type ?? 'arrow_open'] ?? ARROW_MAP.arrow_open;\n edgeData.arrowTypeStart = edge.arrowTypeStart ?? arrowMap[0];\n edgeData.arrowTypeEnd = edge.arrowTypeEnd ?? arrowMap[1];\n\n edgeData.startLabelRight = edge.startLabelRight;\n edgeData.endLabelLeft = edge.endLabelLeft;\n\n const strokeRes = computeStroke(edge.stroke, defaults.defaultStyle, defaults.defaultLabelStyle);\n edgeData.thickness = edge.thickness ?? strokeRes.thickness;\n edgeData.pattern = edge.pattern ?? strokeRes.pattern;\n edgeData.style = edge.style ?? strokeRes.style;\n edgeData.labelStyle = edge.labelStyle ?? strokeRes.labelStyle;\n edgeData.classes = buildFallbackEdgeClasses(edge);\n\n edgeData.curve = elkContext.interpolateToCurve(\n getCurve(edge.curve ?? edge.interpolate, defaults.defaultInterpolate, defaults.confCurve) as\n | string\n | undefined,\n curveLinear\n );\n\n const hasText = (edgeData.text ?? '') !== '';\n if (edge.arrowheadStyle !== undefined) {\n edgeData.arrowheadStyle = edge.arrowheadStyle;\n } else if (hasText || edge.style !== undefined) {\n edgeData.arrowheadStyle = 'fill: #333';\n }\n edgeData.labelpos = edge.labelpos ?? (hasText ? 'c' : undefined);\n\n edgeData.labelType = edge.labelType;\n edgeData.label = (edge.label ?? edgeData.text ?? '').replace(\n elkContext.common.lineBreakRegex,\n '\\n'\n );\n\n return edgeData;\n}\n\nfunction getEffectiveGroupWidth(node: NodeWithVertex): number {\n return Math.max(node.width ?? 0, groupTitleWidth(node));\n}\n\nfunction boundsFor(node: NodeWithVertex): RectLike {\n const width = node?.isGroup ? getEffectiveGroupWidth(node) : node.width;\n return {\n x: node.offset!.posX + node.width! / 2,\n y: node.offset!.posY + node.height! / 2,\n width: width ?? 0,\n height: node.height ?? 0,\n padding: node.padding,\n };\n}\n\nfunction approxEq(a: number, b: number, eps = 1e-6): boolean {\n return Math.abs(a - b) < eps;\n}\n\nfunction isCenterApprox(point: P, node: { x?: number; y?: number }): boolean {\n return approxEq(point.x, node.x ?? 0) && approxEq(point.y, node.y ?? 0);\n}\n\nfunction getCandidateBorderPoint(\n points: P[],\n node: NodeWithVertex,\n side: Side\n): { candidate: P; centerApprox: boolean } {\n if (!points?.length) {\n return { candidate: { x: node.x ?? 0, y: node.y ?? 0 }, centerApprox: true };\n }\n if (side === 'start') {\n const first = points[0];\n const centerApprox = isCenterApprox(first, node);\n const candidate = centerApprox && points.length > 1 ? points[1] : first;\n return { candidate, centerApprox };\n } else {\n const last = points[points.length - 1];\n const centerApprox = isCenterApprox(last, node);\n const candidate = centerApprox && points.length > 1 ? points[points.length - 2] : last;\n return { candidate, centerApprox };\n }\n}\n\nfunction dropAutoCenterPoint(points: P[], side: Side, doDrop: boolean): void {\n if (!doDrop) {\n return;\n }\n if (side === 'start') {\n if (points.length > 0) {\n points.shift();\n }\n } else {\n if (points.length > 0) {\n points.pop();\n }\n }\n}\n\nfunction clipGroupEndpoint(points: P[], bounds: RectLike, side: Side): boolean {\n const step = side === 'start' ? 1 : -1;\n let index = side === 'start' ? 0 : points.length - 1;\n const terminalIndex = index;\n while (index >= 0 && index < points.length && !outsideNode(bounds, points[index])) {\n index += step;\n }\n if (index === terminalIndex || index < 0 || index >= points.length) {\n return false;\n }\n\n const outside = points[index];\n const inside = points[index - step];\n const dx = outside.x - inside.x;\n const dy = outside.y - inside.y;\n // Walk from the last interior point to the first exterior point. The first\n // side reached is the crossing, including diagonal and corner approaches.\n const tx = dx === 0 ? Infinity : (bounds.x + (Math.sign(dx) * bounds.width) / 2 - inside.x) / dx;\n const ty = dy === 0 ? Infinity : (bounds.y + (Math.sign(dy) * bounds.height) / 2 - inside.y) / dy;\n const t = Math.min(tx, ty);\n const crossing = { x: inside.x + t * dx, y: inside.y + t * dy };\n if (side === 'start') {\n points.splice(0, index, crossing);\n } else {\n points.splice(index + 1, points.length - index - 1, crossing);\n }\n return true;\n}\n\nfunction applyStartIntersectionIfNeeded(\n points: P[],\n startNode: NodeWithVertex,\n startBounds: RectLike,\n log: ElkLayoutContext['log']\n): void {\n let firstOutsideStartIndex = -1;\n for (const [index, point] of points.entries()) {\n if (outsideNode(startBounds, point)) {\n firstOutsideStartIndex = index;\n break;\n }\n }\n if (firstOutsideStartIndex !== -1) {\n const outsidePointForStart = points[firstOutsideStartIndex];\n const startCenter = points[0];\n const startIntersection = computeNodeIntersection(\n startNode,\n startBounds,\n outsidePointForStart,\n startCenter\n );\n replaceEndpoint(points, 'start', startIntersection);\n log.debug('UIO cutter2: start-only intersection applied', { startIntersection });\n }\n}\n\nfunction applyEndIntersectionIfNeeded(\n points: P[],\n endNode: NodeWithVertex,\n endBounds: RectLike,\n log: ElkLayoutContext['log']\n): void {\n let outsideIndexForEnd = -1;\n for (let index = points.length - 1; index >= 0; index--) {\n if (outsideNode(endBounds, points[index])) {\n outsideIndexForEnd = index;\n break;\n }\n }\n if (outsideIndexForEnd !== -1) {\n const outsidePointForEnd = points[outsideIndexForEnd];\n const endCenter = points[points.length - 1];\n const endIntersection = computeNodeIntersection(\n endNode,\n endBounds,\n outsidePointForEnd,\n endCenter\n );\n replaceEndpoint(points, 'end', endIntersection);\n log.debug('UIO cutter2: end-only intersection applied', { endIntersection });\n }\n}\n\n/**\n * Attachment point for the terminal at `portIndex`, on the axis the edge\n * departs along.\n *\n * `step` is +1 at the start of the polyline and -1 at the end, i.e. the\n * direction that walks AWAY from the node, which is what gives the departure\n * direction. Groups are excluded: their frame already is their outline, and the\n * caller has its own on-border handling for them.\n */\nfunction attachAlongDepartureAxis(\n node: NodeWithVertex,\n bounds: RectLike,\n points: P[],\n portIndex: number,\n step: 1 | -1\n): P | null {\n if (node?.isGroup) {\n return null;\n }\n const port = points[portIndex];\n const next = points[portIndex + step];\n if (!port || !next) {\n return null;\n }\n return outlineAttachPoint(node, bounds, port, next);\n}\n\nfunction cutter2(\n startNode: NodeWithVertex,\n endNode: NodeWithVertex,\n originalPoints: P[],\n log: ElkLayoutContext['log']\n): P[] {\n const startBounds = boundsFor(startNode);\n const endBounds = boundsFor(endNode);\n\n if (originalPoints.length === 0) {\n return [];\n }\n\n const points = [...originalPoints];\n const startCenter = points[0];\n const endCenter = points[points.length - 1];\n\n log.debug('PPP cutter2: bounds', { startBounds, endBounds });\n log.debug('PPP cutter2: original points', originalPoints);\n\n let firstOutsideStartIndex = -1;\n\n for (const [index, point] of points.entries()) {\n if (firstOutsideStartIndex === -1 && outsideNode(startBounds, point)) {\n firstOutsideStartIndex = index;\n }\n }\n\n if (firstOutsideStartIndex !== -1) {\n const outsidePointForStart = points[firstOutsideStartIndex];\n const startIntersection =\n // Prefer an attachment on the edge's own departure axis; see\n // `outlineAttachPoint`. Falls back to the centre-ray intersection, which\n // is all a non-axis-aligned or shapeless endpoint can offer.\n attachAlongDepartureAxis(startNode, startBounds, points, firstOutsideStartIndex, 1) ??\n computeNodeIntersection(startNode, startBounds, outsidePointForStart, startCenter);\n log.debug('UIO cutter2: start intersection', startIntersection);\n replaceEndpoint(points, 'start', startIntersection);\n }\n\n let outsidePointForEnd = null;\n let outsideIndexForEnd = -1;\n\n for (let index = points.length - 1; index >= 0; index--) {\n if (outsideNode(endBounds, points[index])) {\n outsidePointForEnd = points[index];\n outsideIndexForEnd = index;\n break;\n }\n }\n\n if (!outsidePointForEnd && points.length > 1) {\n outsidePointForEnd = points[points.length - 2];\n outsideIndexForEnd = points.length - 2;\n }\n\n if (outsidePointForEnd) {\n const endIntersection =\n attachAlongDepartureAxis(endNode, endBounds, points, outsideIndexForEnd, -1) ??\n computeNodeIntersection(endNode, endBounds, outsidePointForEnd, endCenter);\n log.debug('UIO cutter2: end intersection', { endIntersection, outsideIndexForEnd });\n replaceEndpoint(points, 'end', endIntersection);\n }\n\n if (points.length > 1) {\n const lastPoint = points[points.length - 1];\n const secondLastPoint = points[points.length - 2];\n const distance = Math.sqrt(\n (lastPoint.x - secondLastPoint.x) ** 2 + (lastPoint.y - secondLastPoint.y) ** 2\n );\n if (distance < 2) {\n log.debug('UIO cutter2: trimming tail point (too close)', {\n distance,\n lastPoint,\n secondLastPoint,\n });\n points.pop();\n }\n }\n\n log.debug('UIO cutter2: final points', points);\n\n return points;\n}\n", "export interface TreeData {\n parentById: Record<string, string>;\n childrenById: Record<string, string[]>;\n}\n\nexport const findCommonAncestor = (id1: string, id2: string, { parentById }: TreeData) => {\n const visited = new Set();\n let currentId = id1;\n\n // Edge case with self edges\n if (id1 === id2) {\n return parentById[id1] || 'root';\n }\n\n while (currentId) {\n visited.add(currentId);\n if (currentId === id2) {\n return currentId;\n }\n currentId = parentById[currentId];\n }\n\n currentId = id2;\n while (currentId) {\n if (visited.has(currentId)) {\n return currentId;\n }\n currentId = parentById[currentId];\n }\n\n return 'root';\n};\n", "import { applyLineJumpsToSvg, type EdgeGeom } from '../../rendering-elements/lineJump.js';\nimport type { CommonLayoutPaintContext } from '../common/index.js';\nimport type { LayoutData } from '../../types.js';\n/**\n * Radius of the arc drawn where one edge hops another.\n *\n * Fixed rather than configurable: a hop reads as a hop because every one in the\n * diagram is the same size, and `lineJump` already shrinks or drops an\n * individual arc where a bend leaves it no room. `elk.lineHops` therefore\n * exposes the STYLE (`arc` or `gap`) but not the radius, since a per-diagram\n * radius would be a knob whose only good value is this one.\n */\nconst JUMP_RADIUS = 6;\n\n/**\n * The paint groups `applyElkLineJumps` needs off the measure context.\n *\n * The selection type is taken from `applyLineJumpsToSvg`'s own signature rather\n * than named here: `D3Selection` is not part of mermaid's public surface, and\n * deriving it keeps the two in step without widening that surface.\n */\ninterface EdgePaintGroups {\n groups: { edgePaths: Parameters<typeof applyLineJumpsToSvg>[0] };\n}\n\n/**\n * Draw a hop where two edges cross.\n *\n * Runs as `afterPaint`, because a hop is a property of the rendered path rather\n * than of the layout: the crossings are only known once every edge has been\n * emitted, and the fix is to rewrite the `d` of the edge that gives way.\n *\n * ELK's curve is compatible either way \u2014 `applyElkEdgeLayout` sets `rounded`\n * for a routed edge and `linear` for its straight-line fallback, and\n * `curveSupportsLineHops` accepts both. An edge that takes a hop loses its\n * corner rounding in exchange, which is the trade the line-jump module\n * documents.\n */\nexport function applyElkLineJumps(\n data4Layout: LayoutData,\n { measure }: CommonLayoutPaintContext<unknown, EdgePaintGroups>\n): void {\n const lineHops = (data4Layout.config as { elk?: { lineHops?: boolean | string } })?.elk?.lineHops;\n if (lineHops === false) {\n return;\n }\n\n const edgeGeometries: EdgeGeom[] = data4Layout.edges\n .filter(\n (edge): edge is typeof edge & { points: EdgeGeom['points'] } =>\n Array.isArray(edge.points) && edge.points.length >= 2\n )\n .map((edge) => ({\n id: edge.id,\n points: edge.points,\n curve: edge.curve,\n arrowTypeStart: edge.arrowTypeStart,\n arrowTypeEnd: edge.arrowTypeEnd,\n }));\n\n applyLineJumpsToSvg(measure.groups.edgePaths, edgeGeometries, {\n enabled: true,\n jumpRadius: JUMP_RADIUS,\n jumpStyle: lineHops === 'gap' ? 'gap' : 'arc',\n });\n}\n", "/**\n * A catalogue of the ELK options that affect layout, with every valid value\n * listed and one line saying what it does.\n *\n * THIS FILE IS LIVE. Uncomment an option below and it applies to every ELK\n * diagram on the next rebuild \u2014 no edit to `render.ts`, nothing to paste\n * anywhere. Re-comment it to switch it back off. That is the whole workflow.\n *\n * Each block is merged over the shipping `layoutOptions` as the LAST word, so\n * an entry beats the `elk.preset`, any `config.elk.*` key the diagram sets, and\n * the DDLT sweep. Each block has exactly ONE destination:\n *\n * PLACEMENT_OPTIONS \u2500\u2510\n * EDGE_ROUTING_OPTIONS \u251C\u2500\u2192 root graph (`createRootElkGraph`)\n * ROOT_EXPERIMENT_OVERRIDES \u2500\u2518\n * SUBGRAPH_EXPERIMENT_OVERRIDES \u2192 every container (`buildSubgraphLayoutOptions`)\n *\n * Root is right for layering, node placement, cycle breaking and edge routing:\n * `elk.hierarchyHandling` is `INCLUDE_CHILDREN`, so one pass governs nodes\n * inside frames too. It is WRONG for `spacing.*`, `elk.padding` and\n * `nodeLabels.placement` \u2014 containers get their own set, so those do nothing at\n * the root and belong in `SUBGRAPH_EXPERIMENT_OVERRIDES`. Several options here\n * were written off as \"no effect\" before anyone noticed that.\n *\n * Do not merge a block into both sides. The shipping config gives containers a\n * DIFFERENT node placement from the root (`preset.containerPlacement` vs\n * `preset.placement`); forcing one value on both measures a layout the product\n * can never produce.\n *\n * Two things to know before reading a result:\n *\n * - Most keys are listed several times, once per valid value. Uncomment TWO\n * lines of the same key and it is a duplicate-key error, which is the\n * intended guard rather than a silent last-one-wins.\n * - Anything here that names a key already wired to `config.elk.*` silently\n * disables that config for every diagram while it is live. `elk.cycleBreakingStrategy`\n * was dead this way, and it took a bisect against the raw ELK option to spot.\n *\n * Everything here MUST be commented out on `develop`. `elkOptionCatalogue.spec.ts`\n * asserts that, so an option left switched on fails the build instead of\n * shipping as a silent rendering change for every user.\n */\n\nexport const PLACEMENT_OPTIONS: Record<string, unknown> = {\n // \u2500\u2500\u2500 Layering \u2014 which layer a node lands in (the column in LR, row in TB) \u2500\u2500\u2500\n // Coarsest placement decision there is; relocates 38-48% of nodes.\n // 'elk.layered.layering.strategy': 'COFFMAN_GRAHAM',\n // 'elk.layered.layering.strategy': 'NETWORK_SIMPLEX', // ELK default: fewest long edges *\n // 'elk.layered.layering.strategy': 'LONGEST_PATH', // every node as late as possible\n // 'elk.layered.layering.strategy': 'LONGEST_PATH_SOURCE', // same, measured from sources\n // 'elk.layered.layering.strategy': 'MIN_WIDTH', // narrower drawing, longer edges\n // 'elk.layered.layering.strategy': 'STRETCH_WIDTH', // wider drawing, shorter edges\n // 'elk.layered.layering.strategy': 'INTERACTIVE', // honours positions already on nodes\n // Cap on how many nodes COFFMAN_GRAHAM puts in one layer; ignored by the rest.\n // 'elk.layered.layering.coffmanGraham.layerBound': 2,\n // 'elk.layered.layering.coffmanGraham.layerBound': 4, // ELK default; taller and narrower\n // Pulls nodes into earlier layers to cut dummy nodes on long edges.\n // 'elk.layered.layering.nodePromotion.strategy': 'NONE', // ELK default\n // 'elk.layered.layering.nodePromotion.strategy': 'NIKOLOV',\n // 'elk.layered.layering.nodePromotion.strategy': 'NIKOLOV_PIXEL',\n // 'elk.layered.layering.nodePromotion.strategy': 'NIKOLOV_IMPROVED',\n // 'elk.layered.layering.nodePromotion.strategy': 'NIKOLOV_IMPROVED_PIXEL',\n // 'elk.layered.layering.nodePromotion.strategy': 'DUMMYNODE_PERCENTAGE',\n // 'elk.layered.layering.nodePromotion.strategy': 'NODECOUNT_PERCENTAGE',\n // 'elk.layered.layering.nodePromotion.strategy': 'NO_BOUNDARY',\n // \u2500\u2500\u2500 Crossing minimisation \u2014 the order of nodes within a layer \u2500\u2500\u2500\n // How node order inside each layer is chosen.\n // 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP', // ELK default\n //'elk.layered.crossingMinimization.strategy': 'INTERACTIVE', // keeps existing order\n // 'elk.layered.crossingMinimization.strategy': 'NONE', // declaration order, no sweep\n // Extra pass that swaps adjacent node pairs when it removes crossings.\n // 'elk.layered.crossingMinimization.greedySwitch.type': 'TWO_SIDED', // ELK default\n // 'elk.layered.crossingMinimization.greedySwitch.type': 'ONE_SIDED',\n // 'elk.layered.crossingMinimization.greedySwitch.type': 'OFF',\n // How hard declaration order is defended against crossing reduction.\n // 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES',\n // 'elk.layered.considerModelOrder.strategy': 'NONE', // ignore declaration order\n // 'elk.layered.considerModelOrder.strategy': 'PREFER_EDGES', // order edges, let nodes move\n // 'elk.layered.considerModelOrder.strategy': 'PREFER_NODES', // order nodes, let edges move\n // \u2500\u2500\u2500 Node placement \u2014 the coordinate within the layer \u2500\u2500\u2500\n // Wired to `elk.nodePlacementStrategy`; uncomment to override that config.\n // 'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX', // balanced; root under modelOrder/depthFirst\n // 'elk.layered.nodePlacement.strategy': 'BRANDES_KOEPF', // ELK default and ours: straight long edges\n // 'elk.layered.nodePlacement.strategy': 'LINEAR_SEGMENTS', // keeps chains aligned\n // 'elk.layered.nodePlacement.strategy': 'SIMPLE', // cheapest, least tidy\n // Shifts nodes to straighten edges rather than centre them in the layer.\n // 'elk.layered.nodePlacement.favorStraightEdges': true,\n // 'elk.layered.nodePlacement.favorStraightEdges': false,\n // Brandes-Koepf only: which of its four candidate alignments to keep.\n // 'elk.layered.nodePlacement.bk.fixedAlignment': 'NONE', // pick the shortest result; named presets\n // 'elk.layered.nodePlacement.bk.fixedAlignment': 'BALANCED', // average all four; default preset\n // 'elk.layered.nodePlacement.bk.fixedAlignment': 'LEFTUP',\n // 'elk.layered.nodePlacement.bk.fixedAlignment': 'RIGHTUP',\n // 'elk.layered.nodePlacement.bk.fixedAlignment': 'LEFTDOWN',\n // 'elk.layered.nodePlacement.bk.fixedAlignment': 'RIGHTDOWN',\n // Brandes-Koepf only: post-pass that trades compactness for straighter edges.\n // 'elk.layered.nodePlacement.bk.edgeStraightening': 'IMPROVE_STRAIGHTNESS',\n // 'elk.layered.nodePlacement.bk.edgeStraightening': 'NONE', // ELK default\n // Network-simplex only: what the placer is allowed to stretch to straighten edges.\n // 'elk.layered.nodePlacement.networkSimplex.nodeFlexibility': 'NONE', // ELK default\n // 'elk.layered.nodePlacement.networkSimplex.nodeFlexibility': 'NODE_SIZE',\n // 'elk.layered.nodePlacement.networkSimplex.nodeFlexibility': 'PORT_POSITION',\n // 'elk.layered.nodePlacement.networkSimplex.nodeFlexibility': 'NODE_SIZE_WHERE_SPACE_PERMITS',\n // \u2500\u2500\u2500 Cycles and hierarchy \u2500\u2500\u2500\n // Which edges get reversed to make the graph acyclic; decides which ones detour.\n // Wired to `elk.cycleBreakingStrategy`; uncomment to override that config.\n // 'elk.layered.cycleBreaking.strategy': 'GREEDY_MODEL_ORDER', // our default\n // 'elk.layered.cycleBreaking.strategy': 'GREEDY', // ELK default; short back edges, +20% total\n // 'elk.layered.cycleBreaking.strategy': 'DEPTH_FIRST', // middle ground, +6% total\n // 'elk.layered.cycleBreaking.strategy': 'MODEL_ORDER', // reverse purely by declaration order\n // 'elk.layered.cycleBreaking.strategy': 'INTERACTIVE', // reverse by existing positions\n // Whether subgraphs are laid out with the parent or in their own coordinate system.\n // 'elk.hierarchyHandling': 'INCLUDE_CHILDREN', // our default, one global pass\n // 'elk.hierarchyHandling': 'SEPARATE_CHILDREN', // shorter edges, far more constraint violations\n // Post-pass that pulls nodes back towards one side to reclaim space.\n // 'elk.layered.compaction.postCompaction.strategy': 'NONE', // ELK default\n // 'elk.layered.compaction.postCompaction.strategy': 'LEFT',\n // 'elk.layered.compaction.postCompaction.strategy': 'RIGHT',\n // 'elk.layered.compaction.postCompaction.strategy': 'LEFT_RIGHT_CONSTRAINT_LOCKING',\n // 'elk.layered.compaction.postCompaction.strategy': 'LEFT_RIGHT_CONNECTION_LOCKING',\n // 'elk.layered.compaction.postCompaction.strategy': 'EDGE_LENGTH',\n // \u2500\u2500\u2500 Spacing and labels \u2500\u2500\u2500\n // Base spacing everything else derives from; the single biggest lever on size.\n // 'spacing.baseValue': 40,\n // 'spacing.baseValue': 20, // ELK default \u2014 collapses this corpus, 13/14 invalid\n // Where a container's own title sits inside its frame.\n // 'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]',\n // \u2500\u2500\u2500 Measured inert on this corpus \u2014 a null result here means nothing \u2500\u2500\u2500\n // Overwritten straight after createRootElkGraph by the diagram's own direction.\n // 'elk.direction': 'UP',\n // ELK ignores this key in every spelling; the gap derives from spacing.baseValue.\n // 'elk.spacing.edgeNode': 20,\n // Only applies when wrapping.strategy is on, and it is off.\n // 'elk.layered.wrapping.cutting.strategy': 'ARD',\n // Routes reversed edges in their own band. No effect measured here.\n // 'elk.layered.feedbackEdges': true,\n // \u2500\u2500\u2500 Tried and parked \u2500\u2500\u2500\n // 'elk.layered.wrapping.strategy': 'MULTI_EDGE',\n // 'elk.layered.wrapping.strategy': 'SINGLE_EDGE',\n // 'elk.layered.crossingMinimization.semiInteractive': true,\n // 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1,\n // 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0,\n // 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK',\n // 'elk.insideSelfLoops.activate': true,\n // 'elk.separateConnectedComponents': true,\n // 'elk.alignment': 'LEFT',\n};\n\n/**\n * Edge ROUTING options. Live, on the same terms as {@link PLACEMENT_OPTIONS} \u2014\n * merged over root and subgraph alike, and spread after it so these win.\n *\n * Routing decides how an edge is drawn between the layers it was already\n * assigned to. It cannot change which way round the graph an edge travels \u2014 a\n * long detour is a back edge, and that is settled in cycle breaking and\n * layering, both of which live in {@link PLACEMENT_OPTIONS}.\n *\n * MUST be fully commented out on `develop`. The routing options actually in\n * force ship in `createRootElkGraph`: `edgeRouting.selfLoopDistribution`,\n * `unnecessaryBendpoints` and `mergeHierarchyEdges`.\n */\nexport const EDGE_ROUTING_OPTIONS: Record<string, unknown> = {\n // Shape of every edge. ORTHOGONAL is ELK's default and what the adapter expects.\n // 'elk.edgeRouting': 'ORTHOGONAL',\n // 'elk.edgeRouting': 'POLYLINE', // diagonal runs, fewer bends\n // 'elk.edgeRouting': 'SPLINES', // curved; validateLayout treats these as non-orthogonal\n // 'elk.edgeRouting': 'UNDEFINED', // let the algorithm decide\n // Drops bends that do not change the path. Already on in the literal below.\n // 'elk.layered.unnecessaryBendpoints': true,\n // 'elk.layered.unnecessaryBendpoints': false,\n // Routes reversed edges in their own band instead of among the forward ones.\n // The obvious candidate for a back-edge detour \u2014 measured inert on this corpus.\n // 'elk.layered.feedbackEdges': true,\n // 'elk.layered.feedbackEdges': false,\n // Lets edges that meet at a node share a trunk. Collapses arriving and leaving\n // onto ONE handle, which can imply a connection that does not exist.\n // 'elk.layered.mergeEdges': true,\n // 'elk.layered.mergeEdges': false,\n // Same, for edges that cross a subgraph boundary. On in the literal below.\n // 'elk.layered.mergeHierarchyEdges': true,\n // 'elk.layered.mergeHierarchyEdges': false,\n // How much straightening an edge is worth relative to other objectives.\n // Also settable per edge, which is the targeted way to rescue one bad route.\n // 'elk.layered.priority.straightness': 0,\n // 'elk.layered.priority.shortness': 0,\n // 'elk.layered.priority.direction': 1,\n // \u2500\u2500\u2500 Self loops \u2500\u2500\u2500\n // Which sides a node's self loops are spread across. EQUALLY ships below.\n // 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY',\n // 'elk.layered.edgeRouting.selfLoopDistribution': 'NORTH',\n // 'elk.layered.edgeRouting.selfLoopDistribution': 'NORTH_SOUTH',\n // Whether stacked self loops nest or sit side by side.\n // 'elk.layered.edgeRouting.selfLoopOrdering': 'STACKED',\n // 'elk.layered.edgeRouting.selfLoopOrdering': 'SEQUENCED',\n // Draw self loops inside the node rather than hanging off it.\n // 'elk.insideSelfLoops.activate': true,\n // \u2500\u2500\u2500 Spline and polyline tuning (only read by the matching edgeRouting) \u2500\u2500\u2500\n // How closely splines hug the orthogonal path they replace.\n // 'elk.layered.edgeRouting.splines.mode': 'CONSERVATIVE',\n // 'elk.layered.edgeRouting.splines.mode': 'CONSERVATIVE_SOFT',\n // 'elk.layered.edgeRouting.splines.mode': 'SLOPPY',\n // 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1,\n // Width of the band a POLYLINE edge may slope through.\n // (Was left uncommented while this block was inert; commented now that it is\n // live, since it would otherwise be permanently on for every diagram.)\n // 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0,\n // \u2500\u2500\u2500 Lanes and clearance \u2500\u2500\u2500\n // Gap between two edges sharing a lane; too small trips the proximity checks.\n // 'spacing.edgeEdge': 10,\n // 'elk.layered.spacing.edgeEdgeBetweenLayers': 20,\n // Gap between an edge and a node it passes. Ignored at root; the subgraph\n // value derives from `spacing.baseValue` at roughly half.\n // 'spacing.edgeNode': 20,\n // 'elk.layered.spacing.edgeNodeBetweenLayers': 80,\n // \u2500\u2500\u2500 Edge labels \u2500\u2500\u2500\n // Which side of its edge a label sits on.\n // 'elk.layered.edgeLabels.sideSelection': 'SMART_DOWN',\n // 'elk.layered.edgeLabels.sideSelection': 'SMART_UP',\n // 'elk.layered.edgeLabels.sideSelection': 'ALWAYS_UP',\n // 'elk.layered.edgeLabels.sideSelection': 'ALWAYS_DOWN',\n // 'elk.layered.edgeLabels.sideSelection': 'DIRECTION_UP',\n // 'elk.layered.edgeLabels.sideSelection': 'DIRECTION_DOWN',\n // Which layer a centre label is parked in when the edge spans several.\n // 'elk.layered.edgeLabels.centerLabelPlacementStrategy': 'MEDIAN_LAYER',\n // 'elk.layered.edgeLabels.centerLabelPlacementStrategy': 'HEAD_LAYER',\n // 'elk.layered.edgeLabels.centerLabelPlacementStrategy': 'TAIL_LAYER',\n // 'elk.layered.edgeLabels.centerLabelPlacementStrategy': 'SPACE_EFFICIENT_LAYER',\n // 'elk.layered.edgeLabels.centerLabelPlacementStrategy': 'WIDEST_LAYER',\n // 'elk.layered.edgeLabels.centerLabelPlacementStrategy': 'CENTER_LAYER',\n};\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * THE SWITCH\n *\n * Everything above is reference material to read and copy from \u2014 but it is not\n * inert: `render.ts` imports `PLACEMENT_OPTIONS` and `EDGE_ROUTING_OPTIONS` too\n * and merges them over the ROOT graph's layout options, so uncommenting a line\n * up there switches it on just as surely. The two objects below are the\n * per-scope scratch pads, merged as the last word over the root and subgraph\n * options. All four objects together are the on/off switch.\n *\n * To try an option: copy its line out of the catalogue above into the matching\n * object below and uncomment it. To switch back off: re-comment it. Nothing in\n * `render.ts` needs editing either way, so an experiment can never be left\n * behind as a stray edit in production code \u2014 which is how the previous round\n * of these ended up deleted rather than kept.\n *\n * All four MUST be empty on `develop`. `elkOptionCatalogue.spec.ts` asserts\n * exactly that, so an override left switched on fails the build instead of\n * shipping.\n *\n * Which object to use matters more than it looks: options set on the ROOT graph\n * do NOT reach subgraphs \u2014 containers get their own set \u2014 so `spacing.*`,\n * `elk.padding` and `nodeLabels.placement` do nothing at the root. Several\n * options in the catalogue above were written off as \"no effect\" until they\n * were moved to the subgraph side.\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/**\n * Scratch overrides merged last over the ROOT graph's `layoutOptions`, after\n * `elk.preset` and every `config.elk.*` key have been resolved \u2014 so an entry\n * here beats the shipping default AND the diagram's own config.\n *\n * Governs layering, node placement, cycle breaking and edge routing for the\n * top-level graph. MUST be empty on `develop`.\n */\nexport const ROOT_EXPERIMENT_OVERRIDES: Record<string, unknown> = {\n // 'elk.layered.layering.strategy': 'COFFMAN_GRAHAM',\n // 'elk.layered.nodePlacement.strategy': 'BRANDES_KOEPF',\n // 'elk.layered.cycleBreaking.strategy': 'DEPTH_FIRST',\n // 'elk.edgeRouting': 'POLYLINE',\n // 'spacing.baseValue': 60,\n};\n\n/**\n * Scratch overrides merged last over every SUBGRAPH's `layoutOptions`, after\n * the per-container algorithm branch \u2014 so an entry here also beats the\n * rectpacking and directional-subgraph blocks.\n *\n * This is the side that owns spacing, padding and label placement inside a\n * frame. MUST be empty on `develop`.\n */\nexport const SUBGRAPH_EXPERIMENT_OVERRIDES: Record<string, unknown> = {\n // 'spacing.nodeNode': 60,\n // 'elk.spacing.edgeEdge': 10,\n // 'elk.layered.spacing.edgeNodeBetweenLayers': 80,\n // 'elk.padding': '[top=24,left=24,bottom=24,right=24]',\n // 'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]',\n // Equal-width subgraph frames. Tried and DOES NOT WORK: the options reach\n // ELK intact, but every container is forced back to INCLUDE_CHILDREN by\n // `setIncludeChildrenPolicy` (cross-boundary edges), and in that mode ELK\n // sizes a compound node to its contents and ignores the minimum.\n // 'nodeSize.constraints': '[MINIMUM_SIZE]',\n // 'nodeSize.minimum': '(446, 0)',\n};\n", "/* Geometry utilities extracted from render.ts for reuse and testing */\n\nexport interface P {\n x: number;\n y: number;\n}\n\nexport interface RectLike {\n x: number; // center x\n y: number; // center y\n width: number;\n height: number;\n padding?: number;\n}\n\nexport interface NodeLike {\n intersect?: (p: P) => P | null;\n}\n\nexport const EPS = 1;\nexport const PUSH_OUT = 10;\n\nexport const onBorder = (bounds: RectLike, p: P, tol = 0.5): boolean => {\n const halfW = bounds.width / 2;\n const halfH = bounds.height / 2;\n const left = bounds.x - halfW;\n const right = bounds.x + halfW;\n const top = bounds.y - halfH;\n const bottom = bounds.y + halfH;\n\n const onLeft = Math.abs(p.x - left) <= tol && p.y >= top - tol && p.y <= bottom + tol;\n const onRight = Math.abs(p.x - right) <= tol && p.y >= top - tol && p.y <= bottom + tol;\n const onTop = Math.abs(p.y - top) <= tol && p.x >= left - tol && p.x <= right + tol;\n const onBottom = Math.abs(p.y - bottom) <= tol && p.x >= left - tol && p.x <= right + tol;\n return onLeft || onRight || onTop || onBottom;\n};\n\n/**\n * Compute intersection between a rectangle (center x/y, width/height) and the line\n * segment from insidePoint -\\> outsidePoint. Returns the point on the rectangle border.\n *\n * This version avoids snapping to outsidePoint when certain variables evaluate to 0\n * (previously caused vertical top/bottom cases to miss the border). It only enforces\n * axis-constant behavior for purely vertical/horizontal approaches.\n */\nexport const intersection = (node: RectLike, outsidePoint: P, insidePoint: P): P => {\n const x = node.x;\n const y = node.y;\n\n const dx = Math.abs(x - insidePoint.x);\n const w = node.width / 2;\n let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;\n const h = node.height / 2;\n\n const Q = Math.abs(outsidePoint.y - insidePoint.y);\n const R = Math.abs(outsidePoint.x - insidePoint.x);\n\n if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {\n // Intersection is top or bottom of rect.\n const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;\n r = (R * q) / Q;\n const res = {\n x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,\n y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,\n };\n\n // Keep axis-constant special-cases only\n if (R === 0) {\n res.x = outsidePoint.x;\n }\n if (Q === 0) {\n res.y = outsidePoint.y;\n }\n return res;\n } else {\n // Intersection on sides of rect\n if (insidePoint.x < outsidePoint.x) {\n r = outsidePoint.x - w - x;\n } else {\n r = x - w - outsidePoint.x;\n }\n const q = (Q * r) / R;\n let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;\n let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;\n\n // Only handle axis-constant cases\n if (R === 0) {\n _x = outsidePoint.x;\n }\n if (Q === 0) {\n _y = outsidePoint.y;\n }\n\n return { x: _x, y: _y };\n }\n};\n\nexport const outsideNode = (node: RectLike, point: P): boolean => {\n const x = node.x;\n const y = node.y;\n const dx = Math.abs(point.x - x);\n const dy = Math.abs(point.y - y);\n const w = node.width / 2;\n const h = node.height / 2;\n return dx >= w || dy >= h;\n};\n\nexport const ensureTrulyOutside = (bounds: RectLike, p: P, push = PUSH_OUT): P => {\n const dx = Math.abs(p.x - bounds.x);\n const dy = Math.abs(p.y - bounds.y);\n const w = bounds.width / 2;\n const h = bounds.height / 2;\n if (Math.abs(dx - w) < EPS || Math.abs(dy - h) < EPS) {\n const dirX = p.x - bounds.x;\n const dirY = p.y - bounds.y;\n const len = Math.sqrt(dirX * dirX + dirY * dirY);\n if (len > 0) {\n return {\n x: bounds.x + (dirX / len) * (len + push),\n y: bounds.y + (dirY / len) * (len + push),\n };\n }\n }\n return p;\n};\n\nexport const makeInsidePoint = (bounds: RectLike, outside: P, center: P): P => {\n const isVertical = Math.abs(outside.x - bounds.x) < EPS;\n const isHorizontal = Math.abs(outside.y - bounds.y) < EPS;\n return {\n x: isVertical\n ? outside.x\n : outside.x < bounds.x\n ? bounds.x - bounds.width / 4\n : bounds.x + bounds.width / 4,\n y: isHorizontal ? outside.y : center.y,\n };\n};\n\nexport const tryNodeIntersect = (node: NodeLike, bounds: RectLike, outside: P): P | null => {\n if (!node?.intersect) {\n return null;\n }\n const res = node.intersect(outside);\n if (!res) {\n return null;\n }\n const wrongSide =\n (outside.x < bounds.x && res.x > bounds.x) || (outside.x > bounds.x && res.x < bounds.x);\n if (wrongSide) {\n return null;\n }\n const dist = Math.hypot(outside.x - res.x, outside.y - res.y);\n if (dist <= EPS) {\n return null;\n }\n return res;\n};\n\nexport const fallbackIntersection = (bounds: RectLike, outside: P, center: P): P => {\n const inside = makeInsidePoint(bounds, outside, center);\n return intersection(bounds, outside, inside);\n};\n\n/**\n * Bisection steps used to walk a ray onto the node outline.\n *\n * Each step halves the bracket and costs one `node.intersect()` call, and both\n * endpoints of every edge run this \u2014 so the count is paid twice per edge. 20\n * steps take a 200px starting bracket to about 2e-4px, which is four orders of\n * magnitude below anything that can be rendered; going further only buys\n * precision that the SVG coordinate is rounded away from anyway.\n */\nconst OUTLINE_RAY_STEPS = 20;\n\n/**\n * How far off an axis a departure may sit and still count as axis-aligned.\n *\n * ELK's orthogonal routing emits exact horizontal and vertical stubs, so this\n * only has to absorb floating-point dust, not a tolerance for near-diagonals.\n */\nconst DEPARTURE_AXIS_EPS = 1e-6;\n\n/**\n * Whether a point lies inside the node's outline.\n *\n * Derived from `intersect` alone, so it needs no per-shape knowledge: the\n * shape's `intersect` returns where the ray from the node CENTRE through the\n * probe leaves the outline, so the probe is inside exactly when it is no\n * further from the centre than that crossing is. Valid for any outline that is\n * star-shaped about its centre, which every built-in shape is.\n */\nconst insideOutline = (node: NodeLike, centre: P, probe: P): boolean => {\n const crossing = node.intersect?.(probe);\n if (!crossing) {\n return false;\n }\n const probeDist = Math.hypot(probe.x - centre.x, probe.y - centre.y);\n const outlineDist = Math.hypot(crossing.x - centre.x, crossing.y - centre.y);\n return probeDist <= outlineDist + 1e-9;\n};\n\n/**\n * Where the node's outline meets the ray that runs into the node from `port`,\n * against the direction the edge departs in.\n *\n * ELK routes to ports on the node's BOUNDING BOX, and always leaves one\n * perpendicular to the side it sits on. For a rectangle that port is already\n * the attachment point. For anything else the outline is inside the box, so the\n * attachment has to move inwards \u2014 and the direction it moves in decides\n * whether the edge stays orthogonal.\n *\n * Moving along the centre ray (what `intersect` does on its own) lands on the\n * outline at a DIFFERENT offset along the side than the port, so the opening\n * segment comes out diagonal and the edge visibly kinks as it leaves the shape.\n * Moving along the departure axis instead keeps the attachment collinear with\n * ELK's own stub: the edge leaves the outline, crosses the box, and carries on\n * in one straight line.\n *\n * Returns null when the ray cannot be resolved \u2014 no `intersect`, a departure\n * direction that is not axis-aligned, or an interior sample that is not\n * actually inside \u2014 leaving the caller on its existing path.\n */\nexport const outlineAttachPoint = (\n node: NodeLike,\n bounds: RectLike,\n port: P,\n next: P\n): P | null => {\n if (!node?.intersect) {\n return null;\n }\n\n const dx = next.x - port.x;\n const dy = next.y - port.y;\n if (dx === 0 && dy === 0) {\n return null;\n }\n\n // A diagonal departure has no single axis to preserve. The bisection below\n // walks along one axis holding the other fixed, so forcing a diagonal onto\n // its dominant axis would attach at a point the edge does not actually pass\n // through \u2014 reintroducing the offset this function exists to remove. Decline\n // instead, and let the caller fall back to the centre-ray intersection.\n if (Math.abs(dx) > DEPARTURE_AXIS_EPS && Math.abs(dy) > DEPARTURE_AXIS_EPS) {\n return null;\n }\n\n const centre = { x: bounds.x, y: bounds.y };\n const horizontal = Math.abs(dx) > Math.abs(dy);\n const along = (t: number): P => (horizontal ? { x: t, y: port.y } : { x: port.x, y: t });\n\n // Walk in from the centre-line towards the port: inside at one end, on or\n // outside the outline at the other.\n let inner = horizontal ? centre.x : centre.y;\n let outer = horizontal ? port.x : port.y;\n if (!insideOutline(node, centre, along(inner))) {\n return null;\n }\n if (insideOutline(node, centre, along(outer))) {\n // The port itself is on or inside the outline \u2014 it IS the attachment.\n return { ...port };\n }\n\n for (let step = 0; step < OUTLINE_RAY_STEPS; step++) {\n const mid = (inner + outer) / 2;\n if (insideOutline(node, centre, along(mid))) {\n inner = mid;\n } else {\n outer = mid;\n }\n }\n return along(inner);\n};\n\nexport const computeNodeIntersection = (\n node: NodeLike,\n bounds: RectLike,\n outside: P,\n center: P\n): P => {\n const outside2 = ensureTrulyOutside(bounds, outside);\n return tryNodeIntersect(node, bounds, outside2) ?? fallbackIntersection(bounds, outside2, center);\n};\n\nexport const replaceEndpoint = (\n points: P[],\n which: 'start' | 'end',\n value: P | null | undefined,\n tol = 0.1\n) => {\n if (!value || points.length === 0) {\n return;\n }\n\n if (which === 'start') {\n if (\n points.length > 0 &&\n Math.abs(points[0].x - value.x) < tol &&\n Math.abs(points[0].y - value.y) < tol\n ) {\n // duplicate start remove it\n points.shift();\n } else {\n points[0] = value;\n }\n } else {\n const last = points.length - 1;\n if (\n points.length > 0 &&\n Math.abs(points[last].x - value.x) < tol &&\n Math.abs(points[last].y - value.y) < tol\n ) {\n // duplicate end remove it\n points.pop();\n } else {\n points[last] = value;\n }\n }\n};\n"],
"mappings": "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",
"names": ["endBounds"]
}