{
  "version": 3,
  "sources": ["../../../src/rendering-util/rendering-elements/lineJump.ts"],
  "sourcesContent": ["/**\n * Line jumps (\"hops\") for edge crossings.\n *\n * Detects true segment crossings between edge polylines and rewrites the SVG\n * path of the later edge so the crossing renders as either a small arc\n * (`jumpStyle: 'arc'`) or a visible break (`jumpStyle: 'gap'`).\n *\n * The pure functions (`findEdgeIntersections`, `processEdgesWithJumps`) are\n * DOM-free. The DOM-side `applyLineJumpsToSvg` helper reads geometry from\n * layout data and leaves curved (non-`M`/`L`) rendered paths untouched.\n */\n\nimport type { D3Selection } from '../../types.js';\nimport { markerOffsets } from '../../utils/lineWithOffset.js';\n\n/** Radius used by edges.js' generateRoundedPath. Kept in sync so rewritten\n * paths look like the originals at bends. */\nconst ROUNDED_CORNER_RADIUS = 5;\n\n/** Skip the jump if its clamped radius falls below this \u2014 avoids invisible\n * zero-length arcs on very crowded paths. */\nconst CORNER_EPSILON = 1e-5;\n\n/**\n * Straight run kept between a hop and the bend next to it.\n *\n * Without it a hop may start exactly at the tangent point of a rounded corner,\n * so the path leaves the corner's quadratic and enters the arc with no straight\n * run between them. The two curves read as one malformed squiggle rather than\n * as a corner followed by a hop.\n */\nconst CORNER_JUMP_CLEARANCE = 2;\n\n/**\n * Smallest share of the requested radius a hop may shrink to before it is\n * dropped instead of drawn.\n *\n * A hop close to a bend has little room, and the clamps below will happily fit\n * one into whatever is left. That is the wrong trade: an arc at half radius no\n * longer clears the stroke it is meant to hop, so the lines still touch and the\n * result looks like a rendering fault rather than a crossing. An undrawn hop is\n * just an ordinary crossing, which is what every diagram looked like before\n * hops existed \u2014 a much better failure than a broken-looking one.\n *\n * This happens for real: ELK routes subgraph-internal edges into lanes 10px\n * apart, and a 10px offset cannot hold a 7.07px corner cut plus a 6px hop, so\n * every crossing in such a lane was being drawn at 2.9px hard against the bend.\n */\nconst MIN_USEFUL_RADIUS_RATIO = 0.6;\n\nexport interface Point {\n  x: number;\n  y: number;\n}\n\nexport interface EdgeGeom {\n  id: string;\n  points: Point[];\n  /**\n   * Optional curve hint matching `edge.curve` from the rendering layer.\n   * When set, line jumps are only applied for orthogonal-friendly curves\n   * (`'linear'`, `'rounded'`, `'step'`, `'stepBefore'`, `'stepAfter'`, or\n   * undefined). Other curves (basis, monotoneX, \u2026) are skipped to avoid\n   * corrupting smoothed geometry.\n   */\n  curve?: string;\n  /** Arrow type at the start (first point) \u2014 used to apply marker offset so\n   * the rewritten path's endpoint matches the original rendered geometry and\n   * the arrow marker orients correctly. */\n  arrowTypeStart?: string;\n  /** Arrow type at the end (last point). */\n  arrowTypeEnd?: string;\n}\n\nexport interface LineJumpConfig {\n  enabled: boolean;\n  jumpRadius: number;\n  jumpStyle: 'arc' | 'gap';\n}\n\nexport interface Crossing {\n  jumpEdgeId: string;\n  otherEdgeId: string;\n  /** Index of the segment within the jumping edge's polyline. */\n  segIndex: number;\n  /** Position of the crossing along the jumping edge's segment, 0..1. */\n  t: number;\n  point: Point;\n}\n\nconst ENDPOINT_EPSILON = 1e-6;\n\ninterface Segment {\n  a: Point;\n  b: Point;\n}\n\nfunction buildSegmentList(points: Point[]): Segment[] {\n  const segments: Segment[] = [];\n  for (let i = 0; i < points.length - 1; i++) {\n    segments.push({ a: points[i], b: points[i + 1] });\n  }\n  return segments;\n}\n\ninterface SegmentIntersection {\n  point: Point;\n  tA: number;\n  tB: number;\n}\n\n/**\n * Parametric segment-segment intersection. Returns null if the segments are\n * parallel, do not intersect, or only meet at one of their endpoints (within\n * `ENDPOINT_EPSILON`). Endpoint rejection prevents normal joins, T-junctions,\n * and shared-start edges from being treated as crossings.\n */\nfunction segmentIntersection(\n  a1: Point,\n  a2: Point,\n  b1: Point,\n  b2: Point\n): SegmentIntersection | null {\n  const dxA = a2.x - a1.x;\n  const dyA = a2.y - a1.y;\n  const dxB = b2.x - b1.x;\n  const dyB = b2.y - b1.y;\n\n  const denom = dxA * dyB - dyA * dxB;\n  if (denom === 0) {\n    return null;\n  }\n\n  const dx = b1.x - a1.x;\n  const dy = b1.y - a1.y;\n\n  const tA = (dx * dyB - dy * dxB) / denom;\n  const tB = (dx * dyA - dy * dxA) / denom;\n\n  if (\n    tA <= ENDPOINT_EPSILON ||\n    tA >= 1 - ENDPOINT_EPSILON ||\n    tB <= ENDPOINT_EPSILON ||\n    tB >= 1 - ENDPOINT_EPSILON\n  ) {\n    return null;\n  }\n\n  return {\n    point: { x: a1.x + tA * dxA, y: a1.y + tA * dyA },\n    tA,\n    tB,\n  };\n}\n\n/** True if the segment is horizontally dominant (abs(dx) is at least abs(dy)).\n * Ties go to horizontal to keep pure-diagonal edges grouped with the\n * horizontal bucket \u2014 they don't occur in orthogonal layouts anyway. */\nfunction isHorizontalSeg(seg: Segment): boolean {\n  return Math.abs(seg.b.x - seg.a.x) >= Math.abs(seg.b.y - seg.a.y);\n}\n\n/**\n * True if a crossing on `edge`'s segment `segIndex` at parameter `t` falls\n * inside the stretch where the drawn stroke has left the polyline to round a\n * bend.\n *\n * Crossings are found on polylines, but a `rounded` edge is not drawn as its\n * polyline: `generateRoundedPath` replaces each bend with a quadratic that\n * departs the line up to `cutLen` before the vertex and rejoins it `cutLen`\n * after. Inside that stretch the polyline says the stroke is somewhere it is\n * not, so a \"crossing\" computed there is at best mislocated and at worst\n * fictional \u2014 and a hop drawn for it arches over blank paper while the two\n * strokes still touch alongside it.\n *\n * Only `rounded` edges lie this way; every other supported curve is drawn as\n * the polyline it describes.\n */\nfunction crossingSitsInRoundedCorner(edge: EdgeGeom, segIndex: number, t: number): boolean {\n  if (edge.curve !== 'rounded') {\n    return false;\n  }\n  const pts = edge.points;\n  const a = pts[segIndex];\n  const b = pts[segIndex + 1];\n  if (!a || !b) {\n    return false;\n  }\n  const segLen = Math.hypot(b.x - a.x, b.y - a.y);\n  const d = t * segLen;\n\n  const entering =\n    segIndex > 0 ? computeRoundedCorner(pts[segIndex - 1], a, b, ROUNDED_CORNER_RADIUS) : null;\n  if (entering && d < entering.cutLen) {\n    return true;\n  }\n\n  const leaving =\n    segIndex + 2 < pts.length\n      ? computeRoundedCorner(a, b, pts[segIndex + 2], ROUNDED_CORNER_RADIUS)\n      : null;\n  return leaving !== null && segLen - d < leaving.cutLen;\n}\n\nexport function findEdgeIntersections(edges: EdgeGeom[]): Crossing[] {\n  const crossings: Crossing[] = [];\n\n  for (let i = 0; i < edges.length; i++) {\n    const edgeA = edges[i];\n    const segmentsA = buildSegmentList(edgeA.points);\n    for (let j = i + 1; j < edges.length; j++) {\n      const edgeB = edges[j];\n      const segmentsB = buildSegmentList(edgeB.points);\n\n      for (const [si, segA] of segmentsA.entries()) {\n        for (const [sj, segB] of segmentsB.entries()) {\n          const hit = segmentIntersection(segA.a, segA.b, segB.a, segB.b);\n          if (!hit) {\n            continue;\n          }\n\n          // Either edge rounding a bend here means the polyline is not where\n          // the stroke is, so there is nothing trustworthy to hop over.\n          if (\n            crossingSitsInRoundedCorner(edgeA, si, hit.tA) ||\n            crossingSitsInRoundedCorner(edgeB, sj, hit.tB)\n          ) {\n            continue;\n          }\n\n          // Orthogonal-orientation rule: when one segment is horizontal-\n          // dominant and the other vertical-dominant, the HORIZONTAL one\n          // gets the jump (classic line-hop convention \u2014 arcs arch upward\n          // over the vertical line beneath). Falls back to later-index-wins\n          // when both segments share an orientation.\n          const aHoriz = isHorizontalSeg(segA);\n          const bHoriz = isHorizontalSeg(segB);\n          const orthogonalPair = aHoriz !== bHoriz;\n          const jumpOnA = orthogonalPair ? aHoriz : false;\n\n          if (jumpOnA) {\n            crossings.push({\n              jumpEdgeId: edgeA.id,\n              otherEdgeId: edgeB.id,\n              segIndex: si,\n              t: hit.tA,\n              point: hit.point,\n            });\n          } else {\n            crossings.push({\n              jumpEdgeId: edgeB.id,\n              otherEdgeId: edgeA.id,\n              segIndex: sj,\n              t: hit.tB,\n              point: hit.point,\n            });\n          }\n        }\n      }\n    }\n  }\n\n  return crossings;\n}\n\nfunction fmt(n: number): string {\n  // Strip trailing zeros so \"5.00\" \u2192 \"5\"; keep up to 3 decimals otherwise.\n  const rounded = Math.round(n * 1000) / 1000;\n  return Number.isInteger(rounded) ? `${rounded}` : `${rounded}`;\n}\n\nfunction pointToString(p: Point): string {\n  return `${fmt(p.x)},${fmt(p.y)}`;\n}\n\n/**\n * Determines the SVG arc sweep flag so the jump bumps in the conventional\n * direction: horizontal segments bump up (smaller y in SVG), vertical segments\n * bump right (larger x).\n */\nfunction getArcSweepFlag(seg: Segment): 0 | 1 {\n  const dx = seg.b.x - seg.a.x;\n  const dy = seg.b.y - seg.a.y;\n  if (Math.abs(dx) >= Math.abs(dy)) {\n    // Horizontal-dominant: bump up (smaller y in SVG's y-down frame).\n    // Going +x \u2192 sweep=1 sweeps through increasing angle 180\u00B0\u2192270\u00B0\u21920\u00B0,\n    //   which passes through (mid, y-r) = up.\n    // Going -x \u2192 sweep=0 (reverse direction) also lands the bump above.\n    return dx >= 0 ? 1 : 0;\n  }\n  // Vertical-dominant: bump right (positive x).\n  // Going +y \u2192 sweep=1; going -y \u2192 sweep=0.\n  return dy >= 0 ? 1 : 0;\n}\n\ninterface JumpOnSegment {\n  t: number;\n  point: Point;\n  /** Distance from segment start along the segment direction. */\n  d: number;\n  /** Effective radius after boundary + adjacency clamping. */\n  r: number;\n}\n\n/**\n * Shifts the first/last point inward along the edge direction by the amount\n * required for their arrow markers, matching `applyMarkerOffsetsToPoints` in\n * edges.js so the rewritten path ends exactly where the original did.\n */\nfunction applyMarkerOffsets(points: Point[], edge: EdgeGeom): Point[] {\n  if (points.length < 2) {\n    return points.map((p) => ({ ...p }));\n  }\n  const out = points.map((p) => ({ ...p }));\n  const startOff =\n    edge.arrowTypeStart && markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets];\n  if (startOff) {\n    const a = points[0];\n    const b = points[1];\n    const ang = Math.atan2(b.y - a.y, b.x - a.x);\n    out[0].x = a.x + startOff * Math.cos(ang);\n    out[0].y = a.y + startOff * Math.sin(ang);\n  }\n  const endOff =\n    edge.arrowTypeEnd && markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets];\n  if (endOff) {\n    const n = points.length;\n    const a = points[n - 2];\n    const b = points[n - 1];\n    const ang = Math.atan2(b.y - a.y, b.x - a.x);\n    out[n - 1].x = b.x - endOff * Math.cos(ang);\n    out[n - 1].y = b.y - endOff * Math.sin(ang);\n  }\n  return out;\n}\n\n/**\n * Emits the arc or gap command for a crossing, in the segment's direction.\n * Returns the part strings; caller inserts them in order.\n */\nfunction emitJump(\n  jump: JumpOnSegment,\n  ux: number,\n  uy: number,\n  sweep: 0 | 1,\n  style: 'arc' | 'gap'\n): string[] {\n  const cx = jump.point.x;\n  const cy = jump.point.y;\n  const pre = { x: cx - ux * jump.r, y: cy - uy * jump.r };\n  const post = { x: cx + ux * jump.r, y: cy + uy * jump.r };\n  const out = [`L${pointToString(pre)}`];\n  if (style === 'arc') {\n    out.push(`A${fmt(jump.r)},${fmt(jump.r)} 0 0 ${sweep} ${pointToString(post)}`);\n  } else {\n    out.push(`M${pointToString(post)}`);\n  }\n  return out;\n}\n\n/**\n * Mirrors the corner-rounding logic of `generateRoundedPath` in edges.js:\n * given a bend at `curr` between segments `prev\u2192curr` and `curr\u2192next`,\n * computes (startX, startY) just before curr on the incoming segment and\n * (endX, endY) just after curr on the outgoing segment, plus the Q control\n * point (which is curr itself). Returns `null` if the angle is degenerate\n * and the caller should just emit a straight `L curr`.\n */\ninterface RoundedCorner {\n  startX: number;\n  startY: number;\n  endX: number;\n  endY: number;\n  ctrlX: number;\n  ctrlY: number;\n  /** How much the start of the rounded corner eats into the incoming segment. */\n  cutLen: number;\n}\nfunction computeRoundedCorner(\n  prev: Point,\n  curr: Point,\n  next: Point,\n  radius: number\n): RoundedCorner | null {\n  const dx1 = curr.x - prev.x;\n  const dy1 = curr.y - prev.y;\n  const dx2 = next.x - curr.x;\n  const dy2 = next.y - curr.y;\n  const len1 = Math.hypot(dx1, dy1);\n  const len2 = Math.hypot(dx2, dy2);\n  if (len1 < CORNER_EPSILON || len2 < CORNER_EPSILON) {\n    return null;\n  }\n  const nx1 = dx1 / len1;\n  const ny1 = dy1 / len1;\n  const nx2 = dx2 / len2;\n  const ny2 = dy2 / len2;\n  const dot = nx1 * nx2 + ny1 * ny2;\n  const clamped = Math.max(-1, Math.min(1, dot));\n  const angle = Math.acos(clamped);\n  if (angle < CORNER_EPSILON || Math.abs(Math.PI - angle) < CORNER_EPSILON) {\n    return null;\n  }\n  const cutLen = Math.min(radius / Math.sin(angle / 2), len1 / 2, len2 / 2);\n  return {\n    startX: curr.x - nx1 * cutLen,\n    startY: curr.y - ny1 * cutLen,\n    endX: curr.x + nx2 * cutLen,\n    endY: curr.y + ny2 * cutLen,\n    ctrlX: curr.x,\n    ctrlY: curr.y,\n    cutLen,\n  };\n}\n\nfunction rewriteEdgePath(edge: EdgeGeom, jumps: Crossing[], config: LineJumpConfig): string {\n  const rawPoints = edge.points;\n  if (rawPoints.length < 2) {\n    return '';\n  }\n\n  // Match edges.js: shift the first/last point inward so arrow markers line up.\n  const points = applyMarkerOffsets(rawPoints, edge);\n  const rounded = edge.curve === 'rounded';\n\n  // Jumps are indexed into the ORIGINAL (un-offset) segment list. For mid-\n  // segments (i > 0 and i < n-2) the offsets don't change anything, and for\n  // the first/last segment the shift is tiny compared to jump radius so\n  // reusing the same (segIndex, t) is fine.\n  const segments = buildSegmentList(points);\n  const bySeg = new Map<number, JumpOnSegment[]>();\n  for (const j of jumps) {\n    const seg = segments[j.segIndex];\n    if (!seg) {\n      continue;\n    }\n    const segLen = Math.hypot(seg.b.x - seg.a.x, seg.b.y - seg.a.y);\n    const list = bySeg.get(j.segIndex) ?? [];\n    list.push({\n      t: j.t,\n      point: j.point,\n      d: j.t * segLen,\n      r: config.jumpRadius,\n    });\n    bySeg.set(j.segIndex, list);\n  }\n\n  const parts: string[] = [`M${pointToString(points[0])}`];\n  // Running cursor along the current segment measured from seg.a.\n  // Consumed at the front by the previous corner's cutLen (for rounded) and\n  // after that by mid-segment jumps.\n  for (let i = 0; i < segments.length; i++) {\n    const seg = segments[i];\n    const segLen = Math.hypot(seg.b.x - seg.a.x, seg.b.y - seg.a.y);\n    const ux = segLen === 0 ? 0 : (seg.b.x - seg.a.x) / segLen;\n    const uy = segLen === 0 ? 0 : (seg.b.y - seg.a.y) / segLen;\n    const sweep = getArcSweepFlag(seg);\n\n    // How much of the front of this segment was consumed by the previous\n    // corner's Q end-point (endX,endY). Default 0.\n    let segStartConsumed = 0;\n    if (rounded && i > 0) {\n      const corner = computeRoundedCorner(\n        points[i - 1],\n        points[i],\n        points[i + 1] ?? points[i],\n        ROUNDED_CORNER_RADIUS\n      );\n      if (corner) {\n        segStartConsumed = corner.cutLen;\n      }\n    }\n\n    // Rounded: if there's a next corner ahead, we stop short of it by cutLen.\n    let segEndStop = segLen;\n    let upcomingCorner: RoundedCorner | null = null;\n    if (rounded && i < segments.length - 1) {\n      upcomingCorner = computeRoundedCorner(\n        points[i],\n        points[i + 1],\n        points[i + 2] ?? points[i + 1],\n        ROUNDED_CORNER_RADIUS\n      );\n      if (upcomingCorner) {\n        segEndStop = segLen - upcomingCorner.cutLen;\n      }\n    }\n\n    // Clamp each jump to the room between the bends at either end of the\n    // segment, then drop the ones with too little room to be worth drawing.\n    // Dropping happens BEFORE the adjacency pass below so that a hop being\n    // squeezed out by a corner does not also shrink its neighbours.\n    const minUsefulRadius = config.jumpRadius * MIN_USEFUL_RADIUS_RATIO;\n    const segJumps = [...(bySeg.get(i) ?? [])]\n      .sort((a, b) => a.t - b.t)\n      .filter((j) => {\n        const room = Math.min(j.d - segStartConsumed, segEndStop - j.d) - CORNER_JUMP_CLEARANCE;\n        j.r = Math.min(j.r, room);\n        return j.r >= minUsefulRadius;\n      });\n    for (let k = 0; k < segJumps.length - 1; k++) {\n      const gap = segJumps[k + 1].d - segJumps[k].d;\n      if (segJumps[k].r + segJumps[k + 1].r > gap) {\n        const half = gap / 2;\n        segJumps[k].r = Math.min(segJumps[k].r, half);\n        segJumps[k + 1].r = Math.min(segJumps[k + 1].r, half);\n      }\n    }\n\n    for (const j of segJumps) {\n      // Checked AGAIN after the adjacency pass, not only before it. That pass\n      // can halve a radius to keep two hops off each other, and a hop shrunk\n      // that way is just as unreadable as one squeezed by a bend \u2014 same rule,\n      // both times. Two crossings too close to carry a hop each carry none.\n      if (j.r < minUsefulRadius) {\n        continue;\n      }\n      parts.push(...emitJump(j, ux, uy, sweep, config.jumpStyle));\n    }\n\n    // End of segment: either a straight L to seg.b (last segment or linear),\n    // or a Q-corner into seg.b's neighborhood (rounded, middle).\n    if (rounded && upcomingCorner) {\n      parts.push(`L${fmt(upcomingCorner.startX)},${fmt(upcomingCorner.startY)}`);\n      parts.push(\n        `Q${fmt(upcomingCorner.ctrlX)},${fmt(upcomingCorner.ctrlY)} ${fmt(upcomingCorner.endX)},${fmt(upcomingCorner.endY)}`\n      );\n    } else {\n      parts.push(`L${pointToString(seg.b)}`);\n    }\n  }\n\n  return parts.join(' ');\n}\n\nfunction plainPath(points: Point[]): string {\n  if (points.length === 0) {\n    return '';\n  }\n  const parts = [`M${pointToString(points[0])}`];\n  for (let i = 1; i < points.length; i++) {\n    parts.push(`L${pointToString(points[i])}`);\n  }\n  return parts.join(' ');\n}\n\nexport function processEdgesWithJumps(\n  edges: EdgeGeom[],\n  config: LineJumpConfig\n): Map<string, string> {\n  const result = new Map<string, string>();\n\n  if (!config.enabled) {\n    for (const edge of edges) {\n      result.set(edge.id, plainPath(edge.points));\n    }\n    return result;\n  }\n\n  const crossings = findEdgeIntersections(edges);\n  const jumpsByEdge = new Map<string, Crossing[]>();\n  for (const c of crossings) {\n    const list = jumpsByEdge.get(c.jumpEdgeId) ?? [];\n    list.push(c);\n    jumpsByEdge.set(c.jumpEdgeId, list);\n  }\n\n  for (const edge of edges) {\n    const jumps = jumpsByEdge.get(edge.id);\n    if (!jumps || jumps.length === 0) {\n      result.set(edge.id, plainPath(edge.points));\n    } else {\n      result.set(edge.id, rewriteEdgePath(edge, jumps, config));\n    }\n  }\n\n  return result;\n}\n\n/**\n * Returns true iff the SVG path `d` is a straight-line path \u2014 only `M`/`L`/`m`/`l`\n * move/line commands plus their numeric coordinates (digits, sign, decimal point,\n * scientific-notation `e`, and `,`/space separators). Curved paths are skipped by\n * the caller.\n */\nexport function isStraightPath(d: string): boolean {\n  return /^[\\d\\s+,.LMelm-]*$/.test(d);\n}\n\n/**\n * Returns true iff the named curve produces orthogonal-friendly segments that\n * can be safely re-emitted with line jumps. Includes `'rounded'` even though\n * its rendered `d` contains `Q` corner-rounding commands \u2014 when an edge with\n * a jump is rewritten the corner rounding is dropped in exchange for visible\n * arc hops at crossings, which is the desired trade-off.\n */\nexport function curveSupportsLineHops(curve: string | undefined): boolean {\n  if (!curve) {\n    return true;\n  }\n  return (\n    curve === 'linear' ||\n    curve === 'rounded' ||\n    curve === 'step' ||\n    curve === 'stepBefore' ||\n    curve === 'stepAfter'\n  );\n}\n\n/**\n * Decodes the `data-points` attribute set by edges.js at render time. This\n * gives us the exact point list edges.js used to emit the rendered path \u2014\n * i.e. after node-boundary `intersect()` clipping and any orthogonalization,\n * but BEFORE `applyMarkerOffsetsToPoints`. Using these points guarantees the\n * rewrite's endpoints match the original rendered endpoints.\n */\nfunction decodeDataPoints(raw: string | null): Point[] | null {\n  if (!raw) {\n    return null;\n  }\n  try {\n    const json = typeof atob === 'function' ? atob(raw) : Buffer.from(raw, 'base64').toString();\n    const parsed = JSON.parse(json);\n    if (!Array.isArray(parsed)) {\n      return null;\n    }\n    const pts: Point[] = [];\n    for (const p of parsed) {\n      if (p && typeof p.x === 'number' && typeof p.y === 'number') {\n        pts.push({ x: p.x, y: p.y });\n      }\n    }\n    return pts.length >= 2 ? pts : null;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Patches the rendered SVG paths in `edgePathsGroup` for any edges that\n * cross. The true geometry is read from each path's `data-points` attribute\n * (written by edges.js at render time) so the rewrite's endpoints match\n * exactly what was originally rendered. Edges whose curve is a true\n * smoothing curve (`basis`, `monotoneX`, \u2026) are skipped.\n */\nexport function applyLineJumpsToSvg(\n  edgePathsGroup: D3Selection<SVGGElement>,\n  edges: EdgeGeom[],\n  config: LineJumpConfig\n): void {\n  if (!config.enabled) {\n    return;\n  }\n\n  const groupNode = edgePathsGroup.node();\n  if (!groupNode) {\n    return;\n  }\n\n  // Build a metadata lookup so per-edge properties (curve, arrow types)\n  // survive the DOM round-trip.\n  const edgeMeta = new Map<string, EdgeGeom>();\n  for (const e of edges) {\n    edgeMeta.set(e.id, e);\n  }\n\n  // Collect geometry from each path's data-points, preferring that over the\n  // incoming `edges[].points` which came from pre-render layout state.\n  // Index the paths by their own `data-id` instead of building one selector per\n  // edge. An id is author-controlled, so interpolating it into a selector needs\n  // `CSS.escape`, which is not guaranteed outside a browser \u2014 and the fallback\n  // of using the id raw turns a trailing backslash into a `SyntaxError` that\n  // aborts the whole render. Reading the attribute avoids the selector entirely.\n  const pathByDataId = new Map<string, Element>();\n  for (const el of groupNode.querySelectorAll('path[data-id]')) {\n    const id = el.getAttribute('data-id');\n    if (id !== null && !pathByDataId.has(id)) {\n      pathByDataId.set(id, el);\n    }\n  }\n\n  const renderedEdges: EdgeGeom[] = [];\n  for (const e of edges) {\n    const pathEl = pathByDataId.get(e.id);\n    if (!pathEl) {\n      continue;\n    }\n    const decoded = decodeDataPoints(pathEl.getAttribute('data-points'));\n    const points = decoded ?? e.points;\n    renderedEdges.push({ ...e, points });\n  }\n\n  const crossings = findEdgeIntersections(renderedEdges);\n  if (crossings.length === 0) {\n    return;\n  }\n\n  const jumpsByEdge = new Map<string, Crossing[]>();\n  for (const c of crossings) {\n    const list = jumpsByEdge.get(c.jumpEdgeId) ?? [];\n    list.push(c);\n    jumpsByEdge.set(c.jumpEdgeId, list);\n  }\n\n  for (const renderedEdge of renderedEdges) {\n    const jumps = jumpsByEdge.get(renderedEdge.id);\n    if (!jumps || jumps.length === 0) {\n      continue;\n    }\n    const meta = edgeMeta.get(renderedEdge.id);\n    const curveHint = meta?.curve;\n    if (curveHint !== undefined && !curveSupportsLineHops(curveHint)) {\n      continue;\n    }\n\n    const pathEl = pathByDataId.get(renderedEdge.id);\n    if (!pathEl) {\n      continue;\n    }\n\n    if (curveHint === undefined) {\n      const currentD = pathEl.getAttribute('d') ?? '';\n      if (!isStraightPath(currentD)) {\n        continue;\n      }\n    }\n\n    // Read the ORIGINAL stroke-dasharray before rewriting so we can\n    // recompute it against the new total length. The `neo` look emits:\n    //   stroke-dasharray: 0 <oValueS> <len - oValueS - oValueE> <oValueE>;\n    // which hides the first oValueS and last oValueE pixels of the stroke\n    // \u2014 this is what actually prevents the stroke from poking into the arrow\n    // marker body. Our rewritten path has a different length, so without\n    // updating the \"on\" portion the hidden tail ends up in the wrong place.\n    const originalStyle = pathEl.getAttribute('style') ?? '';\n    const dasharrayMatch = /stroke-dasharray\\s*:\\s*0\\s+([\\d.]+)\\s+[\\d.]+\\s+([\\d.]+)/.exec(\n      originalStyle\n    );\n    const preservedOValueS = dasharrayMatch ? Number.parseFloat(dasharrayMatch[1]) : null;\n    const preservedOValueE = dasharrayMatch ? Number.parseFloat(dasharrayMatch[2]) : null;\n\n    const newD = rewriteEdgePath(renderedEdge, jumps, config);\n    pathEl.setAttribute('d', newD);\n\n    if (\n      preservedOValueS !== null &&\n      preservedOValueE !== null &&\n      typeof (pathEl as SVGPathElement).getTotalLength === 'function'\n    ) {\n      const newLen = (pathEl as SVGPathElement).getTotalLength();\n      const onLen = Math.max(0, newLen - preservedOValueS - preservedOValueE);\n      const newDasharray = `0 ${preservedOValueS} ${onLen} ${preservedOValueE}`;\n      const cleaned = originalStyle\n        .replace(/stroke-dasharray\\s*:[^;]*;?/g, `stroke-dasharray: ${newDasharray};`)\n        .replace(/;\\s*;+/g, ';');\n      pathEl.setAttribute('style', cleaned);\n    }\n  }\n}\n"],
  "mappings": "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",
  "names": ["ROUNDED_CORNER_RADIUS", "CORNER_EPSILON", "CORNER_JUMP_CLEARANCE", "MIN_USEFUL_RADIUS_RATIO", "ENDPOINT_EPSILON", "buildSegmentList", "points", "segments", "i", "__name", "segmentIntersection", "a1", "a2", "b1", "b2", "dxA", "dyA", "dxB", "dyB", "denom", "dx", "dy", "tA", "tB", "isHorizontalSeg", "seg", "crossingSitsInRoundedCorner", "edge", "segIndex", "t", "pts", "a", "b", "segLen", "entering", "computeRoundedCorner", "leaving", "findEdgeIntersections", "edges", "crossings", "edgeA", "segmentsA", "j", "edgeB", "segmentsB", "si", "segA", "sj", "segB", "hit", "aHoriz", "bHoriz", "fmt", "n", "rounded", "pointToString", "p", "getArcSweepFlag", "applyMarkerOffsets", "out", "startOff", "markerOffsets", "ang", "endOff", "emitJump", "jump", "ux", "uy", "sweep", "style", "cx", "cy", "pre", "post", "prev", "curr", "next", "radius", "dx1", "dy1", "dx2", "dy2", "len1", "len2", "nx1", "ny1", "nx2", "ny2", "dot", "clamped", "angle", "cutLen", "rewriteEdgePath", "jumps", "config", "rawPoints", "bySeg", "list", "parts", "segStartConsumed", "corner", "segEndStop", "upcomingCorner", "minUsefulRadius", "segJumps", "room", "k", "gap", "half", "isStraightPath", "d", "__name", "curveSupportsLineHops", "curve", "decodeDataPoints", "raw", "json", "parsed", "pts", "p", "applyLineJumpsToSvg", "edgePathsGroup", "edges", "config", "groupNode", "edgeMeta", "e", "pathByDataId", "el", "id", "renderedEdges", "pathEl", "points", "crossings", "findEdgeIntersections", "jumpsByEdge", "c", "list", "renderedEdge", "jumps", "curveHint", "currentD", "originalStyle", "dasharrayMatch", "preservedOValueS", "preservedOValueE", "newD", "rewriteEdgePath", "newLen", "onLen", "newDasharray", "cleaned"]
}
