@willcgage/module-schematic 0.13.1 → 0.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -35,10 +35,13 @@ function emptyEditorState(lengthInches) {
35
35
  controlPoints: []
36
36
  };
37
37
  }
38
+ function isTransitionTurnout(t) {
39
+ return t.onTrack === MAIN_TRACK_ID && t.divergeTrack === MAIN2_TRACK_ID || t.onTrack === MAIN2_TRACK_ID && t.divergeTrack === MAIN_TRACK_ID;
40
+ }
38
41
  function main2Track(state) {
39
42
  const bothDouble = state.configA === "double" && state.configB === "double";
40
- const sw = state.turnouts.find((t) => t.divergeTrack === MAIN2_TRACK_ID);
41
- if (bothDouble || !sw) {
43
+ const sw = state.turnouts.find(isTransitionTurnout);
44
+ if (bothDouble || !sw || sw.divergeTrack !== MAIN2_TRACK_ID) {
42
45
  return { id: MAIN2_TRACK_ID, role: "main", lane: 1, from: "A", to: "B" };
43
46
  }
44
47
  return state.configA === "double" ? (
@@ -61,11 +64,13 @@ function buildTransition(state) {
61
64
  id: swId,
62
65
  name: "End of Double Track",
63
66
  pos,
64
- onTrack: MAIN_TRACK_ID,
65
- divergeTrack: MAIN2_TRACK_ID,
66
- // Main 2 sits above (lane 1); the throw that runs toward the double end is
67
- // left when that end is east (single at A), right when it's west (#bug1).
68
- kind: aDouble ? "right" : "left"
67
+ // The turnout sits ON the ending main (Main 2, the upper track) and diverges
68
+ // down to the continuous Main 1 — the modeller's view of the junction. Left
69
+ // hand when the double end is west (Main 2 comes down going east), right
70
+ // when it's east (mirror).
71
+ onTrack: MAIN2_TRACK_ID,
72
+ divergeTrack: MAIN_TRACK_ID,
73
+ kind: aDouble ? "left" : "right"
69
74
  };
70
75
  const cpId = nextId("cp", state.controlPoints.map((c) => c.id));
71
76
  const sig = (facing) => ({
@@ -518,25 +523,31 @@ function moduleFeatures(doc) {
518
523
  });
519
524
  }
520
525
  const isMainId = (id) => !!id && trackById.get(id)?.role === "main";
526
+ const epDouble = (id) => (doc.endplates.find((e) => e.id === id)?.tracks ?? []).some(
527
+ (t) => t.config === "double"
528
+ );
529
+ const aDbl = epDouble("A");
530
+ const bDbl = epDouble("B");
531
+ const transitionSw = aDbl !== bDbl ? (doc.turnouts ?? []).find(isTransitionTurnout) : void 0;
521
532
  const m2m = (doc.turnouts ?? []).filter(
522
- (sw) => isMainId(sw.onTrack) && isMainId(sw.divergeTrack) && (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1)
533
+ (sw) => sw !== transitionSw && isMainId(sw.onTrack) && isMainId(sw.divergeTrack) && (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1)
523
534
  );
524
535
  const usedLegs = /* @__PURE__ */ new Set();
525
536
  for (const t1 of m2m) {
526
537
  if (usedLegs.has(t1.id)) continue;
527
- usedLegs.add(t1.id);
528
538
  const t2 = m2m.find(
529
- (x) => !usedLegs.has(x.id) && x.onTrack === t1.divergeTrack && x.divergeTrack === t1.onTrack
539
+ (x) => !usedLegs.has(x.id) && x.id !== t1.id && x.onTrack === t1.divergeTrack && x.divergeTrack === t1.onTrack
530
540
  );
531
- if (t2) usedLegs.add(t2.id);
532
- const end = t2 ?? t1;
541
+ if (!t2) continue;
542
+ usedLegs.add(t1.id);
543
+ usedLegs.add(t2.id);
533
544
  crossovers.push({
534
- id: t2 ? `${t1.id}-${t2.id}` : t1.id,
535
- name: t1.name ?? t2?.name ?? "",
545
+ id: `${t1.id}-${t2.id}`,
546
+ name: t1.name ?? t2.name ?? "",
536
547
  fromPosFrac: clampFrac(t1.pos),
537
548
  fromLane: trackLane.get(t1.onTrack) ?? 0,
538
- toPosFrac: clampFrac(end === t1 ? t1.pos : end.pos),
539
- toLane: trackLane.get(t1.divergeTrack) ?? 1
549
+ toPosFrac: clampFrac(t2.pos),
550
+ toLane: trackLane.get(t2.onTrack) ?? 1
540
551
  });
541
552
  }
542
553
  const turnouts = (doc.turnouts ?? []).map((t) => ({
@@ -590,14 +601,26 @@ function moduleFeatures(doc) {
590
601
  const loop = isLoopDoc(doc);
591
602
  const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);
592
603
  const main2Positioned = !!main2 && (main2.fromPos != null || main2.toPos != null) && !loop;
593
- const main2Extent = main2Positioned ? {
594
- fromFrac: clampFrac(main2.fromPos ?? 0),
595
- toFrac: clampFrac(main2.toPos ?? len)
604
+ let main2Extent = null;
605
+ if (main2Positioned) {
606
+ main2Extent = {
607
+ fromFrac: clampFrac(main2.fromPos ?? 0),
608
+ toFrac: clampFrac(main2.toPos ?? len)
609
+ };
610
+ } else if (main2 && transitionSw && transitionSw.divergeTrack === MAIN2_TRACK_ID && !loop) {
611
+ main2Extent = aDbl ? { fromFrac: 0, toFrac: clampFrac(transitionSw.pos) } : { fromFrac: clampFrac(transitionSw.pos), toFrac: 1 };
612
+ }
613
+ const transition = transitionSw && aDbl !== bDbl && !loop ? {
614
+ throughLane: trackLane.get(transitionSw.onTrack) ?? 0,
615
+ branchLane: trackLane.get(transitionSw.divergeTrack) ?? 1,
616
+ atFrac: clampFrac(transitionSw.pos),
617
+ doubleSide: aDbl ? "west" : "east"
596
618
  } : null;
597
619
  return {
598
620
  doubleMain,
599
621
  loop,
600
622
  main2Extent,
623
+ transition,
601
624
  loopInterchange: loop && doc.endplates.filter((e) => !e.at).length >= 2,
602
625
  loopReturn: loop && doc.loopReturn === "main2" ? "main2" : "same",
603
626
  loopRender: loop ? doc.loopRender ?? null : null,
@@ -723,6 +746,7 @@ exports.emptyEditorState = emptyEditorState;
723
746
  exports.geometryTurnDegrees = geometryTurnDegrees;
724
747
  exports.inchesToScaleFeet = inchesToScaleFeet;
725
748
  exports.isLoopDoc = isLoopDoc;
749
+ exports.isTransitionTurnout = isTransitionTurnout;
726
750
  exports.moduleFeatures = moduleFeatures;
727
751
  exports.nextId = nextId;
728
752
  exports.poseNeedsManual = poseNeedsManual;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @willcgage/module-schematic — the shared module operations-schematic (track-graph)\n * that the Module Repository authors and Free-Dispatcher imports.\n *\n * Topological, straightened-first: positions are 1-D inches along the module\n * (from endplate A), lanes are integer track indices (0 = primary main). This is\n * the single source of truth for both apps — the doc types, the lenient parser\n * (docs arrive as jsonb / unknown), the pure feature resolver both renderers\n * draw, the N-scale helpers, and the editor <-> doc state machine an authoring\n * UI binds to. See docs/module-schematic-format.md in the free-dispatcher repo.\n *\n * Framework-agnostic and side-effect-free so it can be unit-tested and consumed\n * from Next.js (server + client) and Electron alike.\n */\n\nexport type TrackConfig = \"single\" | \"double\";\nexport type TrackRole = \"main\" | \"siding\" | \"spur\" | \"yard\" | \"crossover\";\nexport type TurnoutKind = \"left\" | \"right\" | \"wye\";\nexport type SignalFacing = \"AtoB\" | \"BtoA\";\nexport type SignalSide = \"above\" | \"below\";\n\nexport interface SchematicEndplateTrack {\n trackId: string;\n lane: number;\n config?: TrackConfig | null;\n}\nexport interface SchematicEndplate {\n id: string; // \"A\" (West) | \"B\" (East) | \"C\"… (branch, #170)\n label?: string | null;\n tracks?: SchematicEndplateTrack[];\n /** Branch endplates (#170): where a 3rd+ endplate sits — pos inches from A,\n * on the up (north/above) or down side. Absent = axial (A at 0, B at\n * lengthInches). Renderers draw a named connector arrow (the CATS/US&S\n * off-band idiom) until branch spines land. */\n at?: { pos: number; side: \"up\" | \"down\" };\n /** Manual pose override (#175 phase 1b) — the endplate's module-local track\n * point (x, y inches) + outward-normal heading (°). Hand-entered for shapes\n * the geometry fields can't derive (wye, freeform, loop); wins over\n * deriveEndplatePoses' derivation. */\n pose?: { x: number; y: number; heading: number };\n}\nexport interface SchematicTrack {\n id: string;\n role: TrackRole;\n lane: number;\n from?: string;\n to?: string;\n fromPos?: number | null;\n toPos?: number | null;\n capacityFeet?: number | null;\n industryRef?: number | null;\n /** The module_tracks row this track is (single source of truth); null = new. */\n moduleTrackId?: number | null;\n /** Owner's track name, mirrored to module_tracks.track_name. */\n trackName?: string;\n /** Inside the balloon of a loop module (#165): pos still measures from\n * endplate A (past the throat = in the loop), lane is the ladder/arc index —\n * one record drives both the unrolled fan and a geometric render. */\n inLoop?: boolean;\n}\nexport interface SchematicTurnout {\n id: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind?: TurnoutKind;\n name?: string | null;\n address?: string | null;\n}\nexport interface SchematicSignal {\n id: string;\n pos: number;\n /** Track the signal governs; absent = the primary main (lane 0). */\n track?: string;\n facing?: SignalFacing;\n kind?: \"mast\" | \"dwarf\";\n name?: string | null;\n aspects?: string[];\n /** Which side of the track the signal sits on (#122). */\n side?: SignalSide;\n /** Turnout this control point governs; absent = standalone block signal. */\n turnout?: string;\n}\nexport interface SchematicBlock {\n id: string;\n name: string;\n tracks?: string[];\n from: number;\n to: number;\n}\n/** A grade crossing / diamond (#170): two tracks cross with no route choice —\n * a conflict node, not a turnout. A connected diamond adds normal turnouts\n * alongside. Drawn as an X between the two tracks' lanes. */\nexport interface SchematicCrossing {\n id: string;\n pos: number;\n /** The two crossing tracks, by id. */\n tracks: [string, string];\n name?: string | null;\n}\n/**\n * A control point is an interlocking: a named group of one or more signals and\n * zero or more turnouts. A passing siding has two (West/East); a lone block\n * signal is a control point with one signal and no turnouts.\n */\nexport interface SchematicControlPoint {\n id: string;\n name?: string | null;\n turnouts?: string[];\n /** Crossings this interlocking protects (#170). */\n crossings?: string[];\n signals?: SchematicSignal[];\n}\nexport interface ModuleSchematicDoc {\n version: number;\n module?: string;\n lengthInches?: number;\n /** Single-endplate turnback (balloon loop): the main enters at A, runs the\n * lead, and turns back — renderers draw a terminal bulb at the far end\n * instead of a second endplate. Also implied by a single-entry endplates\n * array (a category:\"loop\" module like Seaford). */\n loop?: boolean;\n /** Where the balloon returns (#165): \"same\" (default) turns back onto the\n * same main; \"main2\" is a directional return on a double-track main —\n * out on Main 1, back on Main 2 — drawn as a U joining the two lanes\n * (the transit terminal-loop idiom). */\n loopReturn?: \"same\" | \"main2\";\n /** Optional rendering override: \"bulb\" (abstract terminal), \"fan\" (interior\n * tracks unrolled as a ladder past the throat — the default when inLoop\n * tracks exist), \"geometric\" (drawn balloon, AL&E-style). */\n loopRender?: \"bulb\" | \"fan\" | \"geometric\";\n endplates: SchematicEndplate[];\n tracks: SchematicTrack[];\n turnouts?: SchematicTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings?: SchematicCrossing[];\n controlPoints?: SchematicControlPoint[];\n /** @deprecated pre-grouping flat signals; read for back-compat. */\n signals?: SchematicSignal[];\n}\n\n/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */\nexport function isLoopDoc(doc: ModuleSchematicDoc): boolean {\n return doc.loop === true || doc.endplates.length === 1;\n}\n\nexport const MAIN_TRACK_ID = \"main\";\n/** The second main on double-track modules — a real track entity so turnouts\n * and signals can attach to it (a spur off Main 2 must diverge from lane 1,\n * not draw a crossover from Main 1). Emitted by stateToDoc when either\n * endplate is double; legacy docs without it still parse. */\nexport const MAIN2_TRACK_ID = \"main2\";\n\n// North American N scale (1:160): 396 real inches → 5280 scale feet = one mile.\nexport const N_SCALE_RATIO = 160;\n/** Real inches on the module → scale feet of prototype track represented. */\nexport function inchesToScaleFeet(inches: number, ratio = N_SCALE_RATIO): number {\n return (inches * ratio) / 12;\n}\n/** Scale feet of prototype track → real inches on the module. */\nexport function scaleFeetToInches(feet: number, ratio = N_SCALE_RATIO): number {\n return (feet * 12) / ratio;\n}\n\n/** Parse a jsonb value into a schematic doc, or null if it isn't one. */\nexport function asModuleSchematic(x: unknown): ModuleSchematicDoc | null {\n if (!x || typeof x !== \"object\") return null;\n const d = x as Record<string, unknown>;\n if (typeof d.version !== \"number\") return null;\n if (!Array.isArray(d.endplates) || !Array.isArray(d.tracks)) return null;\n return d as unknown as ModuleSchematicDoc;\n}\n\n// ---- Editor state (a flatter shape an authoring form binds to) -------------\n\nexport interface EditorTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromPos: number;\n toPos: number;\n /** module_tracks row id (single source of truth), or null for a new track. */\n moduleTrackId: number | null;\n /** Owner's track name → module_tracks.track_name. */\n trackName: string;\n /** Inside the balloon of a loop module (#165). */\n inLoop?: boolean;\n}\n\n/** A module_tracks row as loaded for the editor. */\nexport interface ModuleTrackRow {\n id: number;\n track_name: string | null;\n capacity_scale_feet: number | null;\n}\nexport interface EditorTurnout {\n id: string;\n name: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind: TurnoutKind;\n}\nexport interface EditorCpSignal {\n id: string;\n pos: number;\n track: string;\n facing: SignalFacing;\n side: SignalSide;\n}\nexport interface EditorControlPoint {\n id: string;\n name: string;\n turnouts: string[]; // turnout ids grouped under this control point\n /** Crossing ids this interlocking protects (#170). */\n crossings?: string[];\n signals: EditorCpSignal[];\n}\nexport interface EditorCrossing {\n id: string;\n name: string;\n pos: number;\n trackA: string;\n trackB: string;\n}\n/** A 3rd+ endplate — a branch/junction connection off the module (#170).\n * A module may have several (e.g. a set carrying a second railroad through:\n * MoPac enters at one branch endplate and leaves at another). */\nexport interface EditorBranch {\n label: string;\n pos: number;\n side: \"up\" | \"down\";\n config: TrackConfig;\n}\n/** Endplate B on a loop module: a standard endplate makes the balloon an\n * INTERCHANGE (a second route connects at the loop, e.g. Seaford); \"none\"\n * makes it a pure turnback. Non-loop modules always have a real B. */\nexport type EndplateBConfig = TrackConfig | \"none\";\n\nexport interface EditorState {\n lengthInches: number;\n /** Balloon loop: the main runs the lead and turns back; positions past the\n * throat are inside the balloon. Endplate B stays independently available —\n * present = interchange loop, \"none\" = pure turnback. */\n loop: boolean;\n /** Where the balloon returns: same main, or Main 2 (directional return on a\n * double-track main — drawn as a U joining the two lanes). */\n loopReturn: \"same\" | \"main2\";\n configA: TrackConfig;\n configB: EndplateBConfig;\n extraTracks: EditorTrack[]; // sidings/spurs/…; the main track is implicit\n turnouts: EditorTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings: EditorCrossing[];\n /** Branch endplates C, D, … — junction connections (#170); empty = through\n * module. Emitted in order as endplates \"C\", \"D\", \"E\"… */\n branches: EditorBranch[];\n /** Manual endplate pose overrides by endplate id (#175 phase 1b). */\n poseOverrides: Record<string, { x: number; y: number; heading: number }>;\n controlPoints: EditorControlPoint[];\n}\n\n/** Build the empty editor state for a module of the given length. */\nexport function emptyEditorState(lengthInches: number): EditorState {\n return {\n lengthInches: lengthInches > 0 ? lengthInches : 24,\n loop: false,\n loopReturn: \"same\",\n configA: \"single\",\n configB: \"single\",\n extraTracks: [],\n turnouts: [],\n crossings: [],\n branches: [],\n poseOverrides: {},\n controlPoints: [],\n };\n}\n\n/** Main 2's doc track for a non-loop double module. Full length when both\n * endplates are double; on a transition module (one single, one double) it\n * runs between the mainline transition turnout (the one diverging to main2)\n * and the double end. */\nfunction main2Track(state: EditorState): SchematicTrack {\n const bothDouble = state.configA === \"double\" && state.configB === \"double\";\n const sw = state.turnouts.find((t) => t.divergeTrack === MAIN2_TRACK_ID);\n if (bothDouble || !sw) {\n return { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, from: \"A\", to: \"B\" };\n }\n return state.configA === \"double\"\n ? // Double at A: Main 2 runs from A and ends at the turnout.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: 0, toPos: sw.pos }\n : // Double at B: Main 2 begins at the turnout and runs to B.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: sw.pos, toPos: state.lengthInches };\n}\n\n/**\n * Build the single↔double transition as one unit (like buildPassingSiding):\n * the mainline turnout where Main 2 begins/ends, grouped in a control point —\n * an \"End of Double Track\" is a classic CTC interlocking — with signals both\n * directions. Returns items to merge into the editor state.\n */\nexport function buildTransition(state: EditorState): {\n turnout: EditorTurnout;\n controlPoint: EditorControlPoint;\n} | null {\n const aDouble = state.configA === \"double\";\n const bDouble = state.configB === \"double\";\n if (state.loop || aDouble === bDouble) return null; // not a transition module\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.25));\n // The turnout sits toward the single end so the double track carries most\n // of the module; owner adjusts the position afterwards.\n const pos = aDouble ? len - inset : inset;\n\n const swId = nextId(\"sw\", state.turnouts.map((t) => t.id));\n const turnout: EditorTurnout = {\n id: swId,\n name: \"End of Double Track\",\n pos,\n onTrack: MAIN_TRACK_ID,\n divergeTrack: MAIN2_TRACK_ID,\n // Main 2 sits above (lane 1); the throw that runs toward the double end is\n // left when that end is east (single at A), right when it's west (#bug1).\n kind: aDouble ? \"right\" : \"left\",\n };\n\n const cpId = nextId(\"cp\", state.controlPoints.map((c) => c.id));\n const sig = (facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoint: EditorControlPoint = {\n id: cpId,\n name: \"End of Double Track\",\n turnouts: [swId],\n signals: [sig(\"AtoB\"), sig(\"BtoA\")],\n };\n return { turnout, controlPoint };\n}\n\n/** Attach manual pose overrides (#175 phase 1b) to endplates by id. */\nfunction withPoses(\n endplates: SchematicEndplate[],\n overrides: Record<string, { x: number; y: number; heading: number }>,\n): SchematicEndplate[] {\n return endplates.map((e) =>\n overrides[e.id] ? { ...e, pose: overrides[e.id] } : e,\n );\n}\n\n/** Assemble a spec-conformant doc from the editor state. */\nexport function stateToDoc(\n state: EditorState,\n recordNumber: string,\n): ModuleSchematicDoc {\n return {\n version: 1,\n module: recordNumber,\n lengthInches: state.lengthInches,\n ...(state.loop ? { loop: true } : {}),\n ...(state.loop && state.loopReturn === \"main2\" ? { loopReturn: \"main2\" as const } : {}),\n endplates: withPoses(\n [\n ...(state.loop\n ? // Balloon loop: A is the entry. A standard endplate B on the balloon\n // makes it an INTERCHANGE (second route connects at the loop, e.g.\n // Seaford); configB \"none\" makes it a pure turnback.\n [\n { id: \"A\", label: \"Entry\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n ...(state.configB !== \"none\"\n ? [{ id: \"B\", label: \"Interchange\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }]\n : []),\n ]\n : [\n { id: \"A\", label: \"West\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n // Non-loop modules always have a real B (\"none\" never applies).\n {\n id: \"B\",\n label: \"East\",\n tracks: [\n {\n trackId: MAIN_TRACK_ID,\n lane: 0,\n config: state.configB === \"none\" ? \"single\" : state.configB,\n },\n ],\n },\n ]),\n // Branch endplates C, D, … — junction connections at pos, off one side\n // (#170). A set can carry several (e.g. a second railroad through).\n ...state.branches.map((b, i) => ({\n id: String.fromCharCode(67 + i), // C, D, E…\n label: b.label || `Branch ${i + 1}`,\n tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],\n at: { pos: b.pos, side: b.side },\n })),\n ],\n state.poseOverrides,\n ),\n tracks: [\n state.loop\n ? // The main runs the lead from A and turns back at the balloon.\n { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, fromPos: 0, toPos: state.lengthInches }\n : { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, from: \"A\", to: \"B\" },\n // Double track: Main 2 is a real entity so turnouts/signals can attach.\n // On a loop it exists only for a Main 2 directional return (the U joins\n // the two lanes at the balloon); a same-main loop's parallel lead legs\n // are ONE main. On a TRANSITION module (one endplate single, the other\n // double) Main 2 only runs from the mainline turnout to the double end —\n // the turnout that diverges to main2 is the single source of truth for\n // where the transition sits (fd#175 / FMN-0038).\n ...(!state.loop && (state.configA === \"double\" || state.configB === \"double\")\n ? [main2Track(state)]\n : []),\n ...(state.loop && state.loopReturn === \"main2\"\n ? [{ id: MAIN2_TRACK_ID, role: \"main\" as const, lane: 1, fromPos: 0, toPos: state.lengthInches }]\n : []),\n ...state.extraTracks.map((t) => ({\n id: t.id,\n role: t.role,\n lane: t.lane,\n fromPos: t.fromPos,\n toPos: t.toPos,\n moduleTrackId: t.moduleTrackId,\n trackName: t.trackName || undefined,\n capacityFeet: Math.round(inchesToScaleFeet(Math.abs(t.toPos - t.fromPos))),\n ...(state.loop && t.inLoop ? { inLoop: true } : {}),\n })),\n ],\n turnouts: state.turnouts.map((t) => ({\n id: t.id,\n pos: t.pos,\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: t.kind,\n name: t.name || undefined,\n })),\n ...(state.crossings.length > 0\n ? {\n crossings: state.crossings.map((x) => ({\n id: x.id,\n pos: x.pos,\n tracks: [x.trackA, x.trackB] as [string, string],\n name: x.name || undefined,\n })),\n }\n : {}),\n controlPoints: state.controlPoints.map((c) => ({\n id: c.id,\n name: c.name,\n turnouts: c.turnouts,\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: c.signals.map((s) => ({\n id: s.id,\n pos: s.pos,\n track: s.track,\n facing: s.facing,\n kind: \"mast\" as const,\n side: s.side,\n })),\n })),\n };\n}\n\n/**\n * Derive editor state from the doc and the module's Track section rows. Tracks\n * are the single source of truth for name/capacity (module_tracks), while the\n * schematic doc adds geometry (lane, positions). We merge: doc tracks first\n * (they carry geometry + their moduleTrackId link), then any module_tracks not\n * yet positioned in the schematic.\n */\nexport function docToState(\n doc: unknown,\n fallbackLength: number,\n moduleTracks: ModuleTrackRow[] = [],\n): EditorState {\n const base = emptyEditorState(fallbackLength);\n const d =\n doc && typeof doc === \"object\" ? (doc as ModuleSchematicDoc) : null;\n const hasDoc = !!d && typeof d.lengthInches === \"number\" && Array.isArray(d.tracks);\n // The module's length is authoritative (the mainline is the module). If the\n // saved doc used a different length, rescale its feature positions to fit so\n // the mainline always reads as the module's true length.\n const len = fallbackLength > 0 ? fallbackLength : hasDoc ? d!.lengthInches! : 24;\n const docLen = hasDoc && d!.lengthInches! > 0 ? d!.lengthInches! : len;\n const scale = docLen > 0 ? len / docLen : 1;\n const sc = (p: number) => Math.round(p * scale);\n\n const nameOf = (id: number | null | undefined): string => {\n const mt = id != null ? moduleTracks.find((m) => m.id === id) : undefined;\n return mt?.track_name ?? \"\";\n };\n\n const extraTracks: EditorTrack[] = [];\n const usedMt = new Set<number>();\n if (hasDoc) {\n for (const t of d!.tracks) {\n if (t.role === \"main\") continue;\n const moduleTrackId = t.moduleTrackId ?? null;\n if (moduleTrackId != null) usedMt.add(moduleTrackId);\n extraTracks.push({\n id: t.id,\n role: (t.role as TrackRole) ?? \"siding\",\n lane: t.lane ?? 1,\n fromPos: sc(t.fromPos ?? 0),\n toPos: t.toPos != null ? sc(t.toPos) : len,\n moduleTrackId,\n trackName: t.trackName ?? nameOf(moduleTrackId),\n ...(t.inLoop ? { inLoop: true } : {}),\n });\n }\n }\n // Link pre-migration doc tracks (no moduleTrackId yet) to unused module_tracks\n // by order — keeping the doc track's id so turnout/signal references stay\n // valid. Only after that do leftover module_tracks become new tracks.\n const unused = moduleTracks.filter((mt) => !usedMt.has(mt.id));\n let ui = 0;\n for (const et of extraTracks) {\n if (et.moduleTrackId == null && ui < unused.length) {\n const mt = unused[ui++];\n et.moduleTrackId = mt.id;\n if (!et.trackName) et.trackName = mt.track_name ?? \"\";\n usedMt.add(mt.id);\n }\n }\n let lane = Math.max(0, ...extraTracks.map((t) => t.lane));\n for (const mt of moduleTracks) {\n if (usedMt.has(mt.id)) continue;\n lane += 1;\n extraTracks.push({\n id: `mt${mt.id}`,\n role: \"siding\",\n lane,\n fromPos: Math.round(len * 0.2),\n toPos: Math.round(len * 0.8),\n moduleTrackId: mt.id,\n trackName: mt.track_name ?? \"\",\n });\n }\n\n if (!hasDoc) return { ...base, lengthInches: len, extraTracks };\n\n const configOf = (id: string): TrackConfig => {\n const ep = (d!.endplates ?? []).find((e) => e.id === id);\n return ep?.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\";\n };\n const loop = isLoopDoc(d!);\n const hasB = (d!.endplates ?? []).some((e) => e.id === \"B\");\n // Branch endplates C, D, … (junction connections, #170).\n const branchEps = (d!.endplates ?? []).filter(\n (e) => e.id !== \"A\" && e.id !== \"B\" && e.at,\n );\n const poseOverrides = poseOverridesFromDoc(d!);\n return {\n lengthInches: len,\n loop,\n loopReturn: loop && d!.loopReturn === \"main2\" ? \"main2\" : \"same\",\n configA: configOf(\"A\"),\n // On a loop, a missing B means pure turnback; present = interchange loop.\n configB: loop && !hasB ? \"none\" : configOf(\"B\"),\n branches: branchEps.map((ep) => ({\n label: ep.label ?? \"Branch\",\n pos: sc(ep.at!.pos),\n side: ep.at!.side === \"down\" ? \"down\" : \"up\",\n config: ep.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\",\n })),\n poseOverrides,\n crossings: (d!.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n pos: sc(x.pos),\n trackA: x.tracks?.[0] ?? MAIN_TRACK_ID,\n trackB: x.tracks?.[1] ?? MAIN_TRACK_ID,\n })),\n extraTracks,\n turnouts: (d!.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n pos: sc(t.pos),\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: (t.kind as TurnoutKind) ?? \"right\",\n })),\n controlPoints: readControlPoints(d!, sc),\n };\n}\n\n/** Control points from a doc, migrating pre-grouping flat signals into groups. */\nfunction readControlPoints(\n d: ModuleSchematicDoc,\n sc: (p: number) => number = (p) => p,\n): EditorControlPoint[] {\n if (Array.isArray(d.controlPoints)) {\n return d.controlPoints.map((c) => ({\n id: c.id,\n name: c.name ?? \"\",\n turnouts: c.turnouts ?? [],\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: (c.signals ?? []).map((s) => ({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n })),\n }));\n }\n // Back-compat: group old flat signals by their turnout (or standalone).\n const groups = new Map<string, EditorControlPoint>();\n let n = 0;\n for (const s of d.signals ?? []) {\n const key = s.turnout || `blk-${s.id}`;\n let cp = groups.get(key);\n if (!cp) {\n cp = { id: `cp${++n}`, name: s.name ?? \"\", turnouts: s.turnout ? [s.turnout] : [], signals: [] };\n groups.set(key, cp);\n }\n cp.signals.push({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n });\n }\n return [...groups.values()];\n}\n\n/** Find an unused `${prefix}${n}` id given the ones already present. */\nexport function nextId(prefix: string, existing: string[]): string {\n let n = 1;\n while (existing.includes(`${prefix}${n}`)) n += 1;\n return `${prefix}${n}`;\n}\n\n/**\n * Build a passing siding as one unit: the siding track, a switch at each end,\n * and control-point signals for both directions at each end (prototype Station\n * Entering Signal). Returns the new items to merge into the editor state.\n */\nexport function buildPassingSiding(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n controlPoints: EditorControlPoint[];\n} {\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.08));\n const fromPos = inset;\n const toPos = Math.max(fromPos + 1, len - inset);\n // First free lane above the main(s): lane 1 is Main 2 on double modules.\n const baseLane =\n state.configA === \"double\" || state.configB === \"double\" ? 2 : 1;\n const lane = Math.max(baseLane, ...state.extraTracks.map((t) => t.lane + 1));\n\n const trackIds = [MAIN_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const sidId = nextId(\"sid\", trackIds);\n const track: EditorTrack = {\n id: sidId,\n role: \"siding\",\n lane,\n fromPos,\n toPos,\n moduleTrackId: null,\n trackName: \"Passing siding\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const swW = nextId(\"sw\", swIds);\n const swE = nextId(\"sw\", [...swIds, swW]);\n // A siding above the main throws LEFT at its west turnout (body runs east) and\n // RIGHT at its east turnout (body runs west) — both resolve to the same side,\n // so `kind` and the drawn lane agree (divergeSideForHand / #bug1).\n const turnouts: EditorTurnout[] = [\n { id: swW, name: \"West Siding\", pos: fromPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"left\" },\n { id: swE, name: \"East Siding\", pos: toPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"right\" },\n ];\n\n // One control point at each end, each grouping its switch and both-direction\n // signals on the main (prototype Station Entering Signal).\n const cpIds = state.controlPoints.map((c) => c.id);\n const cpW = nextId(\"cp\", cpIds);\n const cpE = nextId(\"cp\", [...cpIds, cpW]);\n const sig = (cpId: string, pos: number, facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n // opposite directions on opposite sides so they never overlap\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoints: EditorControlPoint[] = [\n { id: cpW, name: \"West Siding\", turnouts: [swW], signals: [sig(cpW, fromPos, \"AtoB\"), sig(cpW, fromPos, \"BtoA\")] },\n { id: cpE, name: \"East Siding\", turnouts: [swE], signals: [sig(cpE, toPos, \"AtoB\"), sig(cpE, toPos, \"BtoA\")] },\n ];\n\n return { track, turnouts, controlPoints };\n}\n\n/**\n * Build a crossover as one unit: a short connector track between Main 1 and\n * Main 2, with a turnout on each main. The two turnouts sit on different lanes,\n * so the feature resolver draws it as a diagonal (not a lane-paralleling siding)\n * — this is what connects the two dots (#bug2). Needs a double-track module.\n */\nexport function buildCrossover(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n} | null {\n const hasSecond = state.configA === \"double\" || state.configB === \"double\";\n if (state.loop || !hasSecond) return null;\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const mid = Math.round(len / 2);\n const gap = Math.max(3, Math.round(len * 0.04));\n const w = Math.round(mid - gap / 2);\n const e = Math.round(mid + gap / 2);\n\n const trackIds = [MAIN_TRACK_ID, MAIN2_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const xoId = nextId(\"xo\", trackIds);\n const track: EditorTrack = {\n id: xoId,\n role: \"crossover\",\n lane: 1,\n fromPos: w,\n toPos: e,\n moduleTrackId: null,\n trackName: \"Crossover\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const sw1 = nextId(\"sw\", swIds);\n const sw2 = nextId(\"sw\", [...swIds, sw1]);\n const turnouts: EditorTurnout[] = [\n { id: sw1, name: \"Crossover\", pos: w, onTrack: MAIN_TRACK_ID, divergeTrack: xoId, kind: \"left\" },\n { id: sw2, name: \"Crossover\", pos: e, onTrack: MAIN2_TRACK_ID, divergeTrack: xoId, kind: \"right\" },\n ];\n\n return { track, turnouts };\n}\n\n// ---- Pure feature resolver (both renderers draw these) --------------------\n\nexport interface DrawTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromFrac: number;\n toFrac: number;\n capacityFeet: number | null;\n /** Lane of the main this track diverges from (via its turnout) — the origin\n * of the diverge diagonal. A spur off Main 2 starts at lane 1, not lane 0;\n * without this, renderers draw what looks like a crossover. */\n divergesFromLane: number;\n /** The end that meets the main at its turnout (throat) and the far stub end,\n * as fractions of length. Direction-preserving: unlike fromFrac/toFrac (always\n * sorted West→East), these keep WHICH end joins, so an east-facing spur draws\n * its throat on the east. A siding meets the main at both ends — there\n * throat=fromFrac, stub=toFrac (the fields are only meaningful for spurs). */\n throatFrac: number;\n stubFrac: number;\n /** Inside the balloon of a loop module (#165). */\n inLoop: boolean;\n}\nexport interface DrawTurnout {\n id: string;\n name: string;\n posFrac: number;\n onLane: number;\n divergeLane: number;\n}\nexport interface DrawSignal {\n id: string;\n name: string;\n posFrac: number;\n lane: number;\n facing: SignalFacing;\n side: SignalSide;\n /** Owning control point's id, when the signal came from a CP group — lets a\n * renderer join the drawn signal back to interlocking-level state (aspects). */\n cp?: string;\n /** 0-based rank among signals that would otherwise land on the exact same\n * spot (same lane + side + position). Renderers offset each further from the\n * track by `stack` so a control point's signals never overlap. */\n stack: number;\n}\n/** A grade crossing / diamond — draw an X spanning the two lanes (#170). */\nexport interface DrawCrossing {\n id: string;\n name: string;\n posFrac: number;\n laneA: number;\n laneB: number;\n}\n/** A crossover — a connector track joining two parallel mains through a turnout\n * on each. Draw a straight diagonal between the two turnout points (unlike a\n * siding, which parallels a lane between its turnouts). Detected structurally:\n * a track whose turnouts sit on two different lanes. */\nexport interface DrawCrossover {\n id: string;\n name: string;\n fromPosFrac: number;\n fromLane: number;\n toPosFrac: number;\n toLane: number;\n}\n/** A branch endplate — draw a connector stub + arrow + label off the given\n * side (the CATS/US&S off-band idiom, #170). */\nexport interface BranchConnector {\n id: string;\n label: string;\n posFrac: number;\n side: \"up\" | \"down\";\n}\nexport interface ModuleFeatures {\n /** Whether either endplate declares a double-track main. */\n doubleMain: boolean;\n /** Balloon loop — draw a terminal bulb at the far end. */\n loop: boolean;\n /** Loop with a standard endplate B on the balloon: an interchange — a\n * second route connects at the loop (draw an endplate branch off the bulb). */\n loopInterchange: boolean;\n /** Where the balloon returns: \"main2\" = directional return on a double-track\n * main — draw a U joining lanes 0 and 1 instead of the bulb (#165). */\n loopReturn: \"same\" | \"main2\";\n /** Rendering override from the doc; renderers may ignore modes they don't\n * implement yet (\"geometric\"). */\n loopRender: \"bulb\" | \"fan\" | \"geometric\" | null;\n /** Non-main tracks (sidings/spurs/yard/crossover). */\n extraTracks: DrawTrack[];\n turnouts: DrawTurnout[];\n signals: DrawSignal[];\n /** Grade crossings / diamonds (#170). */\n crossings: DrawCrossing[];\n /** Crossovers — connector tracks between two mains (drawn as a diagonal). */\n crossovers: DrawCrossover[];\n /** Branch endplates — junction connectors off the module (#170). */\n branchConnectors: BranchConnector[];\n /** Main 2's extent when it doesn't run the full module — a single↔double\n * transition (Main 2 starts/ends at the mainline turnout). Null = full\n * length (or no Main 2). Renderers draw the partial line + its diverge. */\n main2Extent: { fromFrac: number; toFrac: number } | null;\n /** Lane extents across every feature (mains included; negative = outside\n * Main 1). Renderers size their vertical space from these. */\n laneMin: number;\n laneMax: number;\n}\n\n/**\n * Which side of the main a diverging track draws on, given the turnout's HAND\n * and the direction the track body runs along the main from the turnout\n * (`stubDir`: +1 = the body extends east / toward B, −1 = west / toward A).\n * Returns +1 (above the main) or −1 (below); 0 for a wye or unset hand (keep the\n * authored side). A left-hand turnout throws its route to the same side its body\n * runs (facing the frog, left is the inside of a body running that way); a\n * right-hand throws to the opposite side. `kind` is the source of truth for the\n * drawn side (#bug1) — the stored lane's sign is reconciled to match it.\n */\nexport function divergeSideForHand(\n kind: TurnoutKind | undefined,\n stubDir: number,\n): -1 | 0 | 1 {\n if (kind !== \"left\" && kind !== \"right\") return 0; // wye / unset → no change\n const s = stubDir >= 0 ? 1 : -1;\n return kind === \"left\" ? (s as 1 | -1) : ((-s) as 1 | -1);\n}\n\n/**\n * Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a\n * fraction of the module length; endplate A = 0, B = length; turnouts sit at\n * their pos. Tracks may carry explicit fromPos/toPos (overriding node lookup).\n * To-scale: a feature renders at its true position, clamped only to the\n * module's own extent — so signals near an end read at their real spot, not\n * bunched at an inset (#122).\n */\nexport function moduleFeatures(doc: ModuleSchematicDoc): ModuleFeatures {\n const len =\n doc.lengthInches && doc.lengthInches > 0\n ? doc.lengthInches\n : Math.max(\n 1,\n ...doc.tracks.map((t) => Math.max(t.fromPos ?? 0, t.toPos ?? 0)),\n ...(doc.turnouts ?? []).map((t) => t.pos),\n );\n const clampFrac = (p: number) => Math.min(1, Math.max(0, p / len));\n\n const trackLane = new Map<string, number>();\n for (const t of doc.tracks) trackLane.set(t.id, t.lane);\n\n // Endplate positions: first endplate = West (0), the rest = East (len).\n const endplatePos = new Map<string, number>();\n doc.endplates.forEach((e, i) => endplatePos.set(e.id, i === 0 ? 0 : len));\n const turnoutPos = new Map<string, number>();\n for (const t of doc.turnouts ?? []) turnoutPos.set(t.id, t.pos);\n const posOf = (nodeId?: string): number | null => {\n if (nodeId == null) return null;\n if (endplatePos.has(nodeId)) return endplatePos.get(nodeId)!;\n if (turnoutPos.has(nodeId)) return turnoutPos.get(nodeId)!;\n return null;\n };\n\n const doubleMain = doc.endplates.some((e) =>\n e.tracks?.some((t) => t.config === \"double\"),\n );\n\n // The lane a track diverges from = the main its turnout sits on.\n const divergeOrigin = (trackId: string): number => {\n const sw = (doc.turnouts ?? []).find((t) => t.divergeTrack === trackId);\n return sw ? (trackLane.get(sw.onTrack) ?? 0) : 0;\n };\n\n // Turnouts grouped by the track they diverge onto (a track's throat/hand).\n const turnoutsByTrack = new Map<string, SchematicTurnout[]>();\n for (const sw of doc.turnouts ?? []) {\n const arr = turnoutsByTrack.get(sw.divergeTrack) ?? [];\n arr.push(sw);\n turnoutsByTrack.set(sw.divergeTrack, arr);\n }\n // A crossover is a connector whose turnouts sit on two different lanes (one on\n // each main) — drawn as a diagonal between them, never as a lane-paralleling\n // siding. A passing siding's two turnouts share one lane, so it isn't one.\n const isCrossover = (trackId: string): boolean => {\n const sws = turnoutsByTrack.get(trackId) ?? [];\n if (sws.length < 2) return false;\n return new Set(sws.map((s) => trackLane.get(s.onTrack) ?? 0)).size >= 2;\n };\n // The in-inches extent of a track (explicit fromPos/toPos or node lookup).\n const extentOf = (t: SchematicTrack): [number, number] | null => {\n const from = t.fromPos ?? posOf(t.from);\n const to = t.toPos ?? posOf(t.to);\n return from == null || to == null ? null : [from, to];\n };\n\n // Reconcile each diverging track's drawn SIDE from its turnout's hand — `kind`\n // is the source of truth (#bug1) — resolved TOPOLOGICALLY. The hand only picks\n // a side for a turnout sitting on the main CENTERLINE (lane 0); a track\n // diverging off any other track (Main 2, or a ladder rung) stays on its\n // PARENT's side and stacks outward — a ladder turnout's hand is relative to\n // its parent, not the main, so it must never flip a rung across the main. The\n // stored lane's magnitude (stacking slot) is always kept.\n const trackById = new Map(doc.tracks.map((t) => [t.id, t]));\n const resolvedLanes = new Map<string, number>();\n const resolving = new Set<string>();\n const resolveLane = (id: string): number => {\n const trk = trackById.get(id);\n if (!trk) return 0;\n if (trk.role === \"main\") return trk.lane; // mains are fixed (0, 1, …)\n if (resolvedLanes.has(id)) return resolvedLanes.get(id)!;\n if (resolving.has(id)) return trk.lane; // cycle guard\n resolving.add(id);\n let lane = trk.lane;\n const sw = turnoutsByTrack.get(id)?.[0];\n const ext = extentOf(trk);\n if (sw && (sw.kind === \"left\" || sw.kind === \"right\") && !isCrossover(id) && ext) {\n const parentLane = resolveLane(sw.onTrack);\n let sign: number;\n if (parentLane === 0) {\n // On the main centerline — the hand chooses above/below.\n const [from, to] = ext;\n const far = Math.abs(to - sw.pos) >= Math.abs(from - sw.pos) ? to : from;\n const s = divergeSideForHand(sw.kind, far - sw.pos);\n sign = s !== 0 ? s : Math.sign(trk.lane) || 1;\n } else {\n // Off Main 2 / a ladder rung — follow the parent's side.\n sign = Math.sign(parentLane) || 1;\n }\n lane = sign * Math.abs(trk.lane);\n }\n resolving.delete(id);\n resolvedLanes.set(id, lane);\n return lane;\n };\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue;\n trackLane.set(t.id, resolveLane(t.id));\n }\n\n const extraTracks: DrawTrack[] = [];\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue; // the spine draws mains\n if (isCrossover(t.id)) continue; // crossovers draw as diagonals, below\n const ext = extentOf(t);\n if (!ext) continue; // can't place it\n const [from, to] = ext;\n // Throat = the end nearest this track's turnout (the join to the main);\n // with no turnout, keep entry order (from = throat) so the author's chosen\n // joining end is honoured. This is what makes an east-facing spur draw its\n // throat on the east instead of always west (#bug3).\n const sw = turnoutsByTrack.get(t.id)?.[0];\n const throatAtTo = sw != null && Math.abs(to - sw.pos) < Math.abs(from - sw.pos);\n const throat = throatAtTo ? to : from;\n const stub = throatAtTo ? from : to;\n extraTracks.push({\n id: t.id,\n role: t.role,\n lane: trackLane.get(t.id) ?? t.lane,\n fromFrac: clampFrac(Math.min(from, to)),\n toFrac: clampFrac(Math.max(from, to)),\n throatFrac: clampFrac(throat),\n stubFrac: clampFrac(stub),\n capacityFeet: t.capacityFeet ?? null,\n divergesFromLane: divergeOrigin(t.id),\n inLoop: t.inLoop === true,\n });\n }\n\n // Crossovers — a diagonal between two parallel mains. Two shapes are drawn:\n const crossovers: DrawCrossover[] = [];\n // (1) a dedicated connector track (buildCrossover) with a turnout on each main.\n for (const t of doc.tracks) {\n if (!isCrossover(t.id)) continue;\n const [s1, s2] = turnoutsByTrack.get(t.id)!;\n crossovers.push({\n id: t.id,\n name: t.trackName ?? \"\",\n fromPosFrac: clampFrac(s1.pos),\n fromLane: trackLane.get(s1.onTrack) ?? 0,\n toPosFrac: clampFrac(s2.pos),\n toLane: trackLane.get(s2.onTrack) ?? 1,\n });\n }\n // (2) modelled as two turnouts each diverging onto the OTHER main (no connector\n // track). Pair a leg with its return leg and connect the two turnout points.\n const isMainId = (id?: string) => !!id && trackById.get(id)?.role === \"main\";\n const m2m = (doc.turnouts ?? []).filter(\n (sw) =>\n isMainId(sw.onTrack) &&\n isMainId(sw.divergeTrack) &&\n (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1),\n );\n const usedLegs = new Set<string>();\n for (const t1 of m2m) {\n if (usedLegs.has(t1.id)) continue;\n usedLegs.add(t1.id);\n const t2 = m2m.find(\n (x) =>\n !usedLegs.has(x.id) &&\n x.onTrack === t1.divergeTrack &&\n x.divergeTrack === t1.onTrack,\n );\n if (t2) usedLegs.add(t2.id);\n const end = t2 ?? t1; // lone leg → a straight rung across at its own pos\n crossovers.push({\n id: t2 ? `${t1.id}-${t2.id}` : t1.id,\n name: t1.name ?? t2?.name ?? \"\",\n fromPosFrac: clampFrac(t1.pos),\n fromLane: trackLane.get(t1.onTrack) ?? 0,\n toPosFrac: clampFrac(end === t1 ? t1.pos : end.pos),\n toLane: trackLane.get(t1.divergeTrack) ?? 1,\n });\n }\n\n const turnouts: DrawTurnout[] = (doc.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n posFrac: clampFrac(t.pos),\n onLane: trackLane.get(t.onTrack) ?? 0,\n divergeLane: trackLane.get(t.divergeTrack) ?? 1,\n }));\n\n const drawSignal = (\n s: SchematicSignal,\n name: string,\n cp?: string,\n ): DrawSignal => ({\n id: s.id,\n name,\n posFrac: clampFrac(s.pos),\n lane: s.track ? (trackLane.get(s.track) ?? 0) : 0,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: s.side === \"below\" ? \"below\" : \"above\",\n ...(cp ? { cp } : {}),\n stack: 0,\n });\n // Signals come from control-point groups; fall back to pre-grouping flat\n // signals for docs authored before the model changed.\n const signals: DrawSignal[] = Array.isArray(doc.controlPoints)\n ? doc.controlPoints.flatMap((c) =>\n (c.signals ?? []).map((s) => drawSignal(s, c.name ?? \"\", c.id)),\n )\n : (doc.signals ?? []).map((s) => drawSignal(s, s.name ?? \"\"));\n // De-collide: signals landing on the exact same lane+side+position get a\n // rising stack rank so a renderer can fan them out (a control point often\n // carries several signals at one interlocking).\n const stackCount = new Map<string, number>();\n for (const s of signals) {\n const key = `${s.lane}|${s.side}|${Math.round(s.posFrac * 1000)}`;\n const n = stackCount.get(key) ?? 0;\n s.stack = n;\n stackCount.set(key, n + 1);\n }\n\n const crossings: DrawCrossing[] = (doc.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n posFrac: clampFrac(x.pos),\n laneA: trackLane.get(x.tracks?.[0] ?? \"\") ?? 0,\n laneB: trackLane.get(x.tracks?.[1] ?? \"\") ?? 1,\n }));\n\n // Branch endplates (any beyond A/B with a placement) → connector arrows.\n const branchConnectors: BranchConnector[] = doc.endplates\n .filter((e) => e.id !== \"A\" && e.id !== \"B\" && e.at)\n .map((e) => ({\n id: e.id,\n label: e.label ?? e.id,\n posFrac: clampFrac(e.at!.pos),\n side: e.at!.side === \"down\" ? \"down\" : \"up\",\n }));\n\n const allLanes = [\n 0,\n doubleMain ? 1 : 0,\n ...extraTracks.map((t) => t.lane),\n ...signals.map((s) => s.lane),\n ...crossings.flatMap((x) => [x.laneA, x.laneB]),\n ...crossovers.flatMap((x) => [x.fromLane, x.toLane]),\n ];\n const loop = isLoopDoc(doc);\n // A positioned Main 2 = a transition module (partial second main).\n const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);\n const main2Positioned =\n !!main2 && (main2.fromPos != null || main2.toPos != null) && !loop;\n const main2Extent = main2Positioned\n ? {\n fromFrac: clampFrac(main2!.fromPos ?? 0),\n toFrac: clampFrac(main2!.toPos ?? len),\n }\n : null;\n return {\n doubleMain,\n loop,\n main2Extent,\n loopInterchange: loop && doc.endplates.filter((e) => !e.at).length >= 2,\n loopReturn: loop && doc.loopReturn === \"main2\" ? \"main2\" : \"same\",\n loopRender: loop ? (doc.loopRender ?? null) : null,\n extraTracks,\n turnouts,\n signals,\n crossings,\n crossovers,\n branchConnectors,\n laneMin: Math.min(...allLanes),\n laneMax: Math.max(...allLanes),\n };\n}\n\n// ---- Endplate geometry & poses (#175) --------------------------------------\n//\n// Free-moN endplates are one standard interface; a single (x, y, heading) at\n// the track-crossing point fully determines mating (any endplate ↔ any\n// compatible endplate). Poses are in MODULE-LOCAL inches: endplate A's track\n// point at the origin, its OUTWARD normal pointing west (180°), so the module\n// body runs toward +X. A layout composes modules by walking joins and stacking\n// each pose's rigid transform (rotation + optional reflection for flips).\n//\n// Poses are DERIVED from the simple fields owners already enter — length +\n// geometry type/degrees/offset for the two axial endplates, plus the schematic\n// doc's branch endplates (#170). Complex shapes (wye, loop, other) fall back to\n// hand-entered overrides. Nothing is stored unless overridden.\n\nexport type GeometryType =\n | \"straight\"\n | \"corner_45\"\n | \"corner_90\"\n | \"curve\"\n | \"offset\"\n | \"dead_end\"\n | \"wye\"\n | \"other\";\n\nexport interface EndplatePose {\n /** Endplate id — \"A\"/\"B\" axial, \"C\"/\"D\"… branch. */\n id: string;\n /** Module-local inches; endplate A's track point is the origin. */\n x: number;\n y: number;\n /** Outward normal in degrees (0 = +X east, 90 = +Y north). A neighbour mates\n * facing the opposite heading. */\n heading: number;\n trackConfig: \"single\" | \"double\";\n /** Lateral track offsets from the crossing anchor (0 = centred single). */\n trackOffsets: number[];\n /** True when the pose was hand-entered, not derived (wye/loop/other). */\n manual?: boolean;\n}\n\nexport interface ModuleGeometryInput {\n lengthInches: number;\n geometryType?: string | null;\n geometryDegrees?: number | null;\n geometryOffsetInches?: number | null;\n /** Axial endplate configs (A first, then B). Missing → single. */\n endplateConfigs?: (\"single\" | \"double\" | null | undefined)[];\n /** Branch endplates (from the schematic doc, #170), positioned along the\n * mainline axis. */\n branches?: {\n id: string;\n atPos: number;\n side: \"up\" | \"down\";\n config?: \"single\" | \"double\" | null;\n }[];\n /** Hand-entered pose overrides by endplate id — win over derivation. */\n poseOverrides?: Record<string, { x: number; y: number; heading: number }>;\n /** Half the spacing between the two tracks of a double endplate (Free-mo ≈ 1\",\n * Free-moN ≈ 9/16\"). */\n trackHalfSpacingInches?: number;\n}\n\n/** Signed turn a module applies to the through track (CCW/left positive). */\nexport function geometryTurnDegrees(\n geometryType?: string | null,\n geometryDegrees?: number | null,\n): number {\n switch (geometryType) {\n case \"corner_45\":\n return 45;\n case \"corner_90\":\n return 90;\n case \"curve\":\n return geometryDegrees ?? 0;\n default:\n return 0;\n }\n}\n\nconst DEG = Math.PI / 180;\nconst norm360 = (d: number) => ((d % 360) + 360) % 360;\n\nfunction offsetsFor(\n config: \"single\" | \"double\",\n half: number,\n): number[] {\n return config === \"double\" ? [-half, half] : [0];\n}\n\n/**\n * Derive every endplate's module-local pose. A at the origin facing west; B\n * placed by the module's geometry (straight/offset/corner/curve via a\n * constant-radius arc = arc-length `lengthInches`, turning by the geometry\n * angle); branch endplates positioned along the mainline axis facing out their\n * side. A `dead_end`/loop module has no B. Overrides replace any derived pose.\n */\nexport function deriveEndplatePoses(geo: ModuleGeometryInput): EndplatePose[] {\n const L = geo.lengthInches > 0 ? geo.lengthInches : 24;\n const half = geo.trackHalfSpacingInches ?? 1;\n const cfg = (i: number): \"single\" | \"double\" =>\n geo.endplateConfigs?.[i] === \"double\" ? \"double\" : \"single\";\n const withOverride = (p: EndplatePose): EndplatePose => {\n const o = geo.poseOverrides?.[p.id];\n return o ? { ...p, x: o.x, y: o.y, heading: norm360(o.heading), manual: true } : p;\n };\n\n const poses: EndplatePose[] = [];\n\n // Endplate A — origin, outward normal west.\n poses.push(\n withOverride({\n id: \"A\",\n x: 0,\n y: 0,\n heading: 180,\n trackConfig: cfg(0),\n trackOffsets: offsetsFor(cfg(0), half),\n }),\n );\n\n // Endplate B — unless the module is a dead end / turnback (single endplate).\n const noB = geo.geometryType === \"dead_end\";\n if (!noB) {\n const turn = geometryTurnDegrees(geo.geometryType, geo.geometryDegrees);\n let bx: number;\n let by: number;\n let bHeading: number;\n if (geo.geometryType === \"offset\") {\n // Parallel endplates, jogged sideways over the run.\n bx = L;\n by = geo.geometryOffsetInches ?? 0;\n bHeading = 0;\n } else if (turn === 0) {\n bx = L;\n by = 0;\n bHeading = 0;\n } else {\n // Constant-radius arc of arc-length L turning `turn` (CCW/left).\n const t = turn * DEG;\n const r = L / t;\n bx = r * Math.sin(t);\n by = r * (1 - Math.cos(t));\n bHeading = turn;\n }\n poses.push(\n withOverride({\n id: \"B\",\n x: bx,\n y: by,\n heading: norm360(bHeading),\n trackConfig: cfg(1),\n trackOffsets: offsetsFor(cfg(1), half),\n }),\n );\n }\n\n // Branch endplates — at their along-axis position, facing out their side.\n // Position along the (possibly curved) mainline is approximated on the A→B\n // chord; the join solver refines with overrides where a module needs it.\n for (const b of geo.branches ?? []) {\n const frac = L > 0 ? Math.min(1, Math.max(0, b.atPos / L)) : 0;\n const px = frac * L;\n const config = b.config === \"double\" ? \"double\" : \"single\";\n poses.push(\n withOverride({\n id: b.id,\n x: px,\n y: 0,\n heading: b.side === \"down\" ? 270 : 90,\n trackConfig: config,\n trackOffsets: offsetsFor(config, half),\n }),\n );\n }\n\n return poses;\n}\n\n/** Whether a module shape's poses are fully derivable, or need manual entry\n * (a helpful cue for the authoring UI). */\nexport function poseNeedsManual(geometryType?: string | null): boolean {\n return geometryType === \"wye\" || geometryType === \"other\";\n}\n\n/** Extract manual pose overrides from a schematic doc's endplates (#175 phase\n * 1b) — the map deriveEndplatePoses / a footprint solver feeds as overrides. */\nexport function poseOverridesFromDoc(\n doc: ModuleSchematicDoc,\n): Record<string, { x: number; y: number; heading: number }> {\n const out: Record<string, { x: number; y: number; heading: number }> = {};\n for (const e of doc.endplates ?? []) {\n if (\n e.id &&\n e.pose &&\n typeof e.pose.x === \"number\" &&\n typeof e.pose.y === \"number\" &&\n typeof e.pose.heading === \"number\"\n ) {\n out[e.id] = { x: e.pose.x, y: e.pose.y, heading: e.pose.heading };\n }\n }\n return out;\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AA8IO,SAAS,UAAU,GAAA,EAAkC;AAC1D,EAAA,OAAO,GAAA,CAAI,IAAA,KAAS,IAAA,IAAQ,GAAA,CAAI,UAAU,MAAA,KAAW,CAAA;AACvD;AAEO,IAAM,aAAA,GAAgB;AAKtB,IAAM,cAAA,GAAiB;AAGvB,IAAM,aAAA,GAAgB;AAEtB,SAAS,iBAAA,CAAkB,MAAA,EAAgB,KAAA,GAAQ,aAAA,EAAuB;AAC/E,EAAA,OAAQ,SAAS,KAAA,GAAS,EAAA;AAC5B;AAEO,SAAS,iBAAA,CAAkB,IAAA,EAAc,KAAA,GAAQ,aAAA,EAAuB;AAC7E,EAAA,OAAQ,OAAO,EAAA,GAAM,KAAA;AACvB;AAGO,SAAS,kBAAkB,CAAA,EAAuC;AACvE,EAAA,IAAI,CAAC,CAAA,IAAK,OAAO,CAAA,KAAM,UAAU,OAAO,IAAA;AACxC,EAAA,MAAM,CAAA,GAAI,CAAA;AACV,EAAA,IAAI,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,EAAU,OAAO,IAAA;AAC1C,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,SAAS,CAAA,IAAK,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,MAAM,CAAA,EAAG,OAAO,IAAA;AACpE,EAAA,OAAO,CAAA;AACT;AA4FO,SAAS,iBAAiB,YAAA,EAAmC;AAClE,EAAA,OAAO;AAAA,IACL,YAAA,EAAc,YAAA,GAAe,CAAA,GAAI,YAAA,GAAe,EAAA;AAAA,IAChD,IAAA,EAAM,KAAA;AAAA,IACN,UAAA,EAAY,MAAA;AAAA,IACZ,OAAA,EAAS,QAAA;AAAA,IACT,OAAA,EAAS,QAAA;AAAA,IACT,aAAa,EAAC;AAAA,IACd,UAAU,EAAC;AAAA,IACX,WAAW,EAAC;AAAA,IACZ,UAAU,EAAC;AAAA,IACX,eAAe,EAAC;AAAA,IAChB,eAAe;AAAC,GAClB;AACF;AAYO,SAAS,oBAAoB,CAAA,EAGxB;AACV,EAAA,OACG,CAAA,CAAE,OAAA,KAAY,aAAA,IAAiB,CAAA,CAAE,YAAA,KAAiB,kBAClD,CAAA,CAAE,OAAA,KAAY,cAAA,IAAkB,CAAA,CAAE,YAAA,KAAiB,aAAA;AAExD;AAEA,SAAS,WAAW,KAAA,EAAoC;AACtD,EAAA,MAAM,UAAA,GAAa,KAAA,CAAM,OAAA,KAAY,QAAA,IAAY,MAAM,OAAA,KAAY,QAAA;AACnE,EAAA,MAAM,EAAA,GAAK,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,mBAAmB,CAAA;AAIlD,EAAA,IAAI,UAAA,IAAc,CAAC,EAAA,IAAM,EAAA,CAAG,iBAAiB,cAAA,EAAgB;AAC3D,IAAA,OAAO,EAAE,EAAA,EAAI,cAAA,EAAgB,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAA,EAAG,IAAA,EAAM,GAAA,EAAK,EAAA,EAAI,GAAA,EAAI;AAAA,EACzE;AACA,EAAA,OAAO,MAAM,OAAA,KAAY,QAAA;AAAA;AAAA,IAErB,EAAE,EAAA,EAAI,cAAA,EAAgB,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,CAAA,EAAG,KAAA,EAAO,EAAA,CAAG,GAAA;AAAI;AAAA;AAAA,IAEvE,EAAE,EAAA,EAAI,cAAA,EAAgB,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,EAAA,CAAG,GAAA,EAAK,KAAA,EAAO,KAAA,CAAM,YAAA;AAAa,GAAA;AAC9F;AAQO,SAAS,gBAAgB,KAAA,EAGvB;AACP,EAAA,MAAM,OAAA,GAAU,MAAM,OAAA,KAAY,QAAA;AAClC,EAAA,MAAM,OAAA,GAAU,MAAM,OAAA,KAAY,QAAA;AAClC,EAAA,IAAI,KAAA,CAAM,IAAA,IAAQ,OAAA,KAAY,OAAA,EAAS,OAAO,IAAA;AAC9C,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,YAAA,GAAe,CAAA,GAAI,MAAM,YAAA,GAAe,EAAA;AAC1D,EAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,GAAA,GAAM,IAAI,CAAC,CAAA;AAGhD,EAAA,MAAM,GAAA,GAAM,OAAA,GAAU,GAAA,GAAM,KAAA,GAAQ,KAAA;AAEpC,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,EAAM,KAAA,CAAM,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AACzD,EAAA,MAAM,OAAA,GAAyB;AAAA,IAC7B,EAAA,EAAI,IAAA;AAAA,IACJ,IAAA,EAAM,qBAAA;AAAA,IACN,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,OAAA,EAAS,cAAA;AAAA,IACT,YAAA,EAAc,aAAA;AAAA,IACd,IAAA,EAAM,UAAU,MAAA,GAAS;AAAA,GAC3B;AAEA,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,EAAM,KAAA,CAAM,aAAA,CAAc,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AAC9D,EAAA,MAAM,GAAA,GAAM,CAAC,MAAA,MAA0C;AAAA,IACrD,EAAA,EAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,IACrB,GAAA;AAAA,IACA,KAAA,EAAO,aAAA;AAAA,IACP,MAAA;AAAA,IACA,IAAA,EAAM,MAAA,KAAW,MAAA,GAAS,OAAA,GAAU;AAAA,GACtC,CAAA;AACA,EAAA,MAAM,YAAA,GAAmC;AAAA,IACvC,EAAA,EAAI,IAAA;AAAA,IACJ,IAAA,EAAM,qBAAA;AAAA,IACN,QAAA,EAAU,CAAC,IAAI,CAAA;AAAA,IACf,SAAS,CAAC,GAAA,CAAI,MAAM,CAAA,EAAG,GAAA,CAAI,MAAM,CAAC;AAAA,GACpC;AACA,EAAA,OAAO,EAAE,SAAS,YAAA,EAAa;AACjC;AAGA,SAAS,SAAA,CACP,WACA,SAAA,EACqB;AACrB,EAAA,OAAO,SAAA,CAAU,GAAA;AAAA,IAAI,CAAC,CAAA,KACpB,SAAA,CAAU,CAAA,CAAE,EAAE,CAAA,GAAI,EAAE,GAAG,CAAA,EAAG,IAAA,EAAM,SAAA,CAAU,CAAA,CAAE,EAAE,GAAE,GAAI;AAAA,GACtD;AACF;AAGO,SAAS,UAAA,CACd,OACA,YAAA,EACoB;AACpB,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,CAAA;AAAA,IACT,MAAA,EAAQ,YAAA;AAAA,IACR,cAAc,KAAA,CAAM,YAAA;AAAA,IACpB,GAAI,KAAA,CAAM,IAAA,GAAO,EAAE,IAAA,EAAM,IAAA,KAAS,EAAC;AAAA,IACnC,GAAI,KAAA,CAAM,IAAA,IAAQ,KAAA,CAAM,UAAA,KAAe,UAAU,EAAE,UAAA,EAAY,OAAA,EAAiB,GAAI,EAAC;AAAA,IACrF,SAAA,EAAW,SAAA;AAAA,MACT;AAAA,QACE,GAAI,KAAA,CAAM,IAAA;AAAA;AAAA;AAAA;AAAA,UAIN;AAAA,YACE,EAAE,EAAA,EAAI,GAAA,EAAK,KAAA,EAAO,OAAA,EAAS,QAAQ,CAAC,EAAE,OAAA,EAAS,aAAA,EAAe,MAAM,CAAA,EAAG,MAAA,EAAQ,KAAA,CAAM,OAAA,EAAS,CAAA,EAAE;AAAA,YAChG,GAAI,KAAA,CAAM,OAAA,KAAY,MAAA,GAClB,CAAC,EAAE,EAAA,EAAI,GAAA,EAAK,KAAA,EAAO,aAAA,EAAe,MAAA,EAAQ,CAAC,EAAE,OAAA,EAAS,aAAA,EAAe,IAAA,EAAM,CAAA,EAAG,MAAA,EAAQ,KAAA,CAAM,SAAS,CAAA,EAAG,CAAA,GACxG;AAAC;AACP,YACA;AAAA,UACE,EAAE,EAAA,EAAI,GAAA,EAAK,KAAA,EAAO,MAAA,EAAQ,QAAQ,CAAC,EAAE,OAAA,EAAS,aAAA,EAAe,MAAM,CAAA,EAAG,MAAA,EAAQ,KAAA,CAAM,OAAA,EAAS,CAAA,EAAE;AAAA;AAAA,UAE/F;AAAA,YACE,EAAA,EAAI,GAAA;AAAA,YACJ,KAAA,EAAO,MAAA;AAAA,YACP,MAAA,EAAQ;AAAA,cACN;AAAA,gBACE,OAAA,EAAS,aAAA;AAAA,gBACT,IAAA,EAAM,CAAA;AAAA,gBACN,MAAA,EAAQ,KAAA,CAAM,OAAA,KAAY,MAAA,GAAS,WAAW,KAAA,CAAM;AAAA;AACtD;AACF;AACF,SACF;AAAA;AAAA;AAAA,QAGJ,GAAG,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAC,GAAG,CAAA,MAAO;AAAA,UAC/B,EAAA,EAAI,MAAA,CAAO,YAAA,CAAa,EAAA,GAAK,CAAC,CAAA;AAAA;AAAA,UAC9B,KAAA,EAAO,CAAA,CAAE,KAAA,IAAS,CAAA,OAAA,EAAU,IAAI,CAAC,CAAA,CAAA;AAAA,UACjC,MAAA,EAAQ,CAAC,EAAE,OAAA,EAAS,aAAA,EAAe,MAAM,CAAA,EAAG,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAQ,CAAA;AAAA,UAC9D,IAAI,EAAE,GAAA,EAAK,EAAE,GAAA,EAAK,IAAA,EAAM,EAAE,IAAA;AAAK,SACjC,CAAE;AAAA,OACJ;AAAA,MACA,KAAA,CAAM;AAAA,KACR;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,KAAA,CAAM,IAAA;AAAA;AAAA,QAEF,EAAE,EAAA,EAAI,aAAA,EAAe,IAAA,EAAM,MAAA,EAAiB,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,CAAA,EAAG,KAAA,EAAO,KAAA,CAAM,YAAA;AAAa,UAC3F,EAAE,EAAA,EAAI,aAAA,EAAe,IAAA,EAAM,MAAA,EAAiB,MAAM,CAAA,EAAG,IAAA,EAAM,GAAA,EAAK,EAAA,EAAI,GAAA,EAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQ5E,GAAI,CAAC,KAAA,CAAM,IAAA,KAAS,MAAM,OAAA,KAAY,QAAA,IAAY,KAAA,CAAM,OAAA,KAAY,YAChE,CAAC,UAAA,CAAW,KAAK,CAAC,IAClB,EAAC;AAAA,MACL,GAAI,MAAM,IAAA,IAAQ,KAAA,CAAM,eAAe,OAAA,GACnC,CAAC,EAAE,EAAA,EAAI,cAAA,EAAgB,MAAM,MAAA,EAAiB,IAAA,EAAM,GAAG,OAAA,EAAS,CAAA,EAAG,OAAO,KAAA,CAAM,YAAA,EAAc,CAAA,GAC9F,EAAC;AAAA,MACL,GAAG,KAAA,CAAM,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QAC/B,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,SAAS,CAAA,CAAE,OAAA;AAAA,QACX,OAAO,CAAA,CAAE,KAAA;AAAA,QACT,eAAe,CAAA,CAAE,aAAA;AAAA,QACjB,SAAA,EAAW,EAAE,SAAA,IAAa,MAAA;AAAA,QAC1B,YAAA,EAAc,IAAA,CAAK,KAAA,CAAM,iBAAA,CAAkB,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,OAAO,CAAC,CAAC,CAAA;AAAA,QACzE,GAAI,MAAM,IAAA,IAAQ,CAAA,CAAE,SAAS,EAAE,MAAA,EAAQ,IAAA,EAAK,GAAI;AAAC,OACnD,CAAE;AAAA,KACJ;AAAA,IACA,QAAA,EAAU,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MACnC,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,KAAK,CAAA,CAAE,GAAA;AAAA,MACP,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,cAAc,CAAA,CAAE,YAAA;AAAA,MAChB,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,IAAA,EAAM,EAAE,IAAA,IAAQ;AAAA,KAClB,CAAE,CAAA;AAAA,IACF,GAAI,KAAA,CAAM,SAAA,CAAU,MAAA,GAAS,CAAA,GACzB;AAAA,MACE,SAAA,EAAW,KAAA,CAAM,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QACrC,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,KAAK,CAAA,CAAE,GAAA;AAAA,QACP,MAAA,EAAQ,CAAC,CAAA,CAAE,MAAA,EAAQ,EAAE,MAAM,CAAA;AAAA,QAC3B,IAAA,EAAM,EAAE,IAAA,IAAQ;AAAA,OAClB,CAAE;AAAA,QAEJ,EAAC;AAAA,IACL,aAAA,EAAe,KAAA,CAAM,aAAA,CAAc,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAC7C,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,GAAI,EAAE,SAAA,EAAW,MAAA,GAAS,EAAE,SAAA,EAAW,CAAA,CAAE,SAAA,EAAU,GAAI,EAAC;AAAA,MACxD,OAAA,EAAS,CAAA,CAAE,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QAC7B,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,KAAK,CAAA,CAAE,GAAA;AAAA,QACP,OAAO,CAAA,CAAE,KAAA;AAAA,QACT,QAAQ,CAAA,CAAE,MAAA;AAAA,QACV,IAAA,EAAM,MAAA;AAAA,QACN,MAAM,CAAA,CAAE;AAAA,OACV,CAAE;AAAA,KACJ,CAAE;AAAA,GACJ;AACF;AASO,SAAS,UAAA,CACd,GAAA,EACA,cAAA,EACA,YAAA,GAAiC,EAAC,EACrB;AACb,EAAA,MAAM,IAAA,GAAO,iBAAiB,cAAc,CAAA;AAC5C,EAAA,MAAM,CAAA,GACJ,GAAA,IAAO,OAAO,GAAA,KAAQ,WAAY,GAAA,GAA6B,IAAA;AACjE,EAAA,MAAM,MAAA,GAAS,CAAC,CAAC,CAAA,IAAK,OAAO,CAAA,CAAE,YAAA,KAAiB,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,MAAM,CAAA;AAIlF,EAAA,MAAM,MAAM,cAAA,GAAiB,CAAA,GAAI,cAAA,GAAiB,MAAA,GAAS,EAAG,YAAA,GAAgB,EAAA;AAC9E,EAAA,MAAM,SAAS,MAAA,IAAU,CAAA,CAAG,YAAA,GAAgB,CAAA,GAAI,EAAG,YAAA,GAAgB,GAAA;AACnE,EAAA,MAAM,KAAA,GAAQ,MAAA,GAAS,CAAA,GAAI,GAAA,GAAM,MAAA,GAAS,CAAA;AAC1C,EAAA,MAAM,KAAK,CAAC,CAAA,KAAc,IAAA,CAAK,KAAA,CAAM,IAAI,KAAK,CAAA;AAE9C,EAAA,MAAM,MAAA,GAAS,CAAC,EAAA,KAA0C;AACxD,IAAA,MAAM,EAAA,GAAK,EAAA,IAAM,IAAA,GAAO,YAAA,CAAa,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAE,CAAA,GAAI,MAAA;AAChE,IAAA,OAAO,IAAI,UAAA,IAAc,EAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,MAAM,cAA6B,EAAC;AACpC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAC/B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,KAAA,MAAW,CAAA,IAAK,EAAG,MAAA,EAAQ;AACzB,MAAA,IAAI,CAAA,CAAE,SAAS,MAAA,EAAQ;AACvB,MAAA,MAAM,aAAA,GAAgB,EAAE,aAAA,IAAiB,IAAA;AACzC,MAAA,IAAI,aAAA,IAAiB,IAAA,EAAM,MAAA,CAAO,GAAA,CAAI,aAAa,CAAA;AACnD,MAAA,WAAA,CAAY,IAAA,CAAK;AAAA,QACf,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,IAAA,EAAO,EAAE,IAAA,IAAsB,QAAA;AAAA,QAC/B,IAAA,EAAM,EAAE,IAAA,IAAQ,CAAA;AAAA,QAChB,OAAA,EAAS,EAAA,CAAG,CAAA,CAAE,OAAA,IAAW,CAAC,CAAA;AAAA,QAC1B,OAAO,CAAA,CAAE,KAAA,IAAS,OAAO,EAAA,CAAG,CAAA,CAAE,KAAK,CAAA,GAAI,GAAA;AAAA,QACvC,aAAA;AAAA,QACA,SAAA,EAAW,CAAA,CAAE,SAAA,IAAa,MAAA,CAAO,aAAa,CAAA;AAAA,QAC9C,GAAI,CAAA,CAAE,MAAA,GAAS,EAAE,MAAA,EAAQ,IAAA,KAAS;AAAC,OACpC,CAAA;AAAA,IACH;AAAA,EACF;AAIA,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,MAAA,CAAO,CAAC,EAAA,KAAO,CAAC,MAAA,CAAO,GAAA,CAAI,EAAA,CAAG,EAAE,CAAC,CAAA;AAC7D,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,KAAA,MAAW,MAAM,WAAA,EAAa;AAC5B,IAAA,IAAI,EAAA,CAAG,aAAA,IAAiB,IAAA,IAAQ,EAAA,GAAK,OAAO,MAAA,EAAQ;AAClD,MAAA,MAAM,EAAA,GAAK,OAAO,EAAA,EAAI,CAAA;AACtB,MAAA,EAAA,CAAG,gBAAgB,EAAA,CAAG,EAAA;AACtB,MAAA,IAAI,CAAC,EAAA,CAAG,SAAA,EAAW,EAAA,CAAG,SAAA,GAAY,GAAG,UAAA,IAAc,EAAA;AACnD,MAAA,MAAA,CAAO,GAAA,CAAI,GAAG,EAAE,CAAA;AAAA,IAClB;AAAA,EACF;AACA,EAAA,IAAI,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAG,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAC,CAAA;AACxD,EAAA,KAAA,MAAW,MAAM,YAAA,EAAc;AAC7B,IAAA,IAAI,MAAA,CAAO,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA,EAAG;AACvB,IAAA,IAAA,IAAQ,CAAA;AACR,IAAA,WAAA,CAAY,IAAA,CAAK;AAAA,MACf,EAAA,EAAI,CAAA,EAAA,EAAK,EAAA,CAAG,EAAE,CAAA,CAAA;AAAA,MACd,IAAA,EAAM,QAAA;AAAA,MACN,IAAA;AAAA,MACA,OAAA,EAAS,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAG,CAAA;AAAA,MAC7B,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAG,CAAA;AAAA,MAC3B,eAAe,EAAA,CAAG,EAAA;AAAA,MAClB,SAAA,EAAW,GAAG,UAAA,IAAc;AAAA,KAC7B,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,CAAC,QAAQ,OAAO,EAAE,GAAG,IAAA,EAAM,YAAA,EAAc,KAAK,WAAA,EAAY;AAE9D,EAAA,MAAM,QAAA,GAAW,CAAC,EAAA,KAA4B;AAC5C,IAAA,MAAM,EAAA,GAAA,CAAM,CAAA,CAAG,SAAA,IAAa,EAAC,EAAG,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAE,CAAA;AACvD,IAAA,OAAO,IAAI,MAAA,GAAS,CAAC,CAAA,EAAG,MAAA,KAAW,WAAW,QAAA,GAAW,QAAA;AAAA,EAC3D,CAAA;AACA,EAAA,MAAM,IAAA,GAAO,UAAU,CAAE,CAAA;AACzB,EAAA,MAAM,IAAA,GAAA,CAAQ,CAAA,CAAG,SAAA,IAAa,EAAC,EAAG,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,GAAG,CAAA;AAE1D,EAAA,MAAM,SAAA,GAAA,CAAa,CAAA,CAAG,SAAA,IAAa,EAAC,EAAG,MAAA;AAAA,IACrC,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,OAAO,CAAA,CAAE,EAAA,KAAO,OAAO,CAAA,CAAE;AAAA,GAC3C;AACA,EAAA,MAAM,aAAA,GAAgB,qBAAqB,CAAE,CAAA;AAC7C,EAAA,OAAO;AAAA,IACL,YAAA,EAAc,GAAA;AAAA,IACd,IAAA;AAAA,IACA,UAAA,EAAY,IAAA,IAAQ,CAAA,CAAG,UAAA,KAAe,UAAU,OAAA,GAAU,MAAA;AAAA,IAC1D,OAAA,EAAS,SAAS,GAAG,CAAA;AAAA;AAAA,IAErB,SAAS,IAAA,IAAQ,CAAC,IAAA,GAAO,MAAA,GAAS,SAAS,GAAG,CAAA;AAAA,IAC9C,QAAA,EAAU,SAAA,CAAU,GAAA,CAAI,CAAC,EAAA,MAAQ;AAAA,MAC/B,KAAA,EAAO,GAAG,KAAA,IAAS,QAAA;AAAA,MACnB,GAAA,EAAK,EAAA,CAAG,EAAA,CAAG,EAAA,CAAI,GAAG,CAAA;AAAA,MAClB,IAAA,EAAM,EAAA,CAAG,EAAA,CAAI,IAAA,KAAS,SAAS,MAAA,GAAS,IAAA;AAAA,MACxC,QAAQ,EAAA,CAAG,MAAA,GAAS,CAAC,CAAA,EAAG,MAAA,KAAW,WAAW,QAAA,GAAW;AAAA,KAC3D,CAAE,CAAA;AAAA,IACF,aAAA;AAAA,IACA,YAAY,CAAA,CAAG,SAAA,IAAa,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAC1C,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,IAAA,EAAM,EAAE,IAAA,IAAQ,EAAA;AAAA,MAChB,GAAA,EAAK,EAAA,CAAG,CAAA,CAAE,GAAG,CAAA;AAAA,MACb,MAAA,EAAQ,CAAA,CAAE,MAAA,GAAS,CAAC,CAAA,IAAK,aAAA;AAAA,MACzB,MAAA,EAAQ,CAAA,CAAE,MAAA,GAAS,CAAC,CAAA,IAAK;AAAA,KAC3B,CAAE,CAAA;AAAA,IACF,WAAA;AAAA,IACA,WAAW,CAAA,CAAG,QAAA,IAAY,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MACxC,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,IAAA,EAAM,EAAE,IAAA,IAAQ,EAAA;AAAA,MAChB,GAAA,EAAK,EAAA,CAAG,CAAA,CAAE,GAAG,CAAA;AAAA,MACb,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,cAAc,CAAA,CAAE,YAAA;AAAA,MAChB,IAAA,EAAO,EAAE,IAAA,IAAwB;AAAA,KACnC,CAAE,CAAA;AAAA,IACF,aAAA,EAAe,iBAAA,CAAkB,CAAA,EAAI,EAAE;AAAA,GACzC;AACF;AAGA,SAAS,iBAAA,CACP,CAAA,EACA,EAAA,GAA4B,CAAC,MAAM,CAAA,EACb;AACtB,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,aAAa,CAAA,EAAG;AAClC,IAAA,OAAO,CAAA,CAAE,aAAA,CAAc,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MACjC,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,IAAA,EAAM,EAAE,IAAA,IAAQ,EAAA;AAAA,MAChB,QAAA,EAAU,CAAA,CAAE,QAAA,IAAY,EAAC;AAAA,MACzB,GAAI,EAAE,SAAA,EAAW,MAAA,GAAS,EAAE,SAAA,EAAW,CAAA,CAAE,SAAA,EAAU,GAAI,EAAC;AAAA,MACxD,UAAU,CAAA,CAAE,OAAA,IAAW,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QACrC,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,GAAA,EAAK,EAAA,CAAG,CAAA,CAAE,GAAG,CAAA;AAAA,QACb,KAAA,EAAO,EAAE,KAAA,IAAS,aAAA;AAAA,QAClB,MAAA,EAAS,EAAE,MAAA,IAA2B,MAAA;AAAA,QACtC,IAAA,EAAO,EAAE,IAAA,IAAuB;AAAA,OAClC,CAAE;AAAA,KACJ,CAAE,CAAA;AAAA,EACJ;AAEA,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAgC;AACnD,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,KAAA,MAAW,CAAA,IAAK,CAAA,CAAE,OAAA,IAAW,EAAC,EAAG;AAC/B,IAAA,MAAM,GAAA,GAAM,CAAA,CAAE,OAAA,IAAW,CAAA,IAAA,EAAO,EAAE,EAAE,CAAA,CAAA;AACpC,IAAA,IAAI,EAAA,GAAK,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,EAAA,GAAK,EAAE,IAAI,CAAA,EAAA,EAAK,EAAE,CAAC,CAAA,CAAA,EAAI,IAAA,EAAM,EAAE,IAAA,IAAQ,EAAA,EAAI,UAAU,CAAA,CAAE,OAAA,GAAU,CAAC,CAAA,CAAE,OAAO,IAAI,EAAC,EAAG,OAAA,EAAS,EAAC,EAAE;AAC/F,MAAA,MAAA,CAAO,GAAA,CAAI,KAAK,EAAE,CAAA;AAAA,IACpB;AACA,IAAA,EAAA,CAAG,QAAQ,IAAA,CAAK;AAAA,MACd,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,GAAA,EAAK,EAAA,CAAG,CAAA,CAAE,GAAG,CAAA;AAAA,MACb,KAAA,EAAO,EAAE,KAAA,IAAS,aAAA;AAAA,MAClB,MAAA,EAAS,EAAE,MAAA,IAA2B,MAAA;AAAA,MACtC,IAAA,EAAO,EAAE,IAAA,IAAuB;AAAA,KACjC,CAAA;AAAA,EACH;AACA,EAAA,OAAO,CAAC,GAAG,MAAA,CAAO,MAAA,EAAQ,CAAA;AAC5B;AAGO,SAAS,MAAA,CAAO,QAAgB,QAAA,EAA4B;AACjE,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,OAAO,QAAA,CAAS,SAAS,CAAA,EAAG,MAAM,GAAG,CAAC,CAAA,CAAE,GAAG,CAAA,IAAK,CAAA;AAChD,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,CAAC,CAAA,CAAA;AACtB;AAOO,SAAS,mBAAmB,KAAA,EAIjC;AACA,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,YAAA,GAAe,CAAA,GAAI,MAAM,YAAA,GAAe,EAAA;AAC1D,EAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,GAAA,GAAM,IAAI,CAAC,CAAA;AAChD,EAAA,MAAM,OAAA,GAAU,KAAA;AAChB,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,OAAA,GAAU,CAAA,EAAG,MAAM,KAAK,CAAA;AAE/C,EAAA,MAAM,WACJ,KAAA,CAAM,OAAA,KAAY,YAAY,KAAA,CAAM,OAAA,KAAY,WAAW,CAAA,GAAI,CAAA;AACjE,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,GAAG,KAAA,CAAM,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,GAAO,CAAC,CAAC,CAAA;AAE3E,EAAA,MAAM,QAAA,GAAW,CAAC,aAAA,EAAe,GAAG,KAAA,CAAM,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AACtE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,EAAO,QAAQ,CAAA;AACpC,EAAA,MAAM,KAAA,GAAqB;AAAA,IACzB,EAAA,EAAI,KAAA;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,IAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,aAAA,EAAe,IAAA;AAAA,IACf,SAAA,EAAW;AAAA,GACb;AAEA,EAAA,MAAM,QAAQ,KAAA,CAAM,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,EAAE,EAAE,CAAA;AAC5C,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,EAAM,KAAK,CAAA;AAC9B,EAAA,MAAM,MAAM,MAAA,CAAO,IAAA,EAAM,CAAC,GAAG,KAAA,EAAO,GAAG,CAAC,CAAA;AAIxC,EAAA,MAAM,QAAA,GAA4B;AAAA,IAChC,EAAE,EAAA,EAAI,GAAA,EAAK,IAAA,EAAM,aAAA,EAAe,GAAA,EAAK,OAAA,EAAS,OAAA,EAAS,aAAA,EAAe,YAAA,EAAc,KAAA,EAAO,IAAA,EAAM,MAAA,EAAO;AAAA,IACxG,EAAE,EAAA,EAAI,GAAA,EAAK,IAAA,EAAM,aAAA,EAAe,GAAA,EAAK,KAAA,EAAO,OAAA,EAAS,aAAA,EAAe,YAAA,EAAc,KAAA,EAAO,IAAA,EAAM,OAAA;AAAQ,GACzG;AAIA,EAAA,MAAM,QAAQ,KAAA,CAAM,aAAA,CAAc,IAAI,CAAC,CAAA,KAAM,EAAE,EAAE,CAAA;AACjD,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,EAAM,KAAK,CAAA;AAC9B,EAAA,MAAM,MAAM,MAAA,CAAO,IAAA,EAAM,CAAC,GAAG,KAAA,EAAO,GAAG,CAAC,CAAA;AACxC,EAAA,MAAM,GAAA,GAAM,CAAC,IAAA,EAAc,GAAA,EAAa,MAAA,MAA0C;AAAA,IAChF,EAAA,EAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,IACrB,GAAA;AAAA,IACA,KAAA,EAAO,aAAA;AAAA,IACP,MAAA;AAAA;AAAA,IAEA,IAAA,EAAM,MAAA,KAAW,MAAA,GAAS,OAAA,GAAU;AAAA,GACtC,CAAA;AACA,EAAA,MAAM,aAAA,GAAsC;AAAA,IAC1C,EAAE,IAAI,GAAA,EAAK,IAAA,EAAM,eAAe,QAAA,EAAU,CAAC,GAAG,CAAA,EAAG,OAAA,EAAS,CAAC,GAAA,CAAI,GAAA,EAAK,SAAS,MAAM,CAAA,EAAG,IAAI,GAAA,EAAK,OAAA,EAAS,MAAM,CAAC,CAAA,EAAE;AAAA,IACjH,EAAE,IAAI,GAAA,EAAK,IAAA,EAAM,eAAe,QAAA,EAAU,CAAC,GAAG,CAAA,EAAG,OAAA,EAAS,CAAC,GAAA,CAAI,GAAA,EAAK,OAAO,MAAM,CAAA,EAAG,IAAI,GAAA,EAAK,KAAA,EAAO,MAAM,CAAC,CAAA;AAAE,GAC/G;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,aAAA,EAAc;AAC1C;AAQO,SAAS,eAAe,KAAA,EAGtB;AACP,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,KAAY,QAAA,IAAY,MAAM,OAAA,KAAY,QAAA;AAClE,EAAA,IAAI,KAAA,CAAM,IAAA,IAAQ,CAAC,SAAA,EAAW,OAAO,IAAA;AACrC,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,YAAA,GAAe,CAAA,GAAI,MAAM,YAAA,GAAe,EAAA;AAC1D,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA;AAC9B,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,GAAA,GAAM,IAAI,CAAC,CAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,MAAM,CAAC,CAAA;AAClC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,MAAM,CAAC,CAAA;AAElC,EAAA,MAAM,QAAA,GAAW,CAAC,aAAA,EAAe,cAAA,EAAgB,GAAG,KAAA,CAAM,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AACtF,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,EAAM,QAAQ,CAAA;AAClC,EAAA,MAAM,KAAA,GAAqB;AAAA,IACzB,EAAA,EAAI,IAAA;AAAA,IACJ,IAAA,EAAM,WAAA;AAAA,IACN,IAAA,EAAM,CAAA;AAAA,IACN,OAAA,EAAS,CAAA;AAAA,IACT,KAAA,EAAO,CAAA;AAAA,IACP,aAAA,EAAe,IAAA;AAAA,IACf,SAAA,EAAW;AAAA,GACb;AAEA,EAAA,MAAM,QAAQ,KAAA,CAAM,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,EAAE,EAAE,CAAA;AAC5C,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,EAAM,KAAK,CAAA;AAC9B,EAAA,MAAM,MAAM,MAAA,CAAO,IAAA,EAAM,CAAC,GAAG,KAAA,EAAO,GAAG,CAAC,CAAA;AACxC,EAAA,MAAM,QAAA,GAA4B;AAAA,IAChC,EAAE,EAAA,EAAI,GAAA,EAAK,IAAA,EAAM,WAAA,EAAa,GAAA,EAAK,CAAA,EAAG,OAAA,EAAS,aAAA,EAAe,YAAA,EAAc,IAAA,EAAM,IAAA,EAAM,MAAA,EAAO;AAAA,IAC/F,EAAE,EAAA,EAAI,GAAA,EAAK,IAAA,EAAM,WAAA,EAAa,GAAA,EAAK,CAAA,EAAG,OAAA,EAAS,cAAA,EAAgB,YAAA,EAAc,IAAA,EAAM,IAAA,EAAM,OAAA;AAAQ,GACnG;AAEA,EAAA,OAAO,EAAE,OAAO,QAAA,EAAS;AAC3B;AAkIO,SAAS,kBAAA,CACd,MACA,OAAA,EACY;AACZ,EAAA,IAAI,IAAA,KAAS,MAAA,IAAU,IAAA,KAAS,OAAA,EAAS,OAAO,CAAA;AAChD,EAAA,MAAM,CAAA,GAAI,OAAA,IAAW,CAAA,GAAI,CAAA,GAAI,EAAA;AAC7B,EAAA,OAAO,IAAA,KAAS,MAAA,GAAU,CAAA,GAAiB,CAAC,CAAA;AAC9C;AAUO,SAAS,eAAe,GAAA,EAAyC;AACtE,EAAA,MAAM,GAAA,GACJ,IAAI,YAAA,IAAgB,GAAA,CAAI,eAAe,CAAA,GACnC,GAAA,CAAI,eACJ,IAAA,CAAK,GAAA;AAAA,IACH,CAAA;AAAA,IACA,GAAG,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,OAAA,IAAW,CAAA,EAAG,CAAA,CAAE,KAAA,IAAS,CAAC,CAAC,CAAA;AAAA,IAC/D,GAAA,CAAI,IAAI,QAAA,IAAY,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG;AAAA,GAC1C;AACN,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAA,GAAI,GAAG,CAAC,CAAA;AAEjE,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAoB;AAC1C,EAAA,KAAA,MAAW,CAAA,IAAK,IAAI,MAAA,EAAQ,SAAA,CAAU,IAAI,CAAA,CAAE,EAAA,EAAI,EAAE,IAAI,CAAA;AAGtD,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,GAAA,CAAI,SAAA,CAAU,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM,WAAA,CAAY,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,CAAA,KAAM,CAAA,GAAI,CAAA,GAAI,GAAG,CAAC,CAAA;AACxE,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAoB;AAC3C,EAAA,KAAA,MAAW,CAAA,IAAK,GAAA,CAAI,QAAA,IAAY,EAAC,aAAc,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,CAAA,CAAE,GAAG,CAAA;AAC9D,EAAA,MAAM,KAAA,GAAQ,CAAC,MAAA,KAAmC;AAChD,IAAA,IAAI,MAAA,IAAU,MAAM,OAAO,IAAA;AAC3B,IAAA,IAAI,YAAY,GAAA,CAAI,MAAM,GAAG,OAAO,WAAA,CAAY,IAAI,MAAM,CAAA;AAC1D,IAAA,IAAI,WAAW,GAAA,CAAI,MAAM,GAAG,OAAO,UAAA,CAAW,IAAI,MAAM,CAAA;AACxD,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,IAAI,SAAA,CAAU,IAAA;AAAA,IAAK,CAAC,MACrC,CAAA,CAAE,MAAA,EAAQ,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW,QAAQ;AAAA,GAC7C;AAGA,EAAA,MAAM,aAAA,GAAgB,CAAC,OAAA,KAA4B;AACjD,IAAA,MAAM,EAAA,GAAA,CAAM,GAAA,CAAI,QAAA,IAAY,EAAC,EAAG,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,YAAA,KAAiB,OAAO,CAAA;AACtE,IAAA,OAAO,KAAM,SAAA,CAAU,GAAA,CAAI,EAAA,CAAG,OAAO,KAAK,CAAA,GAAK,CAAA;AAAA,EACjD,CAAA;AAGA,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAgC;AAC5D,EAAA,KAAA,MAAW,EAAA,IAAM,GAAA,CAAI,QAAA,IAAY,EAAC,EAAG;AACnC,IAAA,MAAM,MAAM,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,YAAY,KAAK,EAAC;AACrD,IAAA,GAAA,CAAI,KAAK,EAAE,CAAA;AACX,IAAA,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,YAAA,EAAc,GAAG,CAAA;AAAA,EAC1C;AAIA,EAAA,MAAM,WAAA,GAAc,CAAC,OAAA,KAA6B;AAChD,IAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,GAAA,CAAI,OAAO,KAAK,EAAC;AAC7C,IAAA,IAAI,GAAA,CAAI,MAAA,GAAS,CAAA,EAAG,OAAO,KAAA;AAC3B,IAAA,OAAO,IAAI,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,KAAM,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,OAAO,CAAA,IAAK,CAAC,CAAC,EAAE,IAAA,IAAQ,CAAA;AAAA,EACxE,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,CAAC,CAAA,KAA+C;AAC/D,IAAA,MAAM,IAAA,GAAO,CAAA,CAAE,OAAA,IAAW,KAAA,CAAM,EAAE,IAAI,CAAA;AACtC,IAAA,MAAM,EAAA,GAAK,CAAA,CAAE,KAAA,IAAS,KAAA,CAAM,EAAE,EAAE,CAAA;AAChC,IAAA,OAAO,QAAQ,IAAA,IAAQ,EAAA,IAAM,OAAO,IAAA,GAAO,CAAC,MAAM,EAAE,CAAA;AAAA,EACtD,CAAA;AASA,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAC1D,EAAA,MAAM,aAAA,uBAAoB,GAAA,EAAoB;AAC9C,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAY;AAClC,EAAA,MAAM,WAAA,GAAc,CAAC,EAAA,KAAuB;AAC1C,IAAA,MAAM,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,EAAE,CAAA;AAC5B,IAAA,IAAI,CAAC,KAAK,OAAO,CAAA;AACjB,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,MAAA,EAAQ,OAAO,GAAA,CAAI,IAAA;AACpC,IAAA,IAAI,cAAc,GAAA,CAAI,EAAE,GAAG,OAAO,aAAA,CAAc,IAAI,EAAE,CAAA;AACtD,IAAA,IAAI,SAAA,CAAU,GAAA,CAAI,EAAE,CAAA,SAAU,GAAA,CAAI,IAAA;AAClC,IAAA,SAAA,CAAU,IAAI,EAAE,CAAA;AAChB,IAAA,IAAI,OAAO,GAAA,CAAI,IAAA;AACf,IAAA,MAAM,EAAA,GAAK,eAAA,CAAgB,GAAA,CAAI,EAAE,IAAI,CAAC,CAAA;AACtC,IAAA,MAAM,GAAA,GAAM,SAAS,GAAG,CAAA;AACxB,IAAA,IAAI,EAAA,KAAO,EAAA,CAAG,IAAA,KAAS,MAAA,IAAU,EAAA,CAAG,IAAA,KAAS,OAAA,CAAA,IAAY,CAAC,WAAA,CAAY,EAAE,CAAA,IAAK,GAAA,EAAK;AAChF,MAAA,MAAM,UAAA,GAAa,WAAA,CAAY,EAAA,CAAG,OAAO,CAAA;AACzC,MAAA,IAAI,IAAA;AACJ,MAAA,IAAI,eAAe,CAAA,EAAG;AAEpB,QAAA,MAAM,CAAC,IAAA,EAAM,EAAE,CAAA,GAAI,GAAA;AACnB,QAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,EAAA,GAAK,EAAA,CAAG,GAAG,CAAA,IAAK,IAAA,CAAK,GAAA,CAAI,IAAA,GAAO,EAAA,CAAG,GAAG,IAAI,EAAA,GAAK,IAAA;AACpE,QAAA,MAAM,IAAI,kBAAA,CAAmB,EAAA,CAAG,IAAA,EAAM,GAAA,GAAM,GAAG,GAAG,CAAA;AAClD,QAAA,IAAA,GAAO,MAAM,CAAA,GAAI,CAAA,GAAI,KAAK,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,IAAK,CAAA;AAAA,MAC9C,CAAA,MAAO;AAEL,QAAA,IAAA,GAAO,IAAA,CAAK,IAAA,CAAK,UAAU,CAAA,IAAK,CAAA;AAAA,MAClC;AACA,MAAA,IAAA,GAAO,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,IAAI,CAAA;AAAA,IACjC;AACA,IAAA,SAAA,CAAU,OAAO,EAAE,CAAA;AACnB,IAAA,aAAA,CAAc,GAAA,CAAI,IAAI,IAAI,CAAA;AAC1B,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,IAAI,MAAA,EAAQ;AAC1B,IAAA,IAAI,CAAA,CAAE,SAAS,MAAA,EAAQ;AACvB,IAAA,SAAA,CAAU,IAAI,CAAA,CAAE,EAAA,EAAI,WAAA,CAAY,CAAA,CAAE,EAAE,CAAC,CAAA;AAAA,EACvC;AAEA,EAAA,MAAM,cAA2B,EAAC;AAClC,EAAA,KAAA,MAAW,CAAA,IAAK,IAAI,MAAA,EAAQ;AAC1B,IAAA,IAAI,CAAA,CAAE,SAAS,MAAA,EAAQ;AACvB,IAAA,IAAI,WAAA,CAAY,CAAA,CAAE,EAAE,CAAA,EAAG;AACvB,IAAA,MAAM,GAAA,GAAM,SAAS,CAAC,CAAA;AACtB,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,MAAM,CAAC,IAAA,EAAM,EAAE,CAAA,GAAI,GAAA;AAKnB,IAAA,MAAM,KAAK,eAAA,CAAgB,GAAA,CAAI,CAAA,CAAE,EAAE,IAAI,CAAC,CAAA;AACxC,IAAA,MAAM,UAAA,GAAa,EAAA,IAAM,IAAA,IAAQ,IAAA,CAAK,GAAA,CAAI,EAAA,GAAK,EAAA,CAAG,GAAG,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAA,GAAO,GAAG,GAAG,CAAA;AAC/E,IAAA,MAAM,MAAA,GAAS,aAAa,EAAA,GAAK,IAAA;AACjC,IAAA,MAAM,IAAA,GAAO,aAAa,IAAA,GAAO,EAAA;AACjC,IAAA,WAAA,CAAY,IAAA,CAAK;AAAA,MACf,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,MAAM,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,EAAE,KAAK,CAAA,CAAE,IAAA;AAAA,MAC/B,UAAU,SAAA,CAAU,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAC,CAAA;AAAA,MACtC,QAAQ,SAAA,CAAU,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAC,CAAA;AAAA,MACpC,UAAA,EAAY,UAAU,MAAM,CAAA;AAAA,MAC5B,QAAA,EAAU,UAAU,IAAI,CAAA;AAAA,MACxB,YAAA,EAAc,EAAE,YAAA,IAAgB,IAAA;AAAA,MAChC,gBAAA,EAAkB,aAAA,CAAc,CAAA,CAAE,EAAE,CAAA;AAAA,MACpC,MAAA,EAAQ,EAAE,MAAA,KAAW;AAAA,KACtB,CAAA;AAAA,EACH;AAGA,EAAA,MAAM,aAA8B,EAAC;AAErC,EAAA,KAAA,MAAW,CAAA,IAAK,IAAI,MAAA,EAAQ;AAC1B,IAAA,IAAI,CAAC,WAAA,CAAY,CAAA,CAAE,EAAE,CAAA,EAAG;AACxB,IAAA,MAAM,CAAC,EAAA,EAAI,EAAE,IAAI,eAAA,CAAgB,GAAA,CAAI,EAAE,EAAE,CAAA;AACzC,IAAA,UAAA,CAAW,IAAA,CAAK;AAAA,MACd,IAAI,CAAA,CAAE,EAAA;AAAA,MACN,IAAA,EAAM,EAAE,SAAA,IAAa,EAAA;AAAA,MACrB,WAAA,EAAa,SAAA,CAAU,EAAA,CAAG,GAAG,CAAA;AAAA,MAC7B,QAAA,EAAU,SAAA,CAAU,GAAA,CAAI,EAAA,CAAG,OAAO,CAAA,IAAK,CAAA;AAAA,MACvC,SAAA,EAAW,SAAA,CAAU,EAAA,CAAG,GAAG,CAAA;AAAA,MAC3B,MAAA,EAAQ,SAAA,CAAU,GAAA,CAAI,EAAA,CAAG,OAAO,CAAA,IAAK;AAAA,KACtC,CAAA;AAAA,EACH;AAMA,EAAA,MAAM,QAAA,GAAW,CAAC,EAAA,KAAgB,CAAC,CAAC,MAAM,SAAA,CAAU,GAAA,CAAI,EAAE,CAAA,EAAG,IAAA,KAAS,MAAA;AACtE,EAAA,MAAM,QAAA,GAAW,CAAC,EAAA,KAAA,CACf,GAAA,CAAI,UAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAE,CAAA,EAAG,MAAA,IAAU,EAAC,EAAG,IAAA;AAAA,IACrD,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW;AAAA,GACtB;AACF,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,MAAM,YAAA,GACJ,SAAS,IAAA,GAAA,CAAQ,GAAA,CAAI,YAAY,EAAC,EAAG,IAAA,CAAK,mBAAmB,CAAA,GAAI,MAAA;AACnE,EAAA,MAAM,GAAA,GAAA,CAAO,GAAA,CAAI,QAAA,IAAY,EAAC,EAAG,MAAA;AAAA,IAC/B,CAAC,OACC,EAAA,KAAO,YAAA,IACP,SAAS,EAAA,CAAG,OAAO,CAAA,IACnB,QAAA,CAAS,EAAA,CAAG,YAAY,MACvB,SAAA,CAAU,GAAA,CAAI,GAAG,OAAO,CAAA,IAAK,QAAQ,SAAA,CAAU,GAAA,CAAI,EAAA,CAAG,YAAY,CAAA,IAAK,CAAA;AAAA,GAC5E;AACA,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,EAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,IAAA,IAAI,QAAA,CAAS,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA,EAAG;AACzB,IAAA,MAAM,KAAK,GAAA,CAAI,IAAA;AAAA,MACb,CAAC,CAAA,KACC,CAAC,SAAS,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,IAClB,CAAA,CAAE,EAAA,KAAO,EAAA,CAAG,MACZ,CAAA,CAAE,OAAA,KAAY,GAAG,YAAA,IACjB,CAAA,CAAE,iBAAiB,EAAA,CAAG;AAAA,KAC1B;AACA,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,QAAA,CAAS,GAAA,CAAI,GAAG,EAAE,CAAA;AAClB,IAAA,QAAA,CAAS,GAAA,CAAI,GAAG,EAAE,CAAA;AAClB,IAAA,UAAA,CAAW,IAAA,CAAK;AAAA,MACd,IAAI,CAAA,EAAG,EAAA,CAAG,EAAE,CAAA,CAAA,EAAI,GAAG,EAAE,CAAA,CAAA;AAAA,MACrB,IAAA,EAAM,EAAA,CAAG,IAAA,IAAQ,EAAA,CAAG,IAAA,IAAQ,EAAA;AAAA,MAC5B,WAAA,EAAa,SAAA,CAAU,EAAA,CAAG,GAAG,CAAA;AAAA,MAC7B,QAAA,EAAU,SAAA,CAAU,GAAA,CAAI,EAAA,CAAG,OAAO,CAAA,IAAK,CAAA;AAAA,MACvC,SAAA,EAAW,SAAA,CAAU,EAAA,CAAG,GAAG,CAAA;AAAA,MAC3B,MAAA,EAAQ,SAAA,CAAU,GAAA,CAAI,EAAA,CAAG,OAAO,CAAA,IAAK;AAAA,KACtC,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,YAA2B,GAAA,CAAI,QAAA,IAAY,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAC/D,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,IAAA,EAAM,EAAE,IAAA,IAAQ,EAAA;AAAA,IAChB,OAAA,EAAS,SAAA,CAAU,CAAA,CAAE,GAAG,CAAA;AAAA,IACxB,MAAA,EAAQ,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,OAAO,CAAA,IAAK,CAAA;AAAA,IACpC,WAAA,EAAa,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,YAAY,CAAA,IAAK;AAAA,GAChD,CAAE,CAAA;AAEF,EAAA,MAAM,UAAA,GAAa,CACjB,CAAA,EACA,IAAA,EACA,EAAA,MACgB;AAAA,IAChB,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,IAAA;AAAA,IACA,OAAA,EAAS,SAAA,CAAU,CAAA,CAAE,GAAG,CAAA;AAAA,IACxB,IAAA,EAAM,EAAE,KAAA,GAAS,SAAA,CAAU,IAAI,CAAA,CAAE,KAAK,KAAK,CAAA,GAAK,CAAA;AAAA,IAChD,MAAA,EAAS,EAAE,MAAA,IAA2B,MAAA;AAAA,IACtC,IAAA,EAAM,CAAA,CAAE,IAAA,KAAS,OAAA,GAAU,OAAA,GAAU,OAAA;AAAA,IACrC,GAAI,EAAA,GAAK,EAAE,EAAA,KAAO,EAAC;AAAA,IACnB,KAAA,EAAO;AAAA,GACT,CAAA;AAGA,EAAA,MAAM,UAAwB,KAAA,CAAM,OAAA,CAAQ,IAAI,aAAa,CAAA,GACzD,IAAI,aAAA,CAAc,OAAA;AAAA,IAAQ,CAAC,CAAA,KAAA,CACxB,CAAA,CAAE,OAAA,IAAW,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,UAAA,CAAW,GAAG,CAAA,CAAE,IAAA,IAAQ,EAAA,EAAI,CAAA,CAAE,EAAE,CAAC;AAAA,GAChE,GAAA,CACC,GAAA,CAAI,OAAA,IAAW,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,KAAM,UAAA,CAAW,CAAA,EAAG,CAAA,CAAE,IAAA,IAAQ,EAAE,CAAC,CAAA;AAI9D,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAoB;AAC3C,EAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,CAAA,EAAI,CAAA,CAAE,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,OAAA,GAAU,GAAI,CAAC,CAAA,CAAA;AAC/D,IAAA,MAAM,CAAA,GAAI,UAAA,CAAW,GAAA,CAAI,GAAG,CAAA,IAAK,CAAA;AACjC,IAAA,CAAA,CAAE,KAAA,GAAQ,CAAA;AACV,IAAA,UAAA,CAAW,GAAA,CAAI,GAAA,EAAK,CAAA,GAAI,CAAC,CAAA;AAAA,EAC3B;AAEA,EAAA,MAAM,aAA6B,GAAA,CAAI,SAAA,IAAa,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAClE,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,IAAA,EAAM,EAAE,IAAA,IAAQ,EAAA;AAAA,IAChB,OAAA,EAAS,SAAA,CAAU,CAAA,CAAE,GAAG,CAAA;AAAA,IACxB,KAAA,EAAO,UAAU,GAAA,CAAI,CAAA,CAAE,SAAS,CAAC,CAAA,IAAK,EAAE,CAAA,IAAK,CAAA;AAAA,IAC7C,KAAA,EAAO,UAAU,GAAA,CAAI,CAAA,CAAE,SAAS,CAAC,CAAA,IAAK,EAAE,CAAA,IAAK;AAAA,GAC/C,CAAE,CAAA;AAGF,EAAA,MAAM,mBAAsC,GAAA,CAAI,SAAA,CAC7C,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,GAAA,IAAO,CAAA,CAAE,OAAO,GAAA,IAAO,CAAA,CAAE,EAAE,CAAA,CAClD,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACX,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,KAAA,EAAO,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,EAAA;AAAA,IACpB,OAAA,EAAS,SAAA,CAAU,CAAA,CAAE,EAAA,CAAI,GAAG,CAAA;AAAA,IAC5B,IAAA,EAAM,CAAA,CAAE,EAAA,CAAI,IAAA,KAAS,SAAS,MAAA,GAAS;AAAA,GACzC,CAAE,CAAA;AAEJ,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,CAAA;AAAA,IACA,aAAa,CAAA,GAAI,CAAA;AAAA,IACjB,GAAG,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,IAChC,GAAG,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,IAC5B,GAAG,SAAA,CAAU,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,KAAA,EAAO,CAAA,CAAE,KAAK,CAAC,CAAA;AAAA,IAC9C,GAAG,UAAA,CAAW,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,QAAA,EAAU,CAAA,CAAE,MAAM,CAAC;AAAA,GACrD;AACA,EAAA,MAAM,IAAA,GAAO,UAAU,GAAG,CAAA;AAE1B,EAAA,MAAM,KAAA,GAAQ,IAAI,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,cAAc,CAAA;AAC5D,EAAA,MAAM,eAAA,GACJ,CAAC,CAAC,KAAA,KAAU,KAAA,CAAM,WAAW,IAAA,IAAQ,KAAA,CAAM,KAAA,IAAS,IAAA,CAAA,IAAS,CAAC,IAAA;AAChE,EAAA,IAAI,WAAA,GAA2D,IAAA;AAC/D,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,WAAA,GAAc;AAAA,MACZ,QAAA,EAAU,SAAA,CAAU,KAAA,CAAO,OAAA,IAAW,CAAC,CAAA;AAAA,MACvC,MAAA,EAAQ,SAAA,CAAU,KAAA,CAAO,KAAA,IAAS,GAAG;AAAA,KACvC;AAAA,EACF,WACE,KAAA,IACA,YAAA,IACA,aAAa,YAAA,KAAiB,cAAA,IAC9B,CAAC,IAAA,EACD;AAGA,IAAA,WAAA,GAAc,OACV,EAAE,QAAA,EAAU,CAAA,EAAG,MAAA,EAAQ,UAAU,YAAA,CAAa,GAAG,CAAA,EAAE,GACnD,EAAE,QAAA,EAAU,SAAA,CAAU,aAAa,GAAG,CAAA,EAAG,QAAQ,CAAA,EAAE;AAAA,EACzD;AAGA,EAAA,MAAM,UAAA,GACJ,YAAA,IAAgB,IAAA,KAAS,IAAA,IAAQ,CAAC,IAAA,GAC9B;AAAA,IACE,WAAA,EAAa,SAAA,CAAU,GAAA,CAAI,YAAA,CAAa,OAAO,CAAA,IAAK,CAAA;AAAA,IACpD,UAAA,EAAY,SAAA,CAAU,GAAA,CAAI,YAAA,CAAa,YAAY,CAAA,IAAK,CAAA;AAAA,IACxD,MAAA,EAAQ,SAAA,CAAU,YAAA,CAAa,GAAG,CAAA;AAAA,IAClC,UAAA,EAAa,OAAO,MAAA,GAAS;AAAA,GAC/B,GACA,IAAA;AACN,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,IAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA,eAAA,EAAiB,IAAA,IAAQ,GAAA,CAAI,SAAA,CAAU,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAE,CAAA,CAAE,MAAA,IAAU,CAAA;AAAA,IACtE,UAAA,EAAY,IAAA,IAAQ,GAAA,CAAI,UAAA,KAAe,UAAU,OAAA,GAAU,MAAA;AAAA,IAC3D,UAAA,EAAY,IAAA,GAAQ,GAAA,CAAI,UAAA,IAAc,IAAA,GAAQ,IAAA;AAAA,IAC9C,WAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA,EAAS,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ,CAAA;AAAA,IAC7B,OAAA,EAAS,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ;AAAA,GAC/B;AACF;AAiEO,SAAS,mBAAA,CACd,cACA,eAAA,EACQ;AACR,EAAA,QAAQ,YAAA;AAAc,IACpB,KAAK,WAAA;AACH,MAAA,OAAO,EAAA;AAAA,IACT,KAAK,WAAA;AACH,MAAA,OAAO,EAAA;AAAA,IACT,KAAK,OAAA;AACH,MAAA,OAAO,eAAA,IAAmB,CAAA;AAAA,IAC5B;AACE,MAAA,OAAO,CAAA;AAAA;AAEb;AAEA,IAAM,GAAA,GAAM,KAAK,EAAA,GAAK,GAAA;AACtB,IAAM,OAAA,GAAU,CAAC,CAAA,KAAA,CAAgB,CAAA,GAAI,MAAO,GAAA,IAAO,GAAA;AAEnD,SAAS,UAAA,CACP,QACA,IAAA,EACU;AACV,EAAA,OAAO,MAAA,KAAW,WAAW,CAAC,CAAC,MAAM,IAAI,CAAA,GAAI,CAAC,CAAC,CAAA;AACjD;AASO,SAAS,oBAAoB,GAAA,EAA0C;AAC5E,EAAA,MAAM,CAAA,GAAI,GAAA,CAAI,YAAA,GAAe,CAAA,GAAI,IAAI,YAAA,GAAe,EAAA;AACpD,EAAA,MAAM,IAAA,GAAO,IAAI,sBAAA,IAA0B,CAAA;AAC3C,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KACX,GAAA,CAAI,kBAAkB,CAAC,CAAA,KAAM,WAAW,QAAA,GAAW,QAAA;AACrD,EAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAAkC;AACtD,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,aAAA,GAAgB,CAAA,CAAE,EAAE,CAAA;AAClC,IAAA,OAAO,IAAI,EAAE,GAAG,CAAA,EAAG,CAAA,EAAG,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,SAAS,OAAA,CAAQ,CAAA,CAAE,OAAO,CAAA,EAAG,MAAA,EAAQ,MAAK,GAAI,CAAA;AAAA,EACnF,CAAA;AAEA,EAAA,MAAM,QAAwB,EAAC;AAG/B,EAAA,KAAA,CAAM,IAAA;AAAA,IACJ,YAAA,CAAa;AAAA,MACX,EAAA,EAAI,GAAA;AAAA,MACJ,CAAA,EAAG,CAAA;AAAA,MACH,CAAA,EAAG,CAAA;AAAA,MACH,OAAA,EAAS,GAAA;AAAA,MACT,WAAA,EAAa,IAAI,CAAC,CAAA;AAAA,MAClB,YAAA,EAAc,UAAA,CAAW,GAAA,CAAI,CAAC,GAAG,IAAI;AAAA,KACtC;AAAA,GACH;AAGA,EAAA,MAAM,GAAA,GAAM,IAAI,YAAA,KAAiB,UAAA;AACjC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAA,GAAO,mBAAA,CAAoB,GAAA,CAAI,YAAA,EAAc,IAAI,eAAe,CAAA;AACtE,IAAA,IAAI,EAAA;AACJ,IAAA,IAAI,EAAA;AACJ,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI,GAAA,CAAI,iBAAiB,QAAA,EAAU;AAEjC,MAAA,EAAA,GAAK,CAAA;AACL,MAAA,EAAA,GAAK,IAAI,oBAAA,IAAwB,CAAA;AACjC,MAAA,QAAA,GAAW,CAAA;AAAA,IACb,CAAA,MAAA,IAAW,SAAS,CAAA,EAAG;AACrB,MAAA,EAAA,GAAK,CAAA;AACL,MAAA,EAAA,GAAK,CAAA;AACL,MAAA,QAAA,GAAW,CAAA;AAAA,IACb,CAAA,MAAO;AAEL,MAAA,MAAM,IAAI,IAAA,GAAO,GAAA;AACjB,MAAA,MAAM,IAAI,CAAA,GAAI,CAAA;AACd,MAAA,EAAA,GAAK,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACnB,MAAA,EAAA,GAAK,CAAA,IAAK,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,CAAA;AACxB,MAAA,QAAA,GAAW,IAAA;AAAA,IACb;AACA,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,YAAA,CAAa;AAAA,QACX,EAAA,EAAI,GAAA;AAAA,QACJ,CAAA,EAAG,EAAA;AAAA,QACH,CAAA,EAAG,EAAA;AAAA,QACH,OAAA,EAAS,QAAQ,QAAQ,CAAA;AAAA,QACzB,WAAA,EAAa,IAAI,CAAC,CAAA;AAAA,QAClB,YAAA,EAAc,UAAA,CAAW,GAAA,CAAI,CAAC,GAAG,IAAI;AAAA,OACtC;AAAA,KACH;AAAA,EACF;AAKA,EAAA,KAAA,MAAW,CAAA,IAAK,GAAA,CAAI,QAAA,IAAY,EAAC,EAAG;AAClC,IAAA,MAAM,IAAA,GAAO,CAAA,GAAI,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAA,CAAE,KAAA,GAAQ,CAAC,CAAC,CAAA,GAAI,CAAA;AAC7D,IAAA,MAAM,KAAK,IAAA,GAAO,CAAA;AAClB,IAAA,MAAM,MAAA,GAAS,CAAA,CAAE,MAAA,KAAW,QAAA,GAAW,QAAA,GAAW,QAAA;AAClD,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,YAAA,CAAa;AAAA,QACX,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,CAAA,EAAG,EAAA;AAAA,QACH,CAAA,EAAG,CAAA;AAAA,QACH,OAAA,EAAS,CAAA,CAAE,IAAA,KAAS,MAAA,GAAS,GAAA,GAAM,EAAA;AAAA,QACnC,WAAA,EAAa,MAAA;AAAA,QACb,YAAA,EAAc,UAAA,CAAW,MAAA,EAAQ,IAAI;AAAA,OACtC;AAAA,KACH;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAIO,SAAS,gBAAgB,YAAA,EAAuC;AACrE,EAAA,OAAO,YAAA,KAAiB,SAAS,YAAA,KAAiB,OAAA;AACpD;AAIO,SAAS,qBACd,GAAA,EAC2D;AAC3D,EAAA,MAAM,MAAiE,EAAC;AACxE,EAAA,KAAA,MAAW,CAAA,IAAK,GAAA,CAAI,SAAA,IAAa,EAAC,EAAG;AACnC,IAAA,IACE,EAAE,EAAA,IACF,CAAA,CAAE,QACF,OAAO,CAAA,CAAE,KAAK,CAAA,KAAM,QAAA,IACpB,OAAO,CAAA,CAAE,KAAK,CAAA,KAAM,QAAA,IACpB,OAAO,CAAA,CAAE,IAAA,CAAK,YAAY,QAAA,EAC1B;AACA,MAAA,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,GAAI,EAAE,GAAG,CAAA,CAAE,IAAA,CAAK,CAAA,EAAG,CAAA,EAAG,EAAE,IAAA,CAAK,CAAA,EAAG,OAAA,EAAS,CAAA,CAAE,KAAK,OAAA,EAAQ;AAAA,IAClE;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT","file":"index.cjs","sourcesContent":["/**\n * @willcgage/module-schematic — the shared module operations-schematic (track-graph)\n * that the Module Repository authors and Free-Dispatcher imports.\n *\n * Topological, straightened-first: positions are 1-D inches along the module\n * (from endplate A), lanes are integer track indices (0 = primary main). This is\n * the single source of truth for both apps — the doc types, the lenient parser\n * (docs arrive as jsonb / unknown), the pure feature resolver both renderers\n * draw, the N-scale helpers, and the editor <-> doc state machine an authoring\n * UI binds to. See docs/module-schematic-format.md in the free-dispatcher repo.\n *\n * Framework-agnostic and side-effect-free so it can be unit-tested and consumed\n * from Next.js (server + client) and Electron alike.\n */\n\nexport type TrackConfig = \"single\" | \"double\";\nexport type TrackRole = \"main\" | \"siding\" | \"spur\" | \"yard\" | \"crossover\";\nexport type TurnoutKind = \"left\" | \"right\" | \"wye\";\nexport type SignalFacing = \"AtoB\" | \"BtoA\";\nexport type SignalSide = \"above\" | \"below\";\n\nexport interface SchematicEndplateTrack {\n trackId: string;\n lane: number;\n config?: TrackConfig | null;\n}\nexport interface SchematicEndplate {\n id: string; // \"A\" (West) | \"B\" (East) | \"C\"… (branch, #170)\n label?: string | null;\n tracks?: SchematicEndplateTrack[];\n /** Branch endplates (#170): where a 3rd+ endplate sits — pos inches from A,\n * on the up (north/above) or down side. Absent = axial (A at 0, B at\n * lengthInches). Renderers draw a named connector arrow (the CATS/US&S\n * off-band idiom) until branch spines land. */\n at?: { pos: number; side: \"up\" | \"down\" };\n /** Manual pose override (#175 phase 1b) — the endplate's module-local track\n * point (x, y inches) + outward-normal heading (°). Hand-entered for shapes\n * the geometry fields can't derive (wye, freeform, loop); wins over\n * deriveEndplatePoses' derivation. */\n pose?: { x: number; y: number; heading: number };\n}\nexport interface SchematicTrack {\n id: string;\n role: TrackRole;\n lane: number;\n from?: string;\n to?: string;\n fromPos?: number | null;\n toPos?: number | null;\n capacityFeet?: number | null;\n industryRef?: number | null;\n /** The module_tracks row this track is (single source of truth); null = new. */\n moduleTrackId?: number | null;\n /** Owner's track name, mirrored to module_tracks.track_name. */\n trackName?: string;\n /** Inside the balloon of a loop module (#165): pos still measures from\n * endplate A (past the throat = in the loop), lane is the ladder/arc index —\n * one record drives both the unrolled fan and a geometric render. */\n inLoop?: boolean;\n}\nexport interface SchematicTurnout {\n id: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind?: TurnoutKind;\n name?: string | null;\n address?: string | null;\n}\nexport interface SchematicSignal {\n id: string;\n pos: number;\n /** Track the signal governs; absent = the primary main (lane 0). */\n track?: string;\n facing?: SignalFacing;\n kind?: \"mast\" | \"dwarf\";\n name?: string | null;\n aspects?: string[];\n /** Which side of the track the signal sits on (#122). */\n side?: SignalSide;\n /** Turnout this control point governs; absent = standalone block signal. */\n turnout?: string;\n}\nexport interface SchematicBlock {\n id: string;\n name: string;\n tracks?: string[];\n from: number;\n to: number;\n}\n/** A grade crossing / diamond (#170): two tracks cross with no route choice —\n * a conflict node, not a turnout. A connected diamond adds normal turnouts\n * alongside. Drawn as an X between the two tracks' lanes. */\nexport interface SchematicCrossing {\n id: string;\n pos: number;\n /** The two crossing tracks, by id. */\n tracks: [string, string];\n name?: string | null;\n}\n/**\n * A control point is an interlocking: a named group of one or more signals and\n * zero or more turnouts. A passing siding has two (West/East); a lone block\n * signal is a control point with one signal and no turnouts.\n */\nexport interface SchematicControlPoint {\n id: string;\n name?: string | null;\n turnouts?: string[];\n /** Crossings this interlocking protects (#170). */\n crossings?: string[];\n signals?: SchematicSignal[];\n}\nexport interface ModuleSchematicDoc {\n version: number;\n module?: string;\n lengthInches?: number;\n /** Single-endplate turnback (balloon loop): the main enters at A, runs the\n * lead, and turns back — renderers draw a terminal bulb at the far end\n * instead of a second endplate. Also implied by a single-entry endplates\n * array (a category:\"loop\" module like Seaford). */\n loop?: boolean;\n /** Where the balloon returns (#165): \"same\" (default) turns back onto the\n * same main; \"main2\" is a directional return on a double-track main —\n * out on Main 1, back on Main 2 — drawn as a U joining the two lanes\n * (the transit terminal-loop idiom). */\n loopReturn?: \"same\" | \"main2\";\n /** Optional rendering override: \"bulb\" (abstract terminal), \"fan\" (interior\n * tracks unrolled as a ladder past the throat — the default when inLoop\n * tracks exist), \"geometric\" (drawn balloon, AL&E-style). */\n loopRender?: \"bulb\" | \"fan\" | \"geometric\";\n endplates: SchematicEndplate[];\n tracks: SchematicTrack[];\n turnouts?: SchematicTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings?: SchematicCrossing[];\n controlPoints?: SchematicControlPoint[];\n /** @deprecated pre-grouping flat signals; read for back-compat. */\n signals?: SchematicSignal[];\n}\n\n/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */\nexport function isLoopDoc(doc: ModuleSchematicDoc): boolean {\n return doc.loop === true || doc.endplates.length === 1;\n}\n\nexport const MAIN_TRACK_ID = \"main\";\n/** The second main on double-track modules — a real track entity so turnouts\n * and signals can attach to it (a spur off Main 2 must diverge from lane 1,\n * not draw a crossover from Main 1). Emitted by stateToDoc when either\n * endplate is double; legacy docs without it still parse. */\nexport const MAIN2_TRACK_ID = \"main2\";\n\n// North American N scale (1:160): 396 real inches → 5280 scale feet = one mile.\nexport const N_SCALE_RATIO = 160;\n/** Real inches on the module → scale feet of prototype track represented. */\nexport function inchesToScaleFeet(inches: number, ratio = N_SCALE_RATIO): number {\n return (inches * ratio) / 12;\n}\n/** Scale feet of prototype track → real inches on the module. */\nexport function scaleFeetToInches(feet: number, ratio = N_SCALE_RATIO): number {\n return (feet * 12) / ratio;\n}\n\n/** Parse a jsonb value into a schematic doc, or null if it isn't one. */\nexport function asModuleSchematic(x: unknown): ModuleSchematicDoc | null {\n if (!x || typeof x !== \"object\") return null;\n const d = x as Record<string, unknown>;\n if (typeof d.version !== \"number\") return null;\n if (!Array.isArray(d.endplates) || !Array.isArray(d.tracks)) return null;\n return d as unknown as ModuleSchematicDoc;\n}\n\n// ---- Editor state (a flatter shape an authoring form binds to) -------------\n\nexport interface EditorTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromPos: number;\n toPos: number;\n /** module_tracks row id (single source of truth), or null for a new track. */\n moduleTrackId: number | null;\n /** Owner's track name → module_tracks.track_name. */\n trackName: string;\n /** Inside the balloon of a loop module (#165). */\n inLoop?: boolean;\n}\n\n/** A module_tracks row as loaded for the editor. */\nexport interface ModuleTrackRow {\n id: number;\n track_name: string | null;\n capacity_scale_feet: number | null;\n}\nexport interface EditorTurnout {\n id: string;\n name: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind: TurnoutKind;\n}\nexport interface EditorCpSignal {\n id: string;\n pos: number;\n track: string;\n facing: SignalFacing;\n side: SignalSide;\n}\nexport interface EditorControlPoint {\n id: string;\n name: string;\n turnouts: string[]; // turnout ids grouped under this control point\n /** Crossing ids this interlocking protects (#170). */\n crossings?: string[];\n signals: EditorCpSignal[];\n}\nexport interface EditorCrossing {\n id: string;\n name: string;\n pos: number;\n trackA: string;\n trackB: string;\n}\n/** A 3rd+ endplate — a branch/junction connection off the module (#170).\n * A module may have several (e.g. a set carrying a second railroad through:\n * MoPac enters at one branch endplate and leaves at another). */\nexport interface EditorBranch {\n label: string;\n pos: number;\n side: \"up\" | \"down\";\n config: TrackConfig;\n}\n/** Endplate B on a loop module: a standard endplate makes the balloon an\n * INTERCHANGE (a second route connects at the loop, e.g. Seaford); \"none\"\n * makes it a pure turnback. Non-loop modules always have a real B. */\nexport type EndplateBConfig = TrackConfig | \"none\";\n\nexport interface EditorState {\n lengthInches: number;\n /** Balloon loop: the main runs the lead and turns back; positions past the\n * throat are inside the balloon. Endplate B stays independently available —\n * present = interchange loop, \"none\" = pure turnback. */\n loop: boolean;\n /** Where the balloon returns: same main, or Main 2 (directional return on a\n * double-track main — drawn as a U joining the two lanes). */\n loopReturn: \"same\" | \"main2\";\n configA: TrackConfig;\n configB: EndplateBConfig;\n extraTracks: EditorTrack[]; // sidings/spurs/…; the main track is implicit\n turnouts: EditorTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings: EditorCrossing[];\n /** Branch endplates C, D, … — junction connections (#170); empty = through\n * module. Emitted in order as endplates \"C\", \"D\", \"E\"… */\n branches: EditorBranch[];\n /** Manual endplate pose overrides by endplate id (#175 phase 1b). */\n poseOverrides: Record<string, { x: number; y: number; heading: number }>;\n controlPoints: EditorControlPoint[];\n}\n\n/** Build the empty editor state for a module of the given length. */\nexport function emptyEditorState(lengthInches: number): EditorState {\n return {\n lengthInches: lengthInches > 0 ? lengthInches : 24,\n loop: false,\n loopReturn: \"same\",\n configA: \"single\",\n configB: \"single\",\n extraTracks: [],\n turnouts: [],\n crossings: [],\n branches: [],\n poseOverrides: {},\n controlPoints: [],\n };\n}\n\n/** Main 2's doc track for a non-loop double module. Full length when both\n * endplates are double; on a transition module (one single, one double) it\n * runs between the mainline transition turnout (the one diverging to main2)\n * and the double end. */\n/**\n * A single↔double transition turnout connects Main 1 and Main 2 — where the\n * second main begins/ends. It may be authored either way round: on Main 1\n * diverging to Main 2, or on Main 2 diverging to Main 1 (both describe the same\n * junction). Recognise both so editing the direction doesn't drop the transition.\n */\nexport function isTransitionTurnout(t: {\n onTrack?: string;\n divergeTrack?: string;\n}): boolean {\n return (\n (t.onTrack === MAIN_TRACK_ID && t.divergeTrack === MAIN2_TRACK_ID) ||\n (t.onTrack === MAIN2_TRACK_ID && t.divergeTrack === MAIN_TRACK_ID)\n );\n}\n\nfunction main2Track(state: EditorState): SchematicTrack {\n const bothDouble = state.configA === \"double\" && state.configB === \"double\";\n const sw = state.turnouts.find(isTransitionTurnout);\n // Main 2 runs partial only when IT is the branch that ends (turnout diverges\n // TO Main 2). If the turnout sits ON Main 2 (Main 2 is the surviving through\n // main, #FMN-0043), Main 2 runs full and Main 1 is the one that ends.\n if (bothDouble || !sw || sw.divergeTrack !== MAIN2_TRACK_ID) {\n return { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, from: \"A\", to: \"B\" };\n }\n return state.configA === \"double\"\n ? // Double at A: Main 2 runs from A and ends at the turnout.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: 0, toPos: sw.pos }\n : // Double at B: Main 2 begins at the turnout and runs to B.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: sw.pos, toPos: state.lengthInches };\n}\n\n/**\n * Build the single↔double transition as one unit (like buildPassingSiding):\n * the mainline turnout where Main 2 begins/ends, grouped in a control point —\n * an \"End of Double Track\" is a classic CTC interlocking — with signals both\n * directions. Returns items to merge into the editor state.\n */\nexport function buildTransition(state: EditorState): {\n turnout: EditorTurnout;\n controlPoint: EditorControlPoint;\n} | null {\n const aDouble = state.configA === \"double\";\n const bDouble = state.configB === \"double\";\n if (state.loop || aDouble === bDouble) return null; // not a transition module\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.25));\n // The turnout sits toward the single end so the double track carries most\n // of the module; owner adjusts the position afterwards.\n const pos = aDouble ? len - inset : inset;\n\n const swId = nextId(\"sw\", state.turnouts.map((t) => t.id));\n const turnout: EditorTurnout = {\n id: swId,\n name: \"End of Double Track\",\n pos,\n // The turnout sits ON the ending main (Main 2, the upper track) and diverges\n // down to the continuous Main 1 — the modeller's view of the junction. Left\n // hand when the double end is west (Main 2 comes down going east), right\n // when it's east (mirror).\n onTrack: MAIN2_TRACK_ID,\n divergeTrack: MAIN_TRACK_ID,\n kind: aDouble ? \"left\" : \"right\",\n };\n\n const cpId = nextId(\"cp\", state.controlPoints.map((c) => c.id));\n const sig = (facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoint: EditorControlPoint = {\n id: cpId,\n name: \"End of Double Track\",\n turnouts: [swId],\n signals: [sig(\"AtoB\"), sig(\"BtoA\")],\n };\n return { turnout, controlPoint };\n}\n\n/** Attach manual pose overrides (#175 phase 1b) to endplates by id. */\nfunction withPoses(\n endplates: SchematicEndplate[],\n overrides: Record<string, { x: number; y: number; heading: number }>,\n): SchematicEndplate[] {\n return endplates.map((e) =>\n overrides[e.id] ? { ...e, pose: overrides[e.id] } : e,\n );\n}\n\n/** Assemble a spec-conformant doc from the editor state. */\nexport function stateToDoc(\n state: EditorState,\n recordNumber: string,\n): ModuleSchematicDoc {\n return {\n version: 1,\n module: recordNumber,\n lengthInches: state.lengthInches,\n ...(state.loop ? { loop: true } : {}),\n ...(state.loop && state.loopReturn === \"main2\" ? { loopReturn: \"main2\" as const } : {}),\n endplates: withPoses(\n [\n ...(state.loop\n ? // Balloon loop: A is the entry. A standard endplate B on the balloon\n // makes it an INTERCHANGE (second route connects at the loop, e.g.\n // Seaford); configB \"none\" makes it a pure turnback.\n [\n { id: \"A\", label: \"Entry\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n ...(state.configB !== \"none\"\n ? [{ id: \"B\", label: \"Interchange\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }]\n : []),\n ]\n : [\n { id: \"A\", label: \"West\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n // Non-loop modules always have a real B (\"none\" never applies).\n {\n id: \"B\",\n label: \"East\",\n tracks: [\n {\n trackId: MAIN_TRACK_ID,\n lane: 0,\n config: state.configB === \"none\" ? \"single\" : state.configB,\n },\n ],\n },\n ]),\n // Branch endplates C, D, … — junction connections at pos, off one side\n // (#170). A set can carry several (e.g. a second railroad through).\n ...state.branches.map((b, i) => ({\n id: String.fromCharCode(67 + i), // C, D, E…\n label: b.label || `Branch ${i + 1}`,\n tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],\n at: { pos: b.pos, side: b.side },\n })),\n ],\n state.poseOverrides,\n ),\n tracks: [\n state.loop\n ? // The main runs the lead from A and turns back at the balloon.\n { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, fromPos: 0, toPos: state.lengthInches }\n : { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, from: \"A\", to: \"B\" },\n // Double track: Main 2 is a real entity so turnouts/signals can attach.\n // On a loop it exists only for a Main 2 directional return (the U joins\n // the two lanes at the balloon); a same-main loop's parallel lead legs\n // are ONE main. On a TRANSITION module (one endplate single, the other\n // double) Main 2 only runs from the mainline turnout to the double end —\n // the turnout that diverges to main2 is the single source of truth for\n // where the transition sits (fd#175 / FMN-0038).\n ...(!state.loop && (state.configA === \"double\" || state.configB === \"double\")\n ? [main2Track(state)]\n : []),\n ...(state.loop && state.loopReturn === \"main2\"\n ? [{ id: MAIN2_TRACK_ID, role: \"main\" as const, lane: 1, fromPos: 0, toPos: state.lengthInches }]\n : []),\n ...state.extraTracks.map((t) => ({\n id: t.id,\n role: t.role,\n lane: t.lane,\n fromPos: t.fromPos,\n toPos: t.toPos,\n moduleTrackId: t.moduleTrackId,\n trackName: t.trackName || undefined,\n capacityFeet: Math.round(inchesToScaleFeet(Math.abs(t.toPos - t.fromPos))),\n ...(state.loop && t.inLoop ? { inLoop: true } : {}),\n })),\n ],\n turnouts: state.turnouts.map((t) => ({\n id: t.id,\n pos: t.pos,\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: t.kind,\n name: t.name || undefined,\n })),\n ...(state.crossings.length > 0\n ? {\n crossings: state.crossings.map((x) => ({\n id: x.id,\n pos: x.pos,\n tracks: [x.trackA, x.trackB] as [string, string],\n name: x.name || undefined,\n })),\n }\n : {}),\n controlPoints: state.controlPoints.map((c) => ({\n id: c.id,\n name: c.name,\n turnouts: c.turnouts,\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: c.signals.map((s) => ({\n id: s.id,\n pos: s.pos,\n track: s.track,\n facing: s.facing,\n kind: \"mast\" as const,\n side: s.side,\n })),\n })),\n };\n}\n\n/**\n * Derive editor state from the doc and the module's Track section rows. Tracks\n * are the single source of truth for name/capacity (module_tracks), while the\n * schematic doc adds geometry (lane, positions). We merge: doc tracks first\n * (they carry geometry + their moduleTrackId link), then any module_tracks not\n * yet positioned in the schematic.\n */\nexport function docToState(\n doc: unknown,\n fallbackLength: number,\n moduleTracks: ModuleTrackRow[] = [],\n): EditorState {\n const base = emptyEditorState(fallbackLength);\n const d =\n doc && typeof doc === \"object\" ? (doc as ModuleSchematicDoc) : null;\n const hasDoc = !!d && typeof d.lengthInches === \"number\" && Array.isArray(d.tracks);\n // The module's length is authoritative (the mainline is the module). If the\n // saved doc used a different length, rescale its feature positions to fit so\n // the mainline always reads as the module's true length.\n const len = fallbackLength > 0 ? fallbackLength : hasDoc ? d!.lengthInches! : 24;\n const docLen = hasDoc && d!.lengthInches! > 0 ? d!.lengthInches! : len;\n const scale = docLen > 0 ? len / docLen : 1;\n const sc = (p: number) => Math.round(p * scale);\n\n const nameOf = (id: number | null | undefined): string => {\n const mt = id != null ? moduleTracks.find((m) => m.id === id) : undefined;\n return mt?.track_name ?? \"\";\n };\n\n const extraTracks: EditorTrack[] = [];\n const usedMt = new Set<number>();\n if (hasDoc) {\n for (const t of d!.tracks) {\n if (t.role === \"main\") continue;\n const moduleTrackId = t.moduleTrackId ?? null;\n if (moduleTrackId != null) usedMt.add(moduleTrackId);\n extraTracks.push({\n id: t.id,\n role: (t.role as TrackRole) ?? \"siding\",\n lane: t.lane ?? 1,\n fromPos: sc(t.fromPos ?? 0),\n toPos: t.toPos != null ? sc(t.toPos) : len,\n moduleTrackId,\n trackName: t.trackName ?? nameOf(moduleTrackId),\n ...(t.inLoop ? { inLoop: true } : {}),\n });\n }\n }\n // Link pre-migration doc tracks (no moduleTrackId yet) to unused module_tracks\n // by order — keeping the doc track's id so turnout/signal references stay\n // valid. Only after that do leftover module_tracks become new tracks.\n const unused = moduleTracks.filter((mt) => !usedMt.has(mt.id));\n let ui = 0;\n for (const et of extraTracks) {\n if (et.moduleTrackId == null && ui < unused.length) {\n const mt = unused[ui++];\n et.moduleTrackId = mt.id;\n if (!et.trackName) et.trackName = mt.track_name ?? \"\";\n usedMt.add(mt.id);\n }\n }\n let lane = Math.max(0, ...extraTracks.map((t) => t.lane));\n for (const mt of moduleTracks) {\n if (usedMt.has(mt.id)) continue;\n lane += 1;\n extraTracks.push({\n id: `mt${mt.id}`,\n role: \"siding\",\n lane,\n fromPos: Math.round(len * 0.2),\n toPos: Math.round(len * 0.8),\n moduleTrackId: mt.id,\n trackName: mt.track_name ?? \"\",\n });\n }\n\n if (!hasDoc) return { ...base, lengthInches: len, extraTracks };\n\n const configOf = (id: string): TrackConfig => {\n const ep = (d!.endplates ?? []).find((e) => e.id === id);\n return ep?.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\";\n };\n const loop = isLoopDoc(d!);\n const hasB = (d!.endplates ?? []).some((e) => e.id === \"B\");\n // Branch endplates C, D, … (junction connections, #170).\n const branchEps = (d!.endplates ?? []).filter(\n (e) => e.id !== \"A\" && e.id !== \"B\" && e.at,\n );\n const poseOverrides = poseOverridesFromDoc(d!);\n return {\n lengthInches: len,\n loop,\n loopReturn: loop && d!.loopReturn === \"main2\" ? \"main2\" : \"same\",\n configA: configOf(\"A\"),\n // On a loop, a missing B means pure turnback; present = interchange loop.\n configB: loop && !hasB ? \"none\" : configOf(\"B\"),\n branches: branchEps.map((ep) => ({\n label: ep.label ?? \"Branch\",\n pos: sc(ep.at!.pos),\n side: ep.at!.side === \"down\" ? \"down\" : \"up\",\n config: ep.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\",\n })),\n poseOverrides,\n crossings: (d!.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n pos: sc(x.pos),\n trackA: x.tracks?.[0] ?? MAIN_TRACK_ID,\n trackB: x.tracks?.[1] ?? MAIN_TRACK_ID,\n })),\n extraTracks,\n turnouts: (d!.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n pos: sc(t.pos),\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: (t.kind as TurnoutKind) ?? \"right\",\n })),\n controlPoints: readControlPoints(d!, sc),\n };\n}\n\n/** Control points from a doc, migrating pre-grouping flat signals into groups. */\nfunction readControlPoints(\n d: ModuleSchematicDoc,\n sc: (p: number) => number = (p) => p,\n): EditorControlPoint[] {\n if (Array.isArray(d.controlPoints)) {\n return d.controlPoints.map((c) => ({\n id: c.id,\n name: c.name ?? \"\",\n turnouts: c.turnouts ?? [],\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: (c.signals ?? []).map((s) => ({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n })),\n }));\n }\n // Back-compat: group old flat signals by their turnout (or standalone).\n const groups = new Map<string, EditorControlPoint>();\n let n = 0;\n for (const s of d.signals ?? []) {\n const key = s.turnout || `blk-${s.id}`;\n let cp = groups.get(key);\n if (!cp) {\n cp = { id: `cp${++n}`, name: s.name ?? \"\", turnouts: s.turnout ? [s.turnout] : [], signals: [] };\n groups.set(key, cp);\n }\n cp.signals.push({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n });\n }\n return [...groups.values()];\n}\n\n/** Find an unused `${prefix}${n}` id given the ones already present. */\nexport function nextId(prefix: string, existing: string[]): string {\n let n = 1;\n while (existing.includes(`${prefix}${n}`)) n += 1;\n return `${prefix}${n}`;\n}\n\n/**\n * Build a passing siding as one unit: the siding track, a switch at each end,\n * and control-point signals for both directions at each end (prototype Station\n * Entering Signal). Returns the new items to merge into the editor state.\n */\nexport function buildPassingSiding(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n controlPoints: EditorControlPoint[];\n} {\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.08));\n const fromPos = inset;\n const toPos = Math.max(fromPos + 1, len - inset);\n // First free lane above the main(s): lane 1 is Main 2 on double modules.\n const baseLane =\n state.configA === \"double\" || state.configB === \"double\" ? 2 : 1;\n const lane = Math.max(baseLane, ...state.extraTracks.map((t) => t.lane + 1));\n\n const trackIds = [MAIN_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const sidId = nextId(\"sid\", trackIds);\n const track: EditorTrack = {\n id: sidId,\n role: \"siding\",\n lane,\n fromPos,\n toPos,\n moduleTrackId: null,\n trackName: \"Passing siding\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const swW = nextId(\"sw\", swIds);\n const swE = nextId(\"sw\", [...swIds, swW]);\n // A siding above the main throws LEFT at its west turnout (body runs east) and\n // RIGHT at its east turnout (body runs west) — both resolve to the same side,\n // so `kind` and the drawn lane agree (divergeSideForHand / #bug1).\n const turnouts: EditorTurnout[] = [\n { id: swW, name: \"West Siding\", pos: fromPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"left\" },\n { id: swE, name: \"East Siding\", pos: toPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"right\" },\n ];\n\n // One control point at each end, each grouping its switch and both-direction\n // signals on the main (prototype Station Entering Signal).\n const cpIds = state.controlPoints.map((c) => c.id);\n const cpW = nextId(\"cp\", cpIds);\n const cpE = nextId(\"cp\", [...cpIds, cpW]);\n const sig = (cpId: string, pos: number, facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n // opposite directions on opposite sides so they never overlap\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoints: EditorControlPoint[] = [\n { id: cpW, name: \"West Siding\", turnouts: [swW], signals: [sig(cpW, fromPos, \"AtoB\"), sig(cpW, fromPos, \"BtoA\")] },\n { id: cpE, name: \"East Siding\", turnouts: [swE], signals: [sig(cpE, toPos, \"AtoB\"), sig(cpE, toPos, \"BtoA\")] },\n ];\n\n return { track, turnouts, controlPoints };\n}\n\n/**\n * Build a crossover as one unit: a short connector track between Main 1 and\n * Main 2, with a turnout on each main. The two turnouts sit on different lanes,\n * so the feature resolver draws it as a diagonal (not a lane-paralleling siding)\n * — this is what connects the two dots (#bug2). Needs a double-track module.\n */\nexport function buildCrossover(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n} | null {\n const hasSecond = state.configA === \"double\" || state.configB === \"double\";\n if (state.loop || !hasSecond) return null;\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const mid = Math.round(len / 2);\n const gap = Math.max(3, Math.round(len * 0.04));\n const w = Math.round(mid - gap / 2);\n const e = Math.round(mid + gap / 2);\n\n const trackIds = [MAIN_TRACK_ID, MAIN2_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const xoId = nextId(\"xo\", trackIds);\n const track: EditorTrack = {\n id: xoId,\n role: \"crossover\",\n lane: 1,\n fromPos: w,\n toPos: e,\n moduleTrackId: null,\n trackName: \"Crossover\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const sw1 = nextId(\"sw\", swIds);\n const sw2 = nextId(\"sw\", [...swIds, sw1]);\n const turnouts: EditorTurnout[] = [\n { id: sw1, name: \"Crossover\", pos: w, onTrack: MAIN_TRACK_ID, divergeTrack: xoId, kind: \"left\" },\n { id: sw2, name: \"Crossover\", pos: e, onTrack: MAIN2_TRACK_ID, divergeTrack: xoId, kind: \"right\" },\n ];\n\n return { track, turnouts };\n}\n\n// ---- Pure feature resolver (both renderers draw these) --------------------\n\nexport interface DrawTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromFrac: number;\n toFrac: number;\n capacityFeet: number | null;\n /** Lane of the main this track diverges from (via its turnout) — the origin\n * of the diverge diagonal. A spur off Main 2 starts at lane 1, not lane 0;\n * without this, renderers draw what looks like a crossover. */\n divergesFromLane: number;\n /** The end that meets the main at its turnout (throat) and the far stub end,\n * as fractions of length. Direction-preserving: unlike fromFrac/toFrac (always\n * sorted West→East), these keep WHICH end joins, so an east-facing spur draws\n * its throat on the east. A siding meets the main at both ends — there\n * throat=fromFrac, stub=toFrac (the fields are only meaningful for spurs). */\n throatFrac: number;\n stubFrac: number;\n /** Inside the balloon of a loop module (#165). */\n inLoop: boolean;\n}\nexport interface DrawTurnout {\n id: string;\n name: string;\n posFrac: number;\n onLane: number;\n divergeLane: number;\n}\nexport interface DrawSignal {\n id: string;\n name: string;\n posFrac: number;\n lane: number;\n facing: SignalFacing;\n side: SignalSide;\n /** Owning control point's id, when the signal came from a CP group — lets a\n * renderer join the drawn signal back to interlocking-level state (aspects). */\n cp?: string;\n /** 0-based rank among signals that would otherwise land on the exact same\n * spot (same lane + side + position). Renderers offset each further from the\n * track by `stack` so a control point's signals never overlap. */\n stack: number;\n}\n/** A grade crossing / diamond — draw an X spanning the two lanes (#170). */\nexport interface DrawCrossing {\n id: string;\n name: string;\n posFrac: number;\n laneA: number;\n laneB: number;\n}\n/** A crossover — a connector track joining two parallel mains through a turnout\n * on each. Draw a straight diagonal between the two turnout points (unlike a\n * siding, which parallels a lane between its turnouts). Detected structurally:\n * a track whose turnouts sit on two different lanes. */\nexport interface DrawCrossover {\n id: string;\n name: string;\n fromPosFrac: number;\n fromLane: number;\n toPosFrac: number;\n toLane: number;\n}\n/** A branch endplate — draw a connector stub + arrow + label off the given\n * side (the CATS/US&S off-band idiom, #170). */\nexport interface BranchConnector {\n id: string;\n label: string;\n posFrac: number;\n side: \"up\" | \"down\";\n}\nexport interface ModuleFeatures {\n /** Whether either endplate declares a double-track main. */\n doubleMain: boolean;\n /** Balloon loop — draw a terminal bulb at the far end. */\n loop: boolean;\n /** Loop with a standard endplate B on the balloon: an interchange — a\n * second route connects at the loop (draw an endplate branch off the bulb). */\n loopInterchange: boolean;\n /** Where the balloon returns: \"main2\" = directional return on a double-track\n * main — draw a U joining lanes 0 and 1 instead of the bulb (#165). */\n loopReturn: \"same\" | \"main2\";\n /** Rendering override from the doc; renderers may ignore modes they don't\n * implement yet (\"geometric\"). */\n loopRender: \"bulb\" | \"fan\" | \"geometric\" | null;\n /** Non-main tracks (sidings/spurs/yard/crossover). */\n extraTracks: DrawTrack[];\n turnouts: DrawTurnout[];\n signals: DrawSignal[];\n /** Grade crossings / diamonds (#170). */\n crossings: DrawCrossing[];\n /** Crossovers — connector tracks between two mains (drawn as a diagonal). */\n crossovers: DrawCrossover[];\n /** Branch endplates — junction connectors off the module (#170). */\n branchConnectors: BranchConnector[];\n /** Main 2's extent when it doesn't run the full module — a single↔double\n * transition (Main 2 starts/ends at the mainline turnout). Null = full\n * length (or no Main 2). Renderers draw the partial line + its diverge. */\n main2Extent: { fromFrac: number; toFrac: number } | null;\n /** Single↔double transition, fully described (#FMN-0043). The `through` main\n * runs the whole module; the `branch` main exists only on the double side and\n * merges at `atFrac`. EITHER main can be through/branch — the surviving single\n * track follows the transition turnout's onTrack, so the drawn side isn't\n * hard-wired to Main 1. Null when not a transition. */\n transition: {\n throughLane: number;\n branchLane: number;\n atFrac: number;\n doubleSide: \"west\" | \"east\";\n } | null;\n /** Lane extents across every feature (mains included; negative = outside\n * Main 1). Renderers size their vertical space from these. */\n laneMin: number;\n laneMax: number;\n}\n\n/**\n * Which side of the main a diverging track draws on, given the turnout's HAND\n * and the direction the track body runs along the main from the turnout\n * (`stubDir`: +1 = the body extends east / toward B, −1 = west / toward A).\n * Returns +1 (above the main) or −1 (below); 0 for a wye or unset hand (keep the\n * authored side). A left-hand turnout throws its route to the same side its body\n * runs (facing the frog, left is the inside of a body running that way); a\n * right-hand throws to the opposite side. `kind` is the source of truth for the\n * drawn side (#bug1) — the stored lane's sign is reconciled to match it.\n */\nexport function divergeSideForHand(\n kind: TurnoutKind | undefined,\n stubDir: number,\n): -1 | 0 | 1 {\n if (kind !== \"left\" && kind !== \"right\") return 0; // wye / unset → no change\n const s = stubDir >= 0 ? 1 : -1;\n return kind === \"left\" ? (s as 1 | -1) : ((-s) as 1 | -1);\n}\n\n/**\n * Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a\n * fraction of the module length; endplate A = 0, B = length; turnouts sit at\n * their pos. Tracks may carry explicit fromPos/toPos (overriding node lookup).\n * To-scale: a feature renders at its true position, clamped only to the\n * module's own extent — so signals near an end read at their real spot, not\n * bunched at an inset (#122).\n */\nexport function moduleFeatures(doc: ModuleSchematicDoc): ModuleFeatures {\n const len =\n doc.lengthInches && doc.lengthInches > 0\n ? doc.lengthInches\n : Math.max(\n 1,\n ...doc.tracks.map((t) => Math.max(t.fromPos ?? 0, t.toPos ?? 0)),\n ...(doc.turnouts ?? []).map((t) => t.pos),\n );\n const clampFrac = (p: number) => Math.min(1, Math.max(0, p / len));\n\n const trackLane = new Map<string, number>();\n for (const t of doc.tracks) trackLane.set(t.id, t.lane);\n\n // Endplate positions: first endplate = West (0), the rest = East (len).\n const endplatePos = new Map<string, number>();\n doc.endplates.forEach((e, i) => endplatePos.set(e.id, i === 0 ? 0 : len));\n const turnoutPos = new Map<string, number>();\n for (const t of doc.turnouts ?? []) turnoutPos.set(t.id, t.pos);\n const posOf = (nodeId?: string): number | null => {\n if (nodeId == null) return null;\n if (endplatePos.has(nodeId)) return endplatePos.get(nodeId)!;\n if (turnoutPos.has(nodeId)) return turnoutPos.get(nodeId)!;\n return null;\n };\n\n const doubleMain = doc.endplates.some((e) =>\n e.tracks?.some((t) => t.config === \"double\"),\n );\n\n // The lane a track diverges from = the main its turnout sits on.\n const divergeOrigin = (trackId: string): number => {\n const sw = (doc.turnouts ?? []).find((t) => t.divergeTrack === trackId);\n return sw ? (trackLane.get(sw.onTrack) ?? 0) : 0;\n };\n\n // Turnouts grouped by the track they diverge onto (a track's throat/hand).\n const turnoutsByTrack = new Map<string, SchematicTurnout[]>();\n for (const sw of doc.turnouts ?? []) {\n const arr = turnoutsByTrack.get(sw.divergeTrack) ?? [];\n arr.push(sw);\n turnoutsByTrack.set(sw.divergeTrack, arr);\n }\n // A crossover is a connector whose turnouts sit on two different lanes (one on\n // each main) — drawn as a diagonal between them, never as a lane-paralleling\n // siding. A passing siding's two turnouts share one lane, so it isn't one.\n const isCrossover = (trackId: string): boolean => {\n const sws = turnoutsByTrack.get(trackId) ?? [];\n if (sws.length < 2) return false;\n return new Set(sws.map((s) => trackLane.get(s.onTrack) ?? 0)).size >= 2;\n };\n // The in-inches extent of a track (explicit fromPos/toPos or node lookup).\n const extentOf = (t: SchematicTrack): [number, number] | null => {\n const from = t.fromPos ?? posOf(t.from);\n const to = t.toPos ?? posOf(t.to);\n return from == null || to == null ? null : [from, to];\n };\n\n // Reconcile each diverging track's drawn SIDE from its turnout's hand — `kind`\n // is the source of truth (#bug1) — resolved TOPOLOGICALLY. The hand only picks\n // a side for a turnout sitting on the main CENTERLINE (lane 0); a track\n // diverging off any other track (Main 2, or a ladder rung) stays on its\n // PARENT's side and stacks outward — a ladder turnout's hand is relative to\n // its parent, not the main, so it must never flip a rung across the main. The\n // stored lane's magnitude (stacking slot) is always kept.\n const trackById = new Map(doc.tracks.map((t) => [t.id, t]));\n const resolvedLanes = new Map<string, number>();\n const resolving = new Set<string>();\n const resolveLane = (id: string): number => {\n const trk = trackById.get(id);\n if (!trk) return 0;\n if (trk.role === \"main\") return trk.lane; // mains are fixed (0, 1, …)\n if (resolvedLanes.has(id)) return resolvedLanes.get(id)!;\n if (resolving.has(id)) return trk.lane; // cycle guard\n resolving.add(id);\n let lane = trk.lane;\n const sw = turnoutsByTrack.get(id)?.[0];\n const ext = extentOf(trk);\n if (sw && (sw.kind === \"left\" || sw.kind === \"right\") && !isCrossover(id) && ext) {\n const parentLane = resolveLane(sw.onTrack);\n let sign: number;\n if (parentLane === 0) {\n // On the main centerline — the hand chooses above/below.\n const [from, to] = ext;\n const far = Math.abs(to - sw.pos) >= Math.abs(from - sw.pos) ? to : from;\n const s = divergeSideForHand(sw.kind, far - sw.pos);\n sign = s !== 0 ? s : Math.sign(trk.lane) || 1;\n } else {\n // Off Main 2 / a ladder rung — follow the parent's side.\n sign = Math.sign(parentLane) || 1;\n }\n lane = sign * Math.abs(trk.lane);\n }\n resolving.delete(id);\n resolvedLanes.set(id, lane);\n return lane;\n };\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue;\n trackLane.set(t.id, resolveLane(t.id));\n }\n\n const extraTracks: DrawTrack[] = [];\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue; // the spine draws mains\n if (isCrossover(t.id)) continue; // crossovers draw as diagonals, below\n const ext = extentOf(t);\n if (!ext) continue; // can't place it\n const [from, to] = ext;\n // Throat = the end nearest this track's turnout (the join to the main);\n // with no turnout, keep entry order (from = throat) so the author's chosen\n // joining end is honoured. This is what makes an east-facing spur draw its\n // throat on the east instead of always west (#bug3).\n const sw = turnoutsByTrack.get(t.id)?.[0];\n const throatAtTo = sw != null && Math.abs(to - sw.pos) < Math.abs(from - sw.pos);\n const throat = throatAtTo ? to : from;\n const stub = throatAtTo ? from : to;\n extraTracks.push({\n id: t.id,\n role: t.role,\n lane: trackLane.get(t.id) ?? t.lane,\n fromFrac: clampFrac(Math.min(from, to)),\n toFrac: clampFrac(Math.max(from, to)),\n throatFrac: clampFrac(throat),\n stubFrac: clampFrac(stub),\n capacityFeet: t.capacityFeet ?? null,\n divergesFromLane: divergeOrigin(t.id),\n inLoop: t.inLoop === true,\n });\n }\n\n // Crossovers — a diagonal between two parallel mains. Two shapes are drawn:\n const crossovers: DrawCrossover[] = [];\n // (1) a dedicated connector track (buildCrossover) with a turnout on each main.\n for (const t of doc.tracks) {\n if (!isCrossover(t.id)) continue;\n const [s1, s2] = turnoutsByTrack.get(t.id)!;\n crossovers.push({\n id: t.id,\n name: t.trackName ?? \"\",\n fromPosFrac: clampFrac(s1.pos),\n fromLane: trackLane.get(s1.onTrack) ?? 0,\n toPosFrac: clampFrac(s2.pos),\n toLane: trackLane.get(s2.onTrack) ?? 1,\n });\n }\n // (2) modelled as two turnouts each diverging onto the OTHER main (no connector\n // track). Pair a leg with its return leg and connect the two turnout points.\n // A single↔double TRANSITION turnout also connects the two mains but isn't a\n // crossover (Main 2 ends there) — exclude it, and require a matching return\n // leg so a lone main-to-main turnout is never drawn as a crossover.\n const isMainId = (id?: string) => !!id && trackById.get(id)?.role === \"main\";\n const epDouble = (id: string) =>\n (doc.endplates.find((e) => e.id === id)?.tracks ?? []).some(\n (t) => t.config === \"double\",\n );\n const aDbl = epDouble(\"A\");\n const bDbl = epDouble(\"B\");\n const transitionSw =\n aDbl !== bDbl ? (doc.turnouts ?? []).find(isTransitionTurnout) : undefined;\n const m2m = (doc.turnouts ?? []).filter(\n (sw) =>\n sw !== transitionSw &&\n isMainId(sw.onTrack) &&\n isMainId(sw.divergeTrack) &&\n (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1),\n );\n const usedLegs = new Set<string>();\n for (const t1 of m2m) {\n if (usedLegs.has(t1.id)) continue;\n const t2 = m2m.find(\n (x) =>\n !usedLegs.has(x.id) &&\n x.id !== t1.id &&\n x.onTrack === t1.divergeTrack &&\n x.divergeTrack === t1.onTrack,\n );\n if (!t2) continue; // lone leg → a transition/junction, not a crossover\n usedLegs.add(t1.id);\n usedLegs.add(t2.id);\n crossovers.push({\n id: `${t1.id}-${t2.id}`,\n name: t1.name ?? t2.name ?? \"\",\n fromPosFrac: clampFrac(t1.pos),\n fromLane: trackLane.get(t1.onTrack) ?? 0,\n toPosFrac: clampFrac(t2.pos),\n toLane: trackLane.get(t2.onTrack) ?? 1,\n });\n }\n\n const turnouts: DrawTurnout[] = (doc.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n posFrac: clampFrac(t.pos),\n onLane: trackLane.get(t.onTrack) ?? 0,\n divergeLane: trackLane.get(t.divergeTrack) ?? 1,\n }));\n\n const drawSignal = (\n s: SchematicSignal,\n name: string,\n cp?: string,\n ): DrawSignal => ({\n id: s.id,\n name,\n posFrac: clampFrac(s.pos),\n lane: s.track ? (trackLane.get(s.track) ?? 0) : 0,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: s.side === \"below\" ? \"below\" : \"above\",\n ...(cp ? { cp } : {}),\n stack: 0,\n });\n // Signals come from control-point groups; fall back to pre-grouping flat\n // signals for docs authored before the model changed.\n const signals: DrawSignal[] = Array.isArray(doc.controlPoints)\n ? doc.controlPoints.flatMap((c) =>\n (c.signals ?? []).map((s) => drawSignal(s, c.name ?? \"\", c.id)),\n )\n : (doc.signals ?? []).map((s) => drawSignal(s, s.name ?? \"\"));\n // De-collide: signals landing on the exact same lane+side+position get a\n // rising stack rank so a renderer can fan them out (a control point often\n // carries several signals at one interlocking).\n const stackCount = new Map<string, number>();\n for (const s of signals) {\n const key = `${s.lane}|${s.side}|${Math.round(s.posFrac * 1000)}`;\n const n = stackCount.get(key) ?? 0;\n s.stack = n;\n stackCount.set(key, n + 1);\n }\n\n const crossings: DrawCrossing[] = (doc.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n posFrac: clampFrac(x.pos),\n laneA: trackLane.get(x.tracks?.[0] ?? \"\") ?? 0,\n laneB: trackLane.get(x.tracks?.[1] ?? \"\") ?? 1,\n }));\n\n // Branch endplates (any beyond A/B with a placement) → connector arrows.\n const branchConnectors: BranchConnector[] = doc.endplates\n .filter((e) => e.id !== \"A\" && e.id !== \"B\" && e.at)\n .map((e) => ({\n id: e.id,\n label: e.label ?? e.id,\n posFrac: clampFrac(e.at!.pos),\n side: e.at!.side === \"down\" ? \"down\" : \"up\",\n }));\n\n const allLanes = [\n 0,\n doubleMain ? 1 : 0,\n ...extraTracks.map((t) => t.lane),\n ...signals.map((s) => s.lane),\n ...crossings.flatMap((x) => [x.laneA, x.laneB]),\n ...crossovers.flatMap((x) => [x.fromLane, x.toLane]),\n ];\n const loop = isLoopDoc(doc);\n // A positioned Main 2 = a transition module (partial second main).\n const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);\n const main2Positioned =\n !!main2 && (main2.fromPos != null || main2.toPos != null) && !loop;\n let main2Extent: { fromFrac: number; toFrac: number } | null = null;\n if (main2Positioned) {\n main2Extent = {\n fromFrac: clampFrac(main2!.fromPos ?? 0),\n toFrac: clampFrac(main2!.toPos ?? len),\n };\n } else if (\n main2 &&\n transitionSw &&\n transitionSw.divergeTrack === MAIN2_TRACK_ID &&\n !loop\n ) {\n // Main 2 is the branch but stored full-length — derive its extent from the\n // junction. (When Main 2 is the through main, it stays full: extent null.)\n main2Extent = aDbl\n ? { fromFrac: 0, toFrac: clampFrac(transitionSw.pos) } // double at west\n : { fromFrac: clampFrac(transitionSw.pos), toFrac: 1 }; // double at east\n }\n // Full transition descriptor: the surviving (through) main follows the\n // turnout's onTrack, so either main can be the one that ends (#FMN-0043).\n const transition =\n transitionSw && aDbl !== bDbl && !loop\n ? {\n throughLane: trackLane.get(transitionSw.onTrack) ?? 0,\n branchLane: trackLane.get(transitionSw.divergeTrack) ?? 1,\n atFrac: clampFrac(transitionSw.pos),\n doubleSide: (aDbl ? \"west\" : \"east\") as \"west\" | \"east\",\n }\n : null;\n return {\n doubleMain,\n loop,\n main2Extent,\n transition,\n loopInterchange: loop && doc.endplates.filter((e) => !e.at).length >= 2,\n loopReturn: loop && doc.loopReturn === \"main2\" ? \"main2\" : \"same\",\n loopRender: loop ? (doc.loopRender ?? null) : null,\n extraTracks,\n turnouts,\n signals,\n crossings,\n crossovers,\n branchConnectors,\n laneMin: Math.min(...allLanes),\n laneMax: Math.max(...allLanes),\n };\n}\n\n// ---- Endplate geometry & poses (#175) --------------------------------------\n//\n// Free-moN endplates are one standard interface; a single (x, y, heading) at\n// the track-crossing point fully determines mating (any endplate ↔ any\n// compatible endplate). Poses are in MODULE-LOCAL inches: endplate A's track\n// point at the origin, its OUTWARD normal pointing west (180°), so the module\n// body runs toward +X. A layout composes modules by walking joins and stacking\n// each pose's rigid transform (rotation + optional reflection for flips).\n//\n// Poses are DERIVED from the simple fields owners already enter — length +\n// geometry type/degrees/offset for the two axial endplates, plus the schematic\n// doc's branch endplates (#170). Complex shapes (wye, loop, other) fall back to\n// hand-entered overrides. Nothing is stored unless overridden.\n\nexport type GeometryType =\n | \"straight\"\n | \"corner_45\"\n | \"corner_90\"\n | \"curve\"\n | \"offset\"\n | \"dead_end\"\n | \"wye\"\n | \"other\";\n\nexport interface EndplatePose {\n /** Endplate id — \"A\"/\"B\" axial, \"C\"/\"D\"… branch. */\n id: string;\n /** Module-local inches; endplate A's track point is the origin. */\n x: number;\n y: number;\n /** Outward normal in degrees (0 = +X east, 90 = +Y north). A neighbour mates\n * facing the opposite heading. */\n heading: number;\n trackConfig: \"single\" | \"double\";\n /** Lateral track offsets from the crossing anchor (0 = centred single). */\n trackOffsets: number[];\n /** True when the pose was hand-entered, not derived (wye/loop/other). */\n manual?: boolean;\n}\n\nexport interface ModuleGeometryInput {\n lengthInches: number;\n geometryType?: string | null;\n geometryDegrees?: number | null;\n geometryOffsetInches?: number | null;\n /** Axial endplate configs (A first, then B). Missing → single. */\n endplateConfigs?: (\"single\" | \"double\" | null | undefined)[];\n /** Branch endplates (from the schematic doc, #170), positioned along the\n * mainline axis. */\n branches?: {\n id: string;\n atPos: number;\n side: \"up\" | \"down\";\n config?: \"single\" | \"double\" | null;\n }[];\n /** Hand-entered pose overrides by endplate id — win over derivation. */\n poseOverrides?: Record<string, { x: number; y: number; heading: number }>;\n /** Half the spacing between the two tracks of a double endplate (Free-mo ≈ 1\",\n * Free-moN ≈ 9/16\"). */\n trackHalfSpacingInches?: number;\n}\n\n/** Signed turn a module applies to the through track (CCW/left positive). */\nexport function geometryTurnDegrees(\n geometryType?: string | null,\n geometryDegrees?: number | null,\n): number {\n switch (geometryType) {\n case \"corner_45\":\n return 45;\n case \"corner_90\":\n return 90;\n case \"curve\":\n return geometryDegrees ?? 0;\n default:\n return 0;\n }\n}\n\nconst DEG = Math.PI / 180;\nconst norm360 = (d: number) => ((d % 360) + 360) % 360;\n\nfunction offsetsFor(\n config: \"single\" | \"double\",\n half: number,\n): number[] {\n return config === \"double\" ? [-half, half] : [0];\n}\n\n/**\n * Derive every endplate's module-local pose. A at the origin facing west; B\n * placed by the module's geometry (straight/offset/corner/curve via a\n * constant-radius arc = arc-length `lengthInches`, turning by the geometry\n * angle); branch endplates positioned along the mainline axis facing out their\n * side. A `dead_end`/loop module has no B. Overrides replace any derived pose.\n */\nexport function deriveEndplatePoses(geo: ModuleGeometryInput): EndplatePose[] {\n const L = geo.lengthInches > 0 ? geo.lengthInches : 24;\n const half = geo.trackHalfSpacingInches ?? 1;\n const cfg = (i: number): \"single\" | \"double\" =>\n geo.endplateConfigs?.[i] === \"double\" ? \"double\" : \"single\";\n const withOverride = (p: EndplatePose): EndplatePose => {\n const o = geo.poseOverrides?.[p.id];\n return o ? { ...p, x: o.x, y: o.y, heading: norm360(o.heading), manual: true } : p;\n };\n\n const poses: EndplatePose[] = [];\n\n // Endplate A — origin, outward normal west.\n poses.push(\n withOverride({\n id: \"A\",\n x: 0,\n y: 0,\n heading: 180,\n trackConfig: cfg(0),\n trackOffsets: offsetsFor(cfg(0), half),\n }),\n );\n\n // Endplate B — unless the module is a dead end / turnback (single endplate).\n const noB = geo.geometryType === \"dead_end\";\n if (!noB) {\n const turn = geometryTurnDegrees(geo.geometryType, geo.geometryDegrees);\n let bx: number;\n let by: number;\n let bHeading: number;\n if (geo.geometryType === \"offset\") {\n // Parallel endplates, jogged sideways over the run.\n bx = L;\n by = geo.geometryOffsetInches ?? 0;\n bHeading = 0;\n } else if (turn === 0) {\n bx = L;\n by = 0;\n bHeading = 0;\n } else {\n // Constant-radius arc of arc-length L turning `turn` (CCW/left).\n const t = turn * DEG;\n const r = L / t;\n bx = r * Math.sin(t);\n by = r * (1 - Math.cos(t));\n bHeading = turn;\n }\n poses.push(\n withOverride({\n id: \"B\",\n x: bx,\n y: by,\n heading: norm360(bHeading),\n trackConfig: cfg(1),\n trackOffsets: offsetsFor(cfg(1), half),\n }),\n );\n }\n\n // Branch endplates — at their along-axis position, facing out their side.\n // Position along the (possibly curved) mainline is approximated on the A→B\n // chord; the join solver refines with overrides where a module needs it.\n for (const b of geo.branches ?? []) {\n const frac = L > 0 ? Math.min(1, Math.max(0, b.atPos / L)) : 0;\n const px = frac * L;\n const config = b.config === \"double\" ? \"double\" : \"single\";\n poses.push(\n withOverride({\n id: b.id,\n x: px,\n y: 0,\n heading: b.side === \"down\" ? 270 : 90,\n trackConfig: config,\n trackOffsets: offsetsFor(config, half),\n }),\n );\n }\n\n return poses;\n}\n\n/** Whether a module shape's poses are fully derivable, or need manual entry\n * (a helpful cue for the authoring UI). */\nexport function poseNeedsManual(geometryType?: string | null): boolean {\n return geometryType === \"wye\" || geometryType === \"other\";\n}\n\n/** Extract manual pose overrides from a schematic doc's endplates (#175 phase\n * 1b) — the map deriveEndplatePoses / a footprint solver feeds as overrides. */\nexport function poseOverridesFromDoc(\n doc: ModuleSchematicDoc,\n): Record<string, { x: number; y: number; heading: number }> {\n const out: Record<string, { x: number; y: number; heading: number }> = {};\n for (const e of doc.endplates ?? []) {\n if (\n e.id &&\n e.pose &&\n typeof e.pose.x === \"number\" &&\n typeof e.pose.y === \"number\" &&\n typeof e.pose.heading === \"number\"\n ) {\n out[e.id] = { x: e.pose.x, y: e.pose.y, heading: e.pose.heading };\n }\n }\n return out;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -248,6 +248,20 @@ interface EditorState {
248
248
  }
249
249
  /** Build the empty editor state for a module of the given length. */
250
250
  declare function emptyEditorState(lengthInches: number): EditorState;
251
+ /** Main 2's doc track for a non-loop double module. Full length when both
252
+ * endplates are double; on a transition module (one single, one double) it
253
+ * runs between the mainline transition turnout (the one diverging to main2)
254
+ * and the double end. */
255
+ /**
256
+ * A single↔double transition turnout connects Main 1 and Main 2 — where the
257
+ * second main begins/ends. It may be authored either way round: on Main 1
258
+ * diverging to Main 2, or on Main 2 diverging to Main 1 (both describe the same
259
+ * junction). Recognise both so editing the direction doesn't drop the transition.
260
+ */
261
+ declare function isTransitionTurnout(t: {
262
+ onTrack?: string;
263
+ divergeTrack?: string;
264
+ }): boolean;
251
265
  /**
252
266
  * Build the single↔double transition as one unit (like buildPassingSiding):
253
267
  * the mainline turnout where Main 2 begins/ends, grouped in a control point —
@@ -392,6 +406,17 @@ interface ModuleFeatures {
392
406
  fromFrac: number;
393
407
  toFrac: number;
394
408
  } | null;
409
+ /** Single↔double transition, fully described (#FMN-0043). The `through` main
410
+ * runs the whole module; the `branch` main exists only on the double side and
411
+ * merges at `atFrac`. EITHER main can be through/branch — the surviving single
412
+ * track follows the transition turnout's onTrack, so the drawn side isn't
413
+ * hard-wired to Main 1. Null when not a transition. */
414
+ transition: {
415
+ throughLane: number;
416
+ branchLane: number;
417
+ atFrac: number;
418
+ doubleSide: "west" | "east";
419
+ } | null;
395
420
  /** Lane extents across every feature (mains included; negative = outside
396
421
  * Main 1). Renderers size their vertical space from these. */
397
422
  laneMin: number;
@@ -479,4 +504,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
479
504
  heading: number;
480
505
  }>;
481
506
 
482
- export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
507
+ export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
package/dist/index.d.ts CHANGED
@@ -248,6 +248,20 @@ interface EditorState {
248
248
  }
249
249
  /** Build the empty editor state for a module of the given length. */
250
250
  declare function emptyEditorState(lengthInches: number): EditorState;
251
+ /** Main 2's doc track for a non-loop double module. Full length when both
252
+ * endplates are double; on a transition module (one single, one double) it
253
+ * runs between the mainline transition turnout (the one diverging to main2)
254
+ * and the double end. */
255
+ /**
256
+ * A single↔double transition turnout connects Main 1 and Main 2 — where the
257
+ * second main begins/ends. It may be authored either way round: on Main 1
258
+ * diverging to Main 2, or on Main 2 diverging to Main 1 (both describe the same
259
+ * junction). Recognise both so editing the direction doesn't drop the transition.
260
+ */
261
+ declare function isTransitionTurnout(t: {
262
+ onTrack?: string;
263
+ divergeTrack?: string;
264
+ }): boolean;
251
265
  /**
252
266
  * Build the single↔double transition as one unit (like buildPassingSiding):
253
267
  * the mainline turnout where Main 2 begins/ends, grouped in a control point —
@@ -392,6 +406,17 @@ interface ModuleFeatures {
392
406
  fromFrac: number;
393
407
  toFrac: number;
394
408
  } | null;
409
+ /** Single↔double transition, fully described (#FMN-0043). The `through` main
410
+ * runs the whole module; the `branch` main exists only on the double side and
411
+ * merges at `atFrac`. EITHER main can be through/branch — the surviving single
412
+ * track follows the transition turnout's onTrack, so the drawn side isn't
413
+ * hard-wired to Main 1. Null when not a transition. */
414
+ transition: {
415
+ throughLane: number;
416
+ branchLane: number;
417
+ atFrac: number;
418
+ doubleSide: "west" | "east";
419
+ } | null;
395
420
  /** Lane extents across every feature (mains included; negative = outside
396
421
  * Main 1). Renderers size their vertical space from these. */
397
422
  laneMin: number;
@@ -479,4 +504,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
479
504
  heading: number;
480
505
  }>;
481
506
 
482
- export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
507
+ export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
package/dist/index.js CHANGED
@@ -33,10 +33,13 @@ function emptyEditorState(lengthInches) {
33
33
  controlPoints: []
34
34
  };
35
35
  }
36
+ function isTransitionTurnout(t) {
37
+ return t.onTrack === MAIN_TRACK_ID && t.divergeTrack === MAIN2_TRACK_ID || t.onTrack === MAIN2_TRACK_ID && t.divergeTrack === MAIN_TRACK_ID;
38
+ }
36
39
  function main2Track(state) {
37
40
  const bothDouble = state.configA === "double" && state.configB === "double";
38
- const sw = state.turnouts.find((t) => t.divergeTrack === MAIN2_TRACK_ID);
39
- if (bothDouble || !sw) {
41
+ const sw = state.turnouts.find(isTransitionTurnout);
42
+ if (bothDouble || !sw || sw.divergeTrack !== MAIN2_TRACK_ID) {
40
43
  return { id: MAIN2_TRACK_ID, role: "main", lane: 1, from: "A", to: "B" };
41
44
  }
42
45
  return state.configA === "double" ? (
@@ -59,11 +62,13 @@ function buildTransition(state) {
59
62
  id: swId,
60
63
  name: "End of Double Track",
61
64
  pos,
62
- onTrack: MAIN_TRACK_ID,
63
- divergeTrack: MAIN2_TRACK_ID,
64
- // Main 2 sits above (lane 1); the throw that runs toward the double end is
65
- // left when that end is east (single at A), right when it's west (#bug1).
66
- kind: aDouble ? "right" : "left"
65
+ // The turnout sits ON the ending main (Main 2, the upper track) and diverges
66
+ // down to the continuous Main 1 — the modeller's view of the junction. Left
67
+ // hand when the double end is west (Main 2 comes down going east), right
68
+ // when it's east (mirror).
69
+ onTrack: MAIN2_TRACK_ID,
70
+ divergeTrack: MAIN_TRACK_ID,
71
+ kind: aDouble ? "left" : "right"
67
72
  };
68
73
  const cpId = nextId("cp", state.controlPoints.map((c) => c.id));
69
74
  const sig = (facing) => ({
@@ -516,25 +521,31 @@ function moduleFeatures(doc) {
516
521
  });
517
522
  }
518
523
  const isMainId = (id) => !!id && trackById.get(id)?.role === "main";
524
+ const epDouble = (id) => (doc.endplates.find((e) => e.id === id)?.tracks ?? []).some(
525
+ (t) => t.config === "double"
526
+ );
527
+ const aDbl = epDouble("A");
528
+ const bDbl = epDouble("B");
529
+ const transitionSw = aDbl !== bDbl ? (doc.turnouts ?? []).find(isTransitionTurnout) : void 0;
519
530
  const m2m = (doc.turnouts ?? []).filter(
520
- (sw) => isMainId(sw.onTrack) && isMainId(sw.divergeTrack) && (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1)
531
+ (sw) => sw !== transitionSw && isMainId(sw.onTrack) && isMainId(sw.divergeTrack) && (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1)
521
532
  );
522
533
  const usedLegs = /* @__PURE__ */ new Set();
523
534
  for (const t1 of m2m) {
524
535
  if (usedLegs.has(t1.id)) continue;
525
- usedLegs.add(t1.id);
526
536
  const t2 = m2m.find(
527
- (x) => !usedLegs.has(x.id) && x.onTrack === t1.divergeTrack && x.divergeTrack === t1.onTrack
537
+ (x) => !usedLegs.has(x.id) && x.id !== t1.id && x.onTrack === t1.divergeTrack && x.divergeTrack === t1.onTrack
528
538
  );
529
- if (t2) usedLegs.add(t2.id);
530
- const end = t2 ?? t1;
539
+ if (!t2) continue;
540
+ usedLegs.add(t1.id);
541
+ usedLegs.add(t2.id);
531
542
  crossovers.push({
532
- id: t2 ? `${t1.id}-${t2.id}` : t1.id,
533
- name: t1.name ?? t2?.name ?? "",
543
+ id: `${t1.id}-${t2.id}`,
544
+ name: t1.name ?? t2.name ?? "",
534
545
  fromPosFrac: clampFrac(t1.pos),
535
546
  fromLane: trackLane.get(t1.onTrack) ?? 0,
536
- toPosFrac: clampFrac(end === t1 ? t1.pos : end.pos),
537
- toLane: trackLane.get(t1.divergeTrack) ?? 1
547
+ toPosFrac: clampFrac(t2.pos),
548
+ toLane: trackLane.get(t2.onTrack) ?? 1
538
549
  });
539
550
  }
540
551
  const turnouts = (doc.turnouts ?? []).map((t) => ({
@@ -588,14 +599,26 @@ function moduleFeatures(doc) {
588
599
  const loop = isLoopDoc(doc);
589
600
  const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);
590
601
  const main2Positioned = !!main2 && (main2.fromPos != null || main2.toPos != null) && !loop;
591
- const main2Extent = main2Positioned ? {
592
- fromFrac: clampFrac(main2.fromPos ?? 0),
593
- toFrac: clampFrac(main2.toPos ?? len)
602
+ let main2Extent = null;
603
+ if (main2Positioned) {
604
+ main2Extent = {
605
+ fromFrac: clampFrac(main2.fromPos ?? 0),
606
+ toFrac: clampFrac(main2.toPos ?? len)
607
+ };
608
+ } else if (main2 && transitionSw && transitionSw.divergeTrack === MAIN2_TRACK_ID && !loop) {
609
+ main2Extent = aDbl ? { fromFrac: 0, toFrac: clampFrac(transitionSw.pos) } : { fromFrac: clampFrac(transitionSw.pos), toFrac: 1 };
610
+ }
611
+ const transition = transitionSw && aDbl !== bDbl && !loop ? {
612
+ throughLane: trackLane.get(transitionSw.onTrack) ?? 0,
613
+ branchLane: trackLane.get(transitionSw.divergeTrack) ?? 1,
614
+ atFrac: clampFrac(transitionSw.pos),
615
+ doubleSide: aDbl ? "west" : "east"
594
616
  } : null;
595
617
  return {
596
618
  doubleMain,
597
619
  loop,
598
620
  main2Extent,
621
+ transition,
599
622
  loopInterchange: loop && doc.endplates.filter((e) => !e.at).length >= 2,
600
623
  loopReturn: loop && doc.loopReturn === "main2" ? "main2" : "same",
601
624
  loopRender: loop ? doc.loopRender ?? null : null,
@@ -707,6 +730,6 @@ function poseOverridesFromDoc(doc) {
707
730
  return out;
708
731
  }
709
732
 
710
- export { MAIN2_TRACK_ID, MAIN_TRACK_ID, N_SCALE_RATIO, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
733
+ export { MAIN2_TRACK_ID, MAIN_TRACK_ID, N_SCALE_RATIO, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
711
734
  //# sourceMappingURL=index.js.map
712
735
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @willcgage/module-schematic — the shared module operations-schematic (track-graph)\n * that the Module Repository authors and Free-Dispatcher imports.\n *\n * Topological, straightened-first: positions are 1-D inches along the module\n * (from endplate A), lanes are integer track indices (0 = primary main). This is\n * the single source of truth for both apps — the doc types, the lenient parser\n * (docs arrive as jsonb / unknown), the pure feature resolver both renderers\n * draw, the N-scale helpers, and the editor <-> doc state machine an authoring\n * UI binds to. See docs/module-schematic-format.md in the free-dispatcher repo.\n *\n * Framework-agnostic and side-effect-free so it can be unit-tested and consumed\n * from Next.js (server + client) and Electron alike.\n */\n\nexport type TrackConfig = \"single\" | \"double\";\nexport type TrackRole = \"main\" | \"siding\" | \"spur\" | \"yard\" | \"crossover\";\nexport type TurnoutKind = \"left\" | \"right\" | \"wye\";\nexport type SignalFacing = \"AtoB\" | \"BtoA\";\nexport type SignalSide = \"above\" | \"below\";\n\nexport interface SchematicEndplateTrack {\n trackId: string;\n lane: number;\n config?: TrackConfig | null;\n}\nexport interface SchematicEndplate {\n id: string; // \"A\" (West) | \"B\" (East) | \"C\"… (branch, #170)\n label?: string | null;\n tracks?: SchematicEndplateTrack[];\n /** Branch endplates (#170): where a 3rd+ endplate sits — pos inches from A,\n * on the up (north/above) or down side. Absent = axial (A at 0, B at\n * lengthInches). Renderers draw a named connector arrow (the CATS/US&S\n * off-band idiom) until branch spines land. */\n at?: { pos: number; side: \"up\" | \"down\" };\n /** Manual pose override (#175 phase 1b) — the endplate's module-local track\n * point (x, y inches) + outward-normal heading (°). Hand-entered for shapes\n * the geometry fields can't derive (wye, freeform, loop); wins over\n * deriveEndplatePoses' derivation. */\n pose?: { x: number; y: number; heading: number };\n}\nexport interface SchematicTrack {\n id: string;\n role: TrackRole;\n lane: number;\n from?: string;\n to?: string;\n fromPos?: number | null;\n toPos?: number | null;\n capacityFeet?: number | null;\n industryRef?: number | null;\n /** The module_tracks row this track is (single source of truth); null = new. */\n moduleTrackId?: number | null;\n /** Owner's track name, mirrored to module_tracks.track_name. */\n trackName?: string;\n /** Inside the balloon of a loop module (#165): pos still measures from\n * endplate A (past the throat = in the loop), lane is the ladder/arc index —\n * one record drives both the unrolled fan and a geometric render. */\n inLoop?: boolean;\n}\nexport interface SchematicTurnout {\n id: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind?: TurnoutKind;\n name?: string | null;\n address?: string | null;\n}\nexport interface SchematicSignal {\n id: string;\n pos: number;\n /** Track the signal governs; absent = the primary main (lane 0). */\n track?: string;\n facing?: SignalFacing;\n kind?: \"mast\" | \"dwarf\";\n name?: string | null;\n aspects?: string[];\n /** Which side of the track the signal sits on (#122). */\n side?: SignalSide;\n /** Turnout this control point governs; absent = standalone block signal. */\n turnout?: string;\n}\nexport interface SchematicBlock {\n id: string;\n name: string;\n tracks?: string[];\n from: number;\n to: number;\n}\n/** A grade crossing / diamond (#170): two tracks cross with no route choice —\n * a conflict node, not a turnout. A connected diamond adds normal turnouts\n * alongside. Drawn as an X between the two tracks' lanes. */\nexport interface SchematicCrossing {\n id: string;\n pos: number;\n /** The two crossing tracks, by id. */\n tracks: [string, string];\n name?: string | null;\n}\n/**\n * A control point is an interlocking: a named group of one or more signals and\n * zero or more turnouts. A passing siding has two (West/East); a lone block\n * signal is a control point with one signal and no turnouts.\n */\nexport interface SchematicControlPoint {\n id: string;\n name?: string | null;\n turnouts?: string[];\n /** Crossings this interlocking protects (#170). */\n crossings?: string[];\n signals?: SchematicSignal[];\n}\nexport interface ModuleSchematicDoc {\n version: number;\n module?: string;\n lengthInches?: number;\n /** Single-endplate turnback (balloon loop): the main enters at A, runs the\n * lead, and turns back — renderers draw a terminal bulb at the far end\n * instead of a second endplate. Also implied by a single-entry endplates\n * array (a category:\"loop\" module like Seaford). */\n loop?: boolean;\n /** Where the balloon returns (#165): \"same\" (default) turns back onto the\n * same main; \"main2\" is a directional return on a double-track main —\n * out on Main 1, back on Main 2 — drawn as a U joining the two lanes\n * (the transit terminal-loop idiom). */\n loopReturn?: \"same\" | \"main2\";\n /** Optional rendering override: \"bulb\" (abstract terminal), \"fan\" (interior\n * tracks unrolled as a ladder past the throat — the default when inLoop\n * tracks exist), \"geometric\" (drawn balloon, AL&E-style). */\n loopRender?: \"bulb\" | \"fan\" | \"geometric\";\n endplates: SchematicEndplate[];\n tracks: SchematicTrack[];\n turnouts?: SchematicTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings?: SchematicCrossing[];\n controlPoints?: SchematicControlPoint[];\n /** @deprecated pre-grouping flat signals; read for back-compat. */\n signals?: SchematicSignal[];\n}\n\n/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */\nexport function isLoopDoc(doc: ModuleSchematicDoc): boolean {\n return doc.loop === true || doc.endplates.length === 1;\n}\n\nexport const MAIN_TRACK_ID = \"main\";\n/** The second main on double-track modules — a real track entity so turnouts\n * and signals can attach to it (a spur off Main 2 must diverge from lane 1,\n * not draw a crossover from Main 1). Emitted by stateToDoc when either\n * endplate is double; legacy docs without it still parse. */\nexport const MAIN2_TRACK_ID = \"main2\";\n\n// North American N scale (1:160): 396 real inches → 5280 scale feet = one mile.\nexport const N_SCALE_RATIO = 160;\n/** Real inches on the module → scale feet of prototype track represented. */\nexport function inchesToScaleFeet(inches: number, ratio = N_SCALE_RATIO): number {\n return (inches * ratio) / 12;\n}\n/** Scale feet of prototype track → real inches on the module. */\nexport function scaleFeetToInches(feet: number, ratio = N_SCALE_RATIO): number {\n return (feet * 12) / ratio;\n}\n\n/** Parse a jsonb value into a schematic doc, or null if it isn't one. */\nexport function asModuleSchematic(x: unknown): ModuleSchematicDoc | null {\n if (!x || typeof x !== \"object\") return null;\n const d = x as Record<string, unknown>;\n if (typeof d.version !== \"number\") return null;\n if (!Array.isArray(d.endplates) || !Array.isArray(d.tracks)) return null;\n return d as unknown as ModuleSchematicDoc;\n}\n\n// ---- Editor state (a flatter shape an authoring form binds to) -------------\n\nexport interface EditorTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromPos: number;\n toPos: number;\n /** module_tracks row id (single source of truth), or null for a new track. */\n moduleTrackId: number | null;\n /** Owner's track name → module_tracks.track_name. */\n trackName: string;\n /** Inside the balloon of a loop module (#165). */\n inLoop?: boolean;\n}\n\n/** A module_tracks row as loaded for the editor. */\nexport interface ModuleTrackRow {\n id: number;\n track_name: string | null;\n capacity_scale_feet: number | null;\n}\nexport interface EditorTurnout {\n id: string;\n name: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind: TurnoutKind;\n}\nexport interface EditorCpSignal {\n id: string;\n pos: number;\n track: string;\n facing: SignalFacing;\n side: SignalSide;\n}\nexport interface EditorControlPoint {\n id: string;\n name: string;\n turnouts: string[]; // turnout ids grouped under this control point\n /** Crossing ids this interlocking protects (#170). */\n crossings?: string[];\n signals: EditorCpSignal[];\n}\nexport interface EditorCrossing {\n id: string;\n name: string;\n pos: number;\n trackA: string;\n trackB: string;\n}\n/** A 3rd+ endplate — a branch/junction connection off the module (#170).\n * A module may have several (e.g. a set carrying a second railroad through:\n * MoPac enters at one branch endplate and leaves at another). */\nexport interface EditorBranch {\n label: string;\n pos: number;\n side: \"up\" | \"down\";\n config: TrackConfig;\n}\n/** Endplate B on a loop module: a standard endplate makes the balloon an\n * INTERCHANGE (a second route connects at the loop, e.g. Seaford); \"none\"\n * makes it a pure turnback. Non-loop modules always have a real B. */\nexport type EndplateBConfig = TrackConfig | \"none\";\n\nexport interface EditorState {\n lengthInches: number;\n /** Balloon loop: the main runs the lead and turns back; positions past the\n * throat are inside the balloon. Endplate B stays independently available —\n * present = interchange loop, \"none\" = pure turnback. */\n loop: boolean;\n /** Where the balloon returns: same main, or Main 2 (directional return on a\n * double-track main — drawn as a U joining the two lanes). */\n loopReturn: \"same\" | \"main2\";\n configA: TrackConfig;\n configB: EndplateBConfig;\n extraTracks: EditorTrack[]; // sidings/spurs/…; the main track is implicit\n turnouts: EditorTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings: EditorCrossing[];\n /** Branch endplates C, D, … — junction connections (#170); empty = through\n * module. Emitted in order as endplates \"C\", \"D\", \"E\"… */\n branches: EditorBranch[];\n /** Manual endplate pose overrides by endplate id (#175 phase 1b). */\n poseOverrides: Record<string, { x: number; y: number; heading: number }>;\n controlPoints: EditorControlPoint[];\n}\n\n/** Build the empty editor state for a module of the given length. */\nexport function emptyEditorState(lengthInches: number): EditorState {\n return {\n lengthInches: lengthInches > 0 ? lengthInches : 24,\n loop: false,\n loopReturn: \"same\",\n configA: \"single\",\n configB: \"single\",\n extraTracks: [],\n turnouts: [],\n crossings: [],\n branches: [],\n poseOverrides: {},\n controlPoints: [],\n };\n}\n\n/** Main 2's doc track for a non-loop double module. Full length when both\n * endplates are double; on a transition module (one single, one double) it\n * runs between the mainline transition turnout (the one diverging to main2)\n * and the double end. */\nfunction main2Track(state: EditorState): SchematicTrack {\n const bothDouble = state.configA === \"double\" && state.configB === \"double\";\n const sw = state.turnouts.find((t) => t.divergeTrack === MAIN2_TRACK_ID);\n if (bothDouble || !sw) {\n return { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, from: \"A\", to: \"B\" };\n }\n return state.configA === \"double\"\n ? // Double at A: Main 2 runs from A and ends at the turnout.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: 0, toPos: sw.pos }\n : // Double at B: Main 2 begins at the turnout and runs to B.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: sw.pos, toPos: state.lengthInches };\n}\n\n/**\n * Build the single↔double transition as one unit (like buildPassingSiding):\n * the mainline turnout where Main 2 begins/ends, grouped in a control point —\n * an \"End of Double Track\" is a classic CTC interlocking — with signals both\n * directions. Returns items to merge into the editor state.\n */\nexport function buildTransition(state: EditorState): {\n turnout: EditorTurnout;\n controlPoint: EditorControlPoint;\n} | null {\n const aDouble = state.configA === \"double\";\n const bDouble = state.configB === \"double\";\n if (state.loop || aDouble === bDouble) return null; // not a transition module\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.25));\n // The turnout sits toward the single end so the double track carries most\n // of the module; owner adjusts the position afterwards.\n const pos = aDouble ? len - inset : inset;\n\n const swId = nextId(\"sw\", state.turnouts.map((t) => t.id));\n const turnout: EditorTurnout = {\n id: swId,\n name: \"End of Double Track\",\n pos,\n onTrack: MAIN_TRACK_ID,\n divergeTrack: MAIN2_TRACK_ID,\n // Main 2 sits above (lane 1); the throw that runs toward the double end is\n // left when that end is east (single at A), right when it's west (#bug1).\n kind: aDouble ? \"right\" : \"left\",\n };\n\n const cpId = nextId(\"cp\", state.controlPoints.map((c) => c.id));\n const sig = (facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoint: EditorControlPoint = {\n id: cpId,\n name: \"End of Double Track\",\n turnouts: [swId],\n signals: [sig(\"AtoB\"), sig(\"BtoA\")],\n };\n return { turnout, controlPoint };\n}\n\n/** Attach manual pose overrides (#175 phase 1b) to endplates by id. */\nfunction withPoses(\n endplates: SchematicEndplate[],\n overrides: Record<string, { x: number; y: number; heading: number }>,\n): SchematicEndplate[] {\n return endplates.map((e) =>\n overrides[e.id] ? { ...e, pose: overrides[e.id] } : e,\n );\n}\n\n/** Assemble a spec-conformant doc from the editor state. */\nexport function stateToDoc(\n state: EditorState,\n recordNumber: string,\n): ModuleSchematicDoc {\n return {\n version: 1,\n module: recordNumber,\n lengthInches: state.lengthInches,\n ...(state.loop ? { loop: true } : {}),\n ...(state.loop && state.loopReturn === \"main2\" ? { loopReturn: \"main2\" as const } : {}),\n endplates: withPoses(\n [\n ...(state.loop\n ? // Balloon loop: A is the entry. A standard endplate B on the balloon\n // makes it an INTERCHANGE (second route connects at the loop, e.g.\n // Seaford); configB \"none\" makes it a pure turnback.\n [\n { id: \"A\", label: \"Entry\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n ...(state.configB !== \"none\"\n ? [{ id: \"B\", label: \"Interchange\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }]\n : []),\n ]\n : [\n { id: \"A\", label: \"West\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n // Non-loop modules always have a real B (\"none\" never applies).\n {\n id: \"B\",\n label: \"East\",\n tracks: [\n {\n trackId: MAIN_TRACK_ID,\n lane: 0,\n config: state.configB === \"none\" ? \"single\" : state.configB,\n },\n ],\n },\n ]),\n // Branch endplates C, D, … — junction connections at pos, off one side\n // (#170). A set can carry several (e.g. a second railroad through).\n ...state.branches.map((b, i) => ({\n id: String.fromCharCode(67 + i), // C, D, E…\n label: b.label || `Branch ${i + 1}`,\n tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],\n at: { pos: b.pos, side: b.side },\n })),\n ],\n state.poseOverrides,\n ),\n tracks: [\n state.loop\n ? // The main runs the lead from A and turns back at the balloon.\n { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, fromPos: 0, toPos: state.lengthInches }\n : { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, from: \"A\", to: \"B\" },\n // Double track: Main 2 is a real entity so turnouts/signals can attach.\n // On a loop it exists only for a Main 2 directional return (the U joins\n // the two lanes at the balloon); a same-main loop's parallel lead legs\n // are ONE main. On a TRANSITION module (one endplate single, the other\n // double) Main 2 only runs from the mainline turnout to the double end —\n // the turnout that diverges to main2 is the single source of truth for\n // where the transition sits (fd#175 / FMN-0038).\n ...(!state.loop && (state.configA === \"double\" || state.configB === \"double\")\n ? [main2Track(state)]\n : []),\n ...(state.loop && state.loopReturn === \"main2\"\n ? [{ id: MAIN2_TRACK_ID, role: \"main\" as const, lane: 1, fromPos: 0, toPos: state.lengthInches }]\n : []),\n ...state.extraTracks.map((t) => ({\n id: t.id,\n role: t.role,\n lane: t.lane,\n fromPos: t.fromPos,\n toPos: t.toPos,\n moduleTrackId: t.moduleTrackId,\n trackName: t.trackName || undefined,\n capacityFeet: Math.round(inchesToScaleFeet(Math.abs(t.toPos - t.fromPos))),\n ...(state.loop && t.inLoop ? { inLoop: true } : {}),\n })),\n ],\n turnouts: state.turnouts.map((t) => ({\n id: t.id,\n pos: t.pos,\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: t.kind,\n name: t.name || undefined,\n })),\n ...(state.crossings.length > 0\n ? {\n crossings: state.crossings.map((x) => ({\n id: x.id,\n pos: x.pos,\n tracks: [x.trackA, x.trackB] as [string, string],\n name: x.name || undefined,\n })),\n }\n : {}),\n controlPoints: state.controlPoints.map((c) => ({\n id: c.id,\n name: c.name,\n turnouts: c.turnouts,\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: c.signals.map((s) => ({\n id: s.id,\n pos: s.pos,\n track: s.track,\n facing: s.facing,\n kind: \"mast\" as const,\n side: s.side,\n })),\n })),\n };\n}\n\n/**\n * Derive editor state from the doc and the module's Track section rows. Tracks\n * are the single source of truth for name/capacity (module_tracks), while the\n * schematic doc adds geometry (lane, positions). We merge: doc tracks first\n * (they carry geometry + their moduleTrackId link), then any module_tracks not\n * yet positioned in the schematic.\n */\nexport function docToState(\n doc: unknown,\n fallbackLength: number,\n moduleTracks: ModuleTrackRow[] = [],\n): EditorState {\n const base = emptyEditorState(fallbackLength);\n const d =\n doc && typeof doc === \"object\" ? (doc as ModuleSchematicDoc) : null;\n const hasDoc = !!d && typeof d.lengthInches === \"number\" && Array.isArray(d.tracks);\n // The module's length is authoritative (the mainline is the module). If the\n // saved doc used a different length, rescale its feature positions to fit so\n // the mainline always reads as the module's true length.\n const len = fallbackLength > 0 ? fallbackLength : hasDoc ? d!.lengthInches! : 24;\n const docLen = hasDoc && d!.lengthInches! > 0 ? d!.lengthInches! : len;\n const scale = docLen > 0 ? len / docLen : 1;\n const sc = (p: number) => Math.round(p * scale);\n\n const nameOf = (id: number | null | undefined): string => {\n const mt = id != null ? moduleTracks.find((m) => m.id === id) : undefined;\n return mt?.track_name ?? \"\";\n };\n\n const extraTracks: EditorTrack[] = [];\n const usedMt = new Set<number>();\n if (hasDoc) {\n for (const t of d!.tracks) {\n if (t.role === \"main\") continue;\n const moduleTrackId = t.moduleTrackId ?? null;\n if (moduleTrackId != null) usedMt.add(moduleTrackId);\n extraTracks.push({\n id: t.id,\n role: (t.role as TrackRole) ?? \"siding\",\n lane: t.lane ?? 1,\n fromPos: sc(t.fromPos ?? 0),\n toPos: t.toPos != null ? sc(t.toPos) : len,\n moduleTrackId,\n trackName: t.trackName ?? nameOf(moduleTrackId),\n ...(t.inLoop ? { inLoop: true } : {}),\n });\n }\n }\n // Link pre-migration doc tracks (no moduleTrackId yet) to unused module_tracks\n // by order — keeping the doc track's id so turnout/signal references stay\n // valid. Only after that do leftover module_tracks become new tracks.\n const unused = moduleTracks.filter((mt) => !usedMt.has(mt.id));\n let ui = 0;\n for (const et of extraTracks) {\n if (et.moduleTrackId == null && ui < unused.length) {\n const mt = unused[ui++];\n et.moduleTrackId = mt.id;\n if (!et.trackName) et.trackName = mt.track_name ?? \"\";\n usedMt.add(mt.id);\n }\n }\n let lane = Math.max(0, ...extraTracks.map((t) => t.lane));\n for (const mt of moduleTracks) {\n if (usedMt.has(mt.id)) continue;\n lane += 1;\n extraTracks.push({\n id: `mt${mt.id}`,\n role: \"siding\",\n lane,\n fromPos: Math.round(len * 0.2),\n toPos: Math.round(len * 0.8),\n moduleTrackId: mt.id,\n trackName: mt.track_name ?? \"\",\n });\n }\n\n if (!hasDoc) return { ...base, lengthInches: len, extraTracks };\n\n const configOf = (id: string): TrackConfig => {\n const ep = (d!.endplates ?? []).find((e) => e.id === id);\n return ep?.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\";\n };\n const loop = isLoopDoc(d!);\n const hasB = (d!.endplates ?? []).some((e) => e.id === \"B\");\n // Branch endplates C, D, … (junction connections, #170).\n const branchEps = (d!.endplates ?? []).filter(\n (e) => e.id !== \"A\" && e.id !== \"B\" && e.at,\n );\n const poseOverrides = poseOverridesFromDoc(d!);\n return {\n lengthInches: len,\n loop,\n loopReturn: loop && d!.loopReturn === \"main2\" ? \"main2\" : \"same\",\n configA: configOf(\"A\"),\n // On a loop, a missing B means pure turnback; present = interchange loop.\n configB: loop && !hasB ? \"none\" : configOf(\"B\"),\n branches: branchEps.map((ep) => ({\n label: ep.label ?? \"Branch\",\n pos: sc(ep.at!.pos),\n side: ep.at!.side === \"down\" ? \"down\" : \"up\",\n config: ep.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\",\n })),\n poseOverrides,\n crossings: (d!.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n pos: sc(x.pos),\n trackA: x.tracks?.[0] ?? MAIN_TRACK_ID,\n trackB: x.tracks?.[1] ?? MAIN_TRACK_ID,\n })),\n extraTracks,\n turnouts: (d!.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n pos: sc(t.pos),\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: (t.kind as TurnoutKind) ?? \"right\",\n })),\n controlPoints: readControlPoints(d!, sc),\n };\n}\n\n/** Control points from a doc, migrating pre-grouping flat signals into groups. */\nfunction readControlPoints(\n d: ModuleSchematicDoc,\n sc: (p: number) => number = (p) => p,\n): EditorControlPoint[] {\n if (Array.isArray(d.controlPoints)) {\n return d.controlPoints.map((c) => ({\n id: c.id,\n name: c.name ?? \"\",\n turnouts: c.turnouts ?? [],\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: (c.signals ?? []).map((s) => ({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n })),\n }));\n }\n // Back-compat: group old flat signals by their turnout (or standalone).\n const groups = new Map<string, EditorControlPoint>();\n let n = 0;\n for (const s of d.signals ?? []) {\n const key = s.turnout || `blk-${s.id}`;\n let cp = groups.get(key);\n if (!cp) {\n cp = { id: `cp${++n}`, name: s.name ?? \"\", turnouts: s.turnout ? [s.turnout] : [], signals: [] };\n groups.set(key, cp);\n }\n cp.signals.push({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n });\n }\n return [...groups.values()];\n}\n\n/** Find an unused `${prefix}${n}` id given the ones already present. */\nexport function nextId(prefix: string, existing: string[]): string {\n let n = 1;\n while (existing.includes(`${prefix}${n}`)) n += 1;\n return `${prefix}${n}`;\n}\n\n/**\n * Build a passing siding as one unit: the siding track, a switch at each end,\n * and control-point signals for both directions at each end (prototype Station\n * Entering Signal). Returns the new items to merge into the editor state.\n */\nexport function buildPassingSiding(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n controlPoints: EditorControlPoint[];\n} {\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.08));\n const fromPos = inset;\n const toPos = Math.max(fromPos + 1, len - inset);\n // First free lane above the main(s): lane 1 is Main 2 on double modules.\n const baseLane =\n state.configA === \"double\" || state.configB === \"double\" ? 2 : 1;\n const lane = Math.max(baseLane, ...state.extraTracks.map((t) => t.lane + 1));\n\n const trackIds = [MAIN_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const sidId = nextId(\"sid\", trackIds);\n const track: EditorTrack = {\n id: sidId,\n role: \"siding\",\n lane,\n fromPos,\n toPos,\n moduleTrackId: null,\n trackName: \"Passing siding\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const swW = nextId(\"sw\", swIds);\n const swE = nextId(\"sw\", [...swIds, swW]);\n // A siding above the main throws LEFT at its west turnout (body runs east) and\n // RIGHT at its east turnout (body runs west) — both resolve to the same side,\n // so `kind` and the drawn lane agree (divergeSideForHand / #bug1).\n const turnouts: EditorTurnout[] = [\n { id: swW, name: \"West Siding\", pos: fromPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"left\" },\n { id: swE, name: \"East Siding\", pos: toPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"right\" },\n ];\n\n // One control point at each end, each grouping its switch and both-direction\n // signals on the main (prototype Station Entering Signal).\n const cpIds = state.controlPoints.map((c) => c.id);\n const cpW = nextId(\"cp\", cpIds);\n const cpE = nextId(\"cp\", [...cpIds, cpW]);\n const sig = (cpId: string, pos: number, facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n // opposite directions on opposite sides so they never overlap\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoints: EditorControlPoint[] = [\n { id: cpW, name: \"West Siding\", turnouts: [swW], signals: [sig(cpW, fromPos, \"AtoB\"), sig(cpW, fromPos, \"BtoA\")] },\n { id: cpE, name: \"East Siding\", turnouts: [swE], signals: [sig(cpE, toPos, \"AtoB\"), sig(cpE, toPos, \"BtoA\")] },\n ];\n\n return { track, turnouts, controlPoints };\n}\n\n/**\n * Build a crossover as one unit: a short connector track between Main 1 and\n * Main 2, with a turnout on each main. The two turnouts sit on different lanes,\n * so the feature resolver draws it as a diagonal (not a lane-paralleling siding)\n * — this is what connects the two dots (#bug2). Needs a double-track module.\n */\nexport function buildCrossover(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n} | null {\n const hasSecond = state.configA === \"double\" || state.configB === \"double\";\n if (state.loop || !hasSecond) return null;\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const mid = Math.round(len / 2);\n const gap = Math.max(3, Math.round(len * 0.04));\n const w = Math.round(mid - gap / 2);\n const e = Math.round(mid + gap / 2);\n\n const trackIds = [MAIN_TRACK_ID, MAIN2_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const xoId = nextId(\"xo\", trackIds);\n const track: EditorTrack = {\n id: xoId,\n role: \"crossover\",\n lane: 1,\n fromPos: w,\n toPos: e,\n moduleTrackId: null,\n trackName: \"Crossover\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const sw1 = nextId(\"sw\", swIds);\n const sw2 = nextId(\"sw\", [...swIds, sw1]);\n const turnouts: EditorTurnout[] = [\n { id: sw1, name: \"Crossover\", pos: w, onTrack: MAIN_TRACK_ID, divergeTrack: xoId, kind: \"left\" },\n { id: sw2, name: \"Crossover\", pos: e, onTrack: MAIN2_TRACK_ID, divergeTrack: xoId, kind: \"right\" },\n ];\n\n return { track, turnouts };\n}\n\n// ---- Pure feature resolver (both renderers draw these) --------------------\n\nexport interface DrawTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromFrac: number;\n toFrac: number;\n capacityFeet: number | null;\n /** Lane of the main this track diverges from (via its turnout) — the origin\n * of the diverge diagonal. A spur off Main 2 starts at lane 1, not lane 0;\n * without this, renderers draw what looks like a crossover. */\n divergesFromLane: number;\n /** The end that meets the main at its turnout (throat) and the far stub end,\n * as fractions of length. Direction-preserving: unlike fromFrac/toFrac (always\n * sorted West→East), these keep WHICH end joins, so an east-facing spur draws\n * its throat on the east. A siding meets the main at both ends — there\n * throat=fromFrac, stub=toFrac (the fields are only meaningful for spurs). */\n throatFrac: number;\n stubFrac: number;\n /** Inside the balloon of a loop module (#165). */\n inLoop: boolean;\n}\nexport interface DrawTurnout {\n id: string;\n name: string;\n posFrac: number;\n onLane: number;\n divergeLane: number;\n}\nexport interface DrawSignal {\n id: string;\n name: string;\n posFrac: number;\n lane: number;\n facing: SignalFacing;\n side: SignalSide;\n /** Owning control point's id, when the signal came from a CP group — lets a\n * renderer join the drawn signal back to interlocking-level state (aspects). */\n cp?: string;\n /** 0-based rank among signals that would otherwise land on the exact same\n * spot (same lane + side + position). Renderers offset each further from the\n * track by `stack` so a control point's signals never overlap. */\n stack: number;\n}\n/** A grade crossing / diamond — draw an X spanning the two lanes (#170). */\nexport interface DrawCrossing {\n id: string;\n name: string;\n posFrac: number;\n laneA: number;\n laneB: number;\n}\n/** A crossover — a connector track joining two parallel mains through a turnout\n * on each. Draw a straight diagonal between the two turnout points (unlike a\n * siding, which parallels a lane between its turnouts). Detected structurally:\n * a track whose turnouts sit on two different lanes. */\nexport interface DrawCrossover {\n id: string;\n name: string;\n fromPosFrac: number;\n fromLane: number;\n toPosFrac: number;\n toLane: number;\n}\n/** A branch endplate — draw a connector stub + arrow + label off the given\n * side (the CATS/US&S off-band idiom, #170). */\nexport interface BranchConnector {\n id: string;\n label: string;\n posFrac: number;\n side: \"up\" | \"down\";\n}\nexport interface ModuleFeatures {\n /** Whether either endplate declares a double-track main. */\n doubleMain: boolean;\n /** Balloon loop — draw a terminal bulb at the far end. */\n loop: boolean;\n /** Loop with a standard endplate B on the balloon: an interchange — a\n * second route connects at the loop (draw an endplate branch off the bulb). */\n loopInterchange: boolean;\n /** Where the balloon returns: \"main2\" = directional return on a double-track\n * main — draw a U joining lanes 0 and 1 instead of the bulb (#165). */\n loopReturn: \"same\" | \"main2\";\n /** Rendering override from the doc; renderers may ignore modes they don't\n * implement yet (\"geometric\"). */\n loopRender: \"bulb\" | \"fan\" | \"geometric\" | null;\n /** Non-main tracks (sidings/spurs/yard/crossover). */\n extraTracks: DrawTrack[];\n turnouts: DrawTurnout[];\n signals: DrawSignal[];\n /** Grade crossings / diamonds (#170). */\n crossings: DrawCrossing[];\n /** Crossovers — connector tracks between two mains (drawn as a diagonal). */\n crossovers: DrawCrossover[];\n /** Branch endplates — junction connectors off the module (#170). */\n branchConnectors: BranchConnector[];\n /** Main 2's extent when it doesn't run the full module — a single↔double\n * transition (Main 2 starts/ends at the mainline turnout). Null = full\n * length (or no Main 2). Renderers draw the partial line + its diverge. */\n main2Extent: { fromFrac: number; toFrac: number } | null;\n /** Lane extents across every feature (mains included; negative = outside\n * Main 1). Renderers size their vertical space from these. */\n laneMin: number;\n laneMax: number;\n}\n\n/**\n * Which side of the main a diverging track draws on, given the turnout's HAND\n * and the direction the track body runs along the main from the turnout\n * (`stubDir`: +1 = the body extends east / toward B, −1 = west / toward A).\n * Returns +1 (above the main) or −1 (below); 0 for a wye or unset hand (keep the\n * authored side). A left-hand turnout throws its route to the same side its body\n * runs (facing the frog, left is the inside of a body running that way); a\n * right-hand throws to the opposite side. `kind` is the source of truth for the\n * drawn side (#bug1) — the stored lane's sign is reconciled to match it.\n */\nexport function divergeSideForHand(\n kind: TurnoutKind | undefined,\n stubDir: number,\n): -1 | 0 | 1 {\n if (kind !== \"left\" && kind !== \"right\") return 0; // wye / unset → no change\n const s = stubDir >= 0 ? 1 : -1;\n return kind === \"left\" ? (s as 1 | -1) : ((-s) as 1 | -1);\n}\n\n/**\n * Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a\n * fraction of the module length; endplate A = 0, B = length; turnouts sit at\n * their pos. Tracks may carry explicit fromPos/toPos (overriding node lookup).\n * To-scale: a feature renders at its true position, clamped only to the\n * module's own extent — so signals near an end read at their real spot, not\n * bunched at an inset (#122).\n */\nexport function moduleFeatures(doc: ModuleSchematicDoc): ModuleFeatures {\n const len =\n doc.lengthInches && doc.lengthInches > 0\n ? doc.lengthInches\n : Math.max(\n 1,\n ...doc.tracks.map((t) => Math.max(t.fromPos ?? 0, t.toPos ?? 0)),\n ...(doc.turnouts ?? []).map((t) => t.pos),\n );\n const clampFrac = (p: number) => Math.min(1, Math.max(0, p / len));\n\n const trackLane = new Map<string, number>();\n for (const t of doc.tracks) trackLane.set(t.id, t.lane);\n\n // Endplate positions: first endplate = West (0), the rest = East (len).\n const endplatePos = new Map<string, number>();\n doc.endplates.forEach((e, i) => endplatePos.set(e.id, i === 0 ? 0 : len));\n const turnoutPos = new Map<string, number>();\n for (const t of doc.turnouts ?? []) turnoutPos.set(t.id, t.pos);\n const posOf = (nodeId?: string): number | null => {\n if (nodeId == null) return null;\n if (endplatePos.has(nodeId)) return endplatePos.get(nodeId)!;\n if (turnoutPos.has(nodeId)) return turnoutPos.get(nodeId)!;\n return null;\n };\n\n const doubleMain = doc.endplates.some((e) =>\n e.tracks?.some((t) => t.config === \"double\"),\n );\n\n // The lane a track diverges from = the main its turnout sits on.\n const divergeOrigin = (trackId: string): number => {\n const sw = (doc.turnouts ?? []).find((t) => t.divergeTrack === trackId);\n return sw ? (trackLane.get(sw.onTrack) ?? 0) : 0;\n };\n\n // Turnouts grouped by the track they diverge onto (a track's throat/hand).\n const turnoutsByTrack = new Map<string, SchematicTurnout[]>();\n for (const sw of doc.turnouts ?? []) {\n const arr = turnoutsByTrack.get(sw.divergeTrack) ?? [];\n arr.push(sw);\n turnoutsByTrack.set(sw.divergeTrack, arr);\n }\n // A crossover is a connector whose turnouts sit on two different lanes (one on\n // each main) — drawn as a diagonal between them, never as a lane-paralleling\n // siding. A passing siding's two turnouts share one lane, so it isn't one.\n const isCrossover = (trackId: string): boolean => {\n const sws = turnoutsByTrack.get(trackId) ?? [];\n if (sws.length < 2) return false;\n return new Set(sws.map((s) => trackLane.get(s.onTrack) ?? 0)).size >= 2;\n };\n // The in-inches extent of a track (explicit fromPos/toPos or node lookup).\n const extentOf = (t: SchematicTrack): [number, number] | null => {\n const from = t.fromPos ?? posOf(t.from);\n const to = t.toPos ?? posOf(t.to);\n return from == null || to == null ? null : [from, to];\n };\n\n // Reconcile each diverging track's drawn SIDE from its turnout's hand — `kind`\n // is the source of truth (#bug1) — resolved TOPOLOGICALLY. The hand only picks\n // a side for a turnout sitting on the main CENTERLINE (lane 0); a track\n // diverging off any other track (Main 2, or a ladder rung) stays on its\n // PARENT's side and stacks outward — a ladder turnout's hand is relative to\n // its parent, not the main, so it must never flip a rung across the main. The\n // stored lane's magnitude (stacking slot) is always kept.\n const trackById = new Map(doc.tracks.map((t) => [t.id, t]));\n const resolvedLanes = new Map<string, number>();\n const resolving = new Set<string>();\n const resolveLane = (id: string): number => {\n const trk = trackById.get(id);\n if (!trk) return 0;\n if (trk.role === \"main\") return trk.lane; // mains are fixed (0, 1, …)\n if (resolvedLanes.has(id)) return resolvedLanes.get(id)!;\n if (resolving.has(id)) return trk.lane; // cycle guard\n resolving.add(id);\n let lane = trk.lane;\n const sw = turnoutsByTrack.get(id)?.[0];\n const ext = extentOf(trk);\n if (sw && (sw.kind === \"left\" || sw.kind === \"right\") && !isCrossover(id) && ext) {\n const parentLane = resolveLane(sw.onTrack);\n let sign: number;\n if (parentLane === 0) {\n // On the main centerline — the hand chooses above/below.\n const [from, to] = ext;\n const far = Math.abs(to - sw.pos) >= Math.abs(from - sw.pos) ? to : from;\n const s = divergeSideForHand(sw.kind, far - sw.pos);\n sign = s !== 0 ? s : Math.sign(trk.lane) || 1;\n } else {\n // Off Main 2 / a ladder rung — follow the parent's side.\n sign = Math.sign(parentLane) || 1;\n }\n lane = sign * Math.abs(trk.lane);\n }\n resolving.delete(id);\n resolvedLanes.set(id, lane);\n return lane;\n };\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue;\n trackLane.set(t.id, resolveLane(t.id));\n }\n\n const extraTracks: DrawTrack[] = [];\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue; // the spine draws mains\n if (isCrossover(t.id)) continue; // crossovers draw as diagonals, below\n const ext = extentOf(t);\n if (!ext) continue; // can't place it\n const [from, to] = ext;\n // Throat = the end nearest this track's turnout (the join to the main);\n // with no turnout, keep entry order (from = throat) so the author's chosen\n // joining end is honoured. This is what makes an east-facing spur draw its\n // throat on the east instead of always west (#bug3).\n const sw = turnoutsByTrack.get(t.id)?.[0];\n const throatAtTo = sw != null && Math.abs(to - sw.pos) < Math.abs(from - sw.pos);\n const throat = throatAtTo ? to : from;\n const stub = throatAtTo ? from : to;\n extraTracks.push({\n id: t.id,\n role: t.role,\n lane: trackLane.get(t.id) ?? t.lane,\n fromFrac: clampFrac(Math.min(from, to)),\n toFrac: clampFrac(Math.max(from, to)),\n throatFrac: clampFrac(throat),\n stubFrac: clampFrac(stub),\n capacityFeet: t.capacityFeet ?? null,\n divergesFromLane: divergeOrigin(t.id),\n inLoop: t.inLoop === true,\n });\n }\n\n // Crossovers — a diagonal between two parallel mains. Two shapes are drawn:\n const crossovers: DrawCrossover[] = [];\n // (1) a dedicated connector track (buildCrossover) with a turnout on each main.\n for (const t of doc.tracks) {\n if (!isCrossover(t.id)) continue;\n const [s1, s2] = turnoutsByTrack.get(t.id)!;\n crossovers.push({\n id: t.id,\n name: t.trackName ?? \"\",\n fromPosFrac: clampFrac(s1.pos),\n fromLane: trackLane.get(s1.onTrack) ?? 0,\n toPosFrac: clampFrac(s2.pos),\n toLane: trackLane.get(s2.onTrack) ?? 1,\n });\n }\n // (2) modelled as two turnouts each diverging onto the OTHER main (no connector\n // track). Pair a leg with its return leg and connect the two turnout points.\n const isMainId = (id?: string) => !!id && trackById.get(id)?.role === \"main\";\n const m2m = (doc.turnouts ?? []).filter(\n (sw) =>\n isMainId(sw.onTrack) &&\n isMainId(sw.divergeTrack) &&\n (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1),\n );\n const usedLegs = new Set<string>();\n for (const t1 of m2m) {\n if (usedLegs.has(t1.id)) continue;\n usedLegs.add(t1.id);\n const t2 = m2m.find(\n (x) =>\n !usedLegs.has(x.id) &&\n x.onTrack === t1.divergeTrack &&\n x.divergeTrack === t1.onTrack,\n );\n if (t2) usedLegs.add(t2.id);\n const end = t2 ?? t1; // lone leg → a straight rung across at its own pos\n crossovers.push({\n id: t2 ? `${t1.id}-${t2.id}` : t1.id,\n name: t1.name ?? t2?.name ?? \"\",\n fromPosFrac: clampFrac(t1.pos),\n fromLane: trackLane.get(t1.onTrack) ?? 0,\n toPosFrac: clampFrac(end === t1 ? t1.pos : end.pos),\n toLane: trackLane.get(t1.divergeTrack) ?? 1,\n });\n }\n\n const turnouts: DrawTurnout[] = (doc.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n posFrac: clampFrac(t.pos),\n onLane: trackLane.get(t.onTrack) ?? 0,\n divergeLane: trackLane.get(t.divergeTrack) ?? 1,\n }));\n\n const drawSignal = (\n s: SchematicSignal,\n name: string,\n cp?: string,\n ): DrawSignal => ({\n id: s.id,\n name,\n posFrac: clampFrac(s.pos),\n lane: s.track ? (trackLane.get(s.track) ?? 0) : 0,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: s.side === \"below\" ? \"below\" : \"above\",\n ...(cp ? { cp } : {}),\n stack: 0,\n });\n // Signals come from control-point groups; fall back to pre-grouping flat\n // signals for docs authored before the model changed.\n const signals: DrawSignal[] = Array.isArray(doc.controlPoints)\n ? doc.controlPoints.flatMap((c) =>\n (c.signals ?? []).map((s) => drawSignal(s, c.name ?? \"\", c.id)),\n )\n : (doc.signals ?? []).map((s) => drawSignal(s, s.name ?? \"\"));\n // De-collide: signals landing on the exact same lane+side+position get a\n // rising stack rank so a renderer can fan them out (a control point often\n // carries several signals at one interlocking).\n const stackCount = new Map<string, number>();\n for (const s of signals) {\n const key = `${s.lane}|${s.side}|${Math.round(s.posFrac * 1000)}`;\n const n = stackCount.get(key) ?? 0;\n s.stack = n;\n stackCount.set(key, n + 1);\n }\n\n const crossings: DrawCrossing[] = (doc.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n posFrac: clampFrac(x.pos),\n laneA: trackLane.get(x.tracks?.[0] ?? \"\") ?? 0,\n laneB: trackLane.get(x.tracks?.[1] ?? \"\") ?? 1,\n }));\n\n // Branch endplates (any beyond A/B with a placement) → connector arrows.\n const branchConnectors: BranchConnector[] = doc.endplates\n .filter((e) => e.id !== \"A\" && e.id !== \"B\" && e.at)\n .map((e) => ({\n id: e.id,\n label: e.label ?? e.id,\n posFrac: clampFrac(e.at!.pos),\n side: e.at!.side === \"down\" ? \"down\" : \"up\",\n }));\n\n const allLanes = [\n 0,\n doubleMain ? 1 : 0,\n ...extraTracks.map((t) => t.lane),\n ...signals.map((s) => s.lane),\n ...crossings.flatMap((x) => [x.laneA, x.laneB]),\n ...crossovers.flatMap((x) => [x.fromLane, x.toLane]),\n ];\n const loop = isLoopDoc(doc);\n // A positioned Main 2 = a transition module (partial second main).\n const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);\n const main2Positioned =\n !!main2 && (main2.fromPos != null || main2.toPos != null) && !loop;\n const main2Extent = main2Positioned\n ? {\n fromFrac: clampFrac(main2!.fromPos ?? 0),\n toFrac: clampFrac(main2!.toPos ?? len),\n }\n : null;\n return {\n doubleMain,\n loop,\n main2Extent,\n loopInterchange: loop && doc.endplates.filter((e) => !e.at).length >= 2,\n loopReturn: loop && doc.loopReturn === \"main2\" ? \"main2\" : \"same\",\n loopRender: loop ? (doc.loopRender ?? null) : null,\n extraTracks,\n turnouts,\n signals,\n crossings,\n crossovers,\n branchConnectors,\n laneMin: Math.min(...allLanes),\n laneMax: Math.max(...allLanes),\n };\n}\n\n// ---- Endplate geometry & poses (#175) --------------------------------------\n//\n// Free-moN endplates are one standard interface; a single (x, y, heading) at\n// the track-crossing point fully determines mating (any endplate ↔ any\n// compatible endplate). Poses are in MODULE-LOCAL inches: endplate A's track\n// point at the origin, its OUTWARD normal pointing west (180°), so the module\n// body runs toward +X. A layout composes modules by walking joins and stacking\n// each pose's rigid transform (rotation + optional reflection for flips).\n//\n// Poses are DERIVED from the simple fields owners already enter — length +\n// geometry type/degrees/offset for the two axial endplates, plus the schematic\n// doc's branch endplates (#170). Complex shapes (wye, loop, other) fall back to\n// hand-entered overrides. Nothing is stored unless overridden.\n\nexport type GeometryType =\n | \"straight\"\n | \"corner_45\"\n | \"corner_90\"\n | \"curve\"\n | \"offset\"\n | \"dead_end\"\n | \"wye\"\n | \"other\";\n\nexport interface EndplatePose {\n /** Endplate id — \"A\"/\"B\" axial, \"C\"/\"D\"… branch. */\n id: string;\n /** Module-local inches; endplate A's track point is the origin. */\n x: number;\n y: number;\n /** Outward normal in degrees (0 = +X east, 90 = +Y north). A neighbour mates\n * facing the opposite heading. */\n heading: number;\n trackConfig: \"single\" | \"double\";\n /** Lateral track offsets from the crossing anchor (0 = centred single). */\n trackOffsets: number[];\n /** True when the pose was hand-entered, not derived (wye/loop/other). */\n manual?: boolean;\n}\n\nexport interface ModuleGeometryInput {\n lengthInches: number;\n geometryType?: string | null;\n geometryDegrees?: number | null;\n geometryOffsetInches?: number | null;\n /** Axial endplate configs (A first, then B). Missing → single. */\n endplateConfigs?: (\"single\" | \"double\" | null | undefined)[];\n /** Branch endplates (from the schematic doc, #170), positioned along the\n * mainline axis. */\n branches?: {\n id: string;\n atPos: number;\n side: \"up\" | \"down\";\n config?: \"single\" | \"double\" | null;\n }[];\n /** Hand-entered pose overrides by endplate id — win over derivation. */\n poseOverrides?: Record<string, { x: number; y: number; heading: number }>;\n /** Half the spacing between the two tracks of a double endplate (Free-mo ≈ 1\",\n * Free-moN ≈ 9/16\"). */\n trackHalfSpacingInches?: number;\n}\n\n/** Signed turn a module applies to the through track (CCW/left positive). */\nexport function geometryTurnDegrees(\n geometryType?: string | null,\n geometryDegrees?: number | null,\n): number {\n switch (geometryType) {\n case \"corner_45\":\n return 45;\n case \"corner_90\":\n return 90;\n case \"curve\":\n return geometryDegrees ?? 0;\n default:\n return 0;\n }\n}\n\nconst DEG = Math.PI / 180;\nconst norm360 = (d: number) => ((d % 360) + 360) % 360;\n\nfunction offsetsFor(\n config: \"single\" | \"double\",\n half: number,\n): number[] {\n return config === \"double\" ? [-half, half] : [0];\n}\n\n/**\n * Derive every endplate's module-local pose. A at the origin facing west; B\n * placed by the module's geometry (straight/offset/corner/curve via a\n * constant-radius arc = arc-length `lengthInches`, turning by the geometry\n * angle); branch endplates positioned along the mainline axis facing out their\n * side. A `dead_end`/loop module has no B. Overrides replace any derived pose.\n */\nexport function deriveEndplatePoses(geo: ModuleGeometryInput): EndplatePose[] {\n const L = geo.lengthInches > 0 ? geo.lengthInches : 24;\n const half = geo.trackHalfSpacingInches ?? 1;\n const cfg = (i: number): \"single\" | \"double\" =>\n geo.endplateConfigs?.[i] === \"double\" ? \"double\" : \"single\";\n const withOverride = (p: EndplatePose): EndplatePose => {\n const o = geo.poseOverrides?.[p.id];\n return o ? { ...p, x: o.x, y: o.y, heading: norm360(o.heading), manual: true } : p;\n };\n\n const poses: EndplatePose[] = [];\n\n // Endplate A — origin, outward normal west.\n poses.push(\n withOverride({\n id: \"A\",\n x: 0,\n y: 0,\n heading: 180,\n trackConfig: cfg(0),\n trackOffsets: offsetsFor(cfg(0), half),\n }),\n );\n\n // Endplate B — unless the module is a dead end / turnback (single endplate).\n const noB = geo.geometryType === \"dead_end\";\n if (!noB) {\n const turn = geometryTurnDegrees(geo.geometryType, geo.geometryDegrees);\n let bx: number;\n let by: number;\n let bHeading: number;\n if (geo.geometryType === \"offset\") {\n // Parallel endplates, jogged sideways over the run.\n bx = L;\n by = geo.geometryOffsetInches ?? 0;\n bHeading = 0;\n } else if (turn === 0) {\n bx = L;\n by = 0;\n bHeading = 0;\n } else {\n // Constant-radius arc of arc-length L turning `turn` (CCW/left).\n const t = turn * DEG;\n const r = L / t;\n bx = r * Math.sin(t);\n by = r * (1 - Math.cos(t));\n bHeading = turn;\n }\n poses.push(\n withOverride({\n id: \"B\",\n x: bx,\n y: by,\n heading: norm360(bHeading),\n trackConfig: cfg(1),\n trackOffsets: offsetsFor(cfg(1), half),\n }),\n );\n }\n\n // Branch endplates — at their along-axis position, facing out their side.\n // Position along the (possibly curved) mainline is approximated on the A→B\n // chord; the join solver refines with overrides where a module needs it.\n for (const b of geo.branches ?? []) {\n const frac = L > 0 ? Math.min(1, Math.max(0, b.atPos / L)) : 0;\n const px = frac * L;\n const config = b.config === \"double\" ? \"double\" : \"single\";\n poses.push(\n withOverride({\n id: b.id,\n x: px,\n y: 0,\n heading: b.side === \"down\" ? 270 : 90,\n trackConfig: config,\n trackOffsets: offsetsFor(config, half),\n }),\n );\n }\n\n return poses;\n}\n\n/** Whether a module shape's poses are fully derivable, or need manual entry\n * (a helpful cue for the authoring UI). */\nexport function poseNeedsManual(geometryType?: string | null): boolean {\n return geometryType === \"wye\" || geometryType === \"other\";\n}\n\n/** Extract manual pose overrides from a schematic doc's endplates (#175 phase\n * 1b) — the map deriveEndplatePoses / a footprint solver feeds as overrides. */\nexport function poseOverridesFromDoc(\n doc: ModuleSchematicDoc,\n): Record<string, { x: number; y: number; heading: number }> {\n const out: Record<string, { x: number; y: number; heading: number }> = {};\n for (const e of doc.endplates ?? []) {\n if (\n e.id &&\n e.pose &&\n typeof e.pose.x === \"number\" &&\n typeof e.pose.y === \"number\" &&\n typeof e.pose.heading === \"number\"\n ) {\n out[e.id] = { x: e.pose.x, y: e.pose.y, heading: e.pose.heading };\n }\n }\n return out;\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @willcgage/module-schematic — the shared module operations-schematic (track-graph)\n * that the Module Repository authors and Free-Dispatcher imports.\n *\n * Topological, straightened-first: positions are 1-D inches along the module\n * (from endplate A), lanes are integer track indices (0 = primary main). This is\n * the single source of truth for both apps — the doc types, the lenient parser\n * (docs arrive as jsonb / unknown), the pure feature resolver both renderers\n * draw, the N-scale helpers, and the editor <-> doc state machine an authoring\n * UI binds to. See docs/module-schematic-format.md in the free-dispatcher repo.\n *\n * Framework-agnostic and side-effect-free so it can be unit-tested and consumed\n * from Next.js (server + client) and Electron alike.\n */\n\nexport type TrackConfig = \"single\" | \"double\";\nexport type TrackRole = \"main\" | \"siding\" | \"spur\" | \"yard\" | \"crossover\";\nexport type TurnoutKind = \"left\" | \"right\" | \"wye\";\nexport type SignalFacing = \"AtoB\" | \"BtoA\";\nexport type SignalSide = \"above\" | \"below\";\n\nexport interface SchematicEndplateTrack {\n trackId: string;\n lane: number;\n config?: TrackConfig | null;\n}\nexport interface SchematicEndplate {\n id: string; // \"A\" (West) | \"B\" (East) | \"C\"… (branch, #170)\n label?: string | null;\n tracks?: SchematicEndplateTrack[];\n /** Branch endplates (#170): where a 3rd+ endplate sits — pos inches from A,\n * on the up (north/above) or down side. Absent = axial (A at 0, B at\n * lengthInches). Renderers draw a named connector arrow (the CATS/US&S\n * off-band idiom) until branch spines land. */\n at?: { pos: number; side: \"up\" | \"down\" };\n /** Manual pose override (#175 phase 1b) — the endplate's module-local track\n * point (x, y inches) + outward-normal heading (°). Hand-entered for shapes\n * the geometry fields can't derive (wye, freeform, loop); wins over\n * deriveEndplatePoses' derivation. */\n pose?: { x: number; y: number; heading: number };\n}\nexport interface SchematicTrack {\n id: string;\n role: TrackRole;\n lane: number;\n from?: string;\n to?: string;\n fromPos?: number | null;\n toPos?: number | null;\n capacityFeet?: number | null;\n industryRef?: number | null;\n /** The module_tracks row this track is (single source of truth); null = new. */\n moduleTrackId?: number | null;\n /** Owner's track name, mirrored to module_tracks.track_name. */\n trackName?: string;\n /** Inside the balloon of a loop module (#165): pos still measures from\n * endplate A (past the throat = in the loop), lane is the ladder/arc index —\n * one record drives both the unrolled fan and a geometric render. */\n inLoop?: boolean;\n}\nexport interface SchematicTurnout {\n id: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind?: TurnoutKind;\n name?: string | null;\n address?: string | null;\n}\nexport interface SchematicSignal {\n id: string;\n pos: number;\n /** Track the signal governs; absent = the primary main (lane 0). */\n track?: string;\n facing?: SignalFacing;\n kind?: \"mast\" | \"dwarf\";\n name?: string | null;\n aspects?: string[];\n /** Which side of the track the signal sits on (#122). */\n side?: SignalSide;\n /** Turnout this control point governs; absent = standalone block signal. */\n turnout?: string;\n}\nexport interface SchematicBlock {\n id: string;\n name: string;\n tracks?: string[];\n from: number;\n to: number;\n}\n/** A grade crossing / diamond (#170): two tracks cross with no route choice —\n * a conflict node, not a turnout. A connected diamond adds normal turnouts\n * alongside. Drawn as an X between the two tracks' lanes. */\nexport interface SchematicCrossing {\n id: string;\n pos: number;\n /** The two crossing tracks, by id. */\n tracks: [string, string];\n name?: string | null;\n}\n/**\n * A control point is an interlocking: a named group of one or more signals and\n * zero or more turnouts. A passing siding has two (West/East); a lone block\n * signal is a control point with one signal and no turnouts.\n */\nexport interface SchematicControlPoint {\n id: string;\n name?: string | null;\n turnouts?: string[];\n /** Crossings this interlocking protects (#170). */\n crossings?: string[];\n signals?: SchematicSignal[];\n}\nexport interface ModuleSchematicDoc {\n version: number;\n module?: string;\n lengthInches?: number;\n /** Single-endplate turnback (balloon loop): the main enters at A, runs the\n * lead, and turns back — renderers draw a terminal bulb at the far end\n * instead of a second endplate. Also implied by a single-entry endplates\n * array (a category:\"loop\" module like Seaford). */\n loop?: boolean;\n /** Where the balloon returns (#165): \"same\" (default) turns back onto the\n * same main; \"main2\" is a directional return on a double-track main —\n * out on Main 1, back on Main 2 — drawn as a U joining the two lanes\n * (the transit terminal-loop idiom). */\n loopReturn?: \"same\" | \"main2\";\n /** Optional rendering override: \"bulb\" (abstract terminal), \"fan\" (interior\n * tracks unrolled as a ladder past the throat — the default when inLoop\n * tracks exist), \"geometric\" (drawn balloon, AL&E-style). */\n loopRender?: \"bulb\" | \"fan\" | \"geometric\";\n endplates: SchematicEndplate[];\n tracks: SchematicTrack[];\n turnouts?: SchematicTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings?: SchematicCrossing[];\n controlPoints?: SchematicControlPoint[];\n /** @deprecated pre-grouping flat signals; read for back-compat. */\n signals?: SchematicSignal[];\n}\n\n/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */\nexport function isLoopDoc(doc: ModuleSchematicDoc): boolean {\n return doc.loop === true || doc.endplates.length === 1;\n}\n\nexport const MAIN_TRACK_ID = \"main\";\n/** The second main on double-track modules — a real track entity so turnouts\n * and signals can attach to it (a spur off Main 2 must diverge from lane 1,\n * not draw a crossover from Main 1). Emitted by stateToDoc when either\n * endplate is double; legacy docs without it still parse. */\nexport const MAIN2_TRACK_ID = \"main2\";\n\n// North American N scale (1:160): 396 real inches → 5280 scale feet = one mile.\nexport const N_SCALE_RATIO = 160;\n/** Real inches on the module → scale feet of prototype track represented. */\nexport function inchesToScaleFeet(inches: number, ratio = N_SCALE_RATIO): number {\n return (inches * ratio) / 12;\n}\n/** Scale feet of prototype track → real inches on the module. */\nexport function scaleFeetToInches(feet: number, ratio = N_SCALE_RATIO): number {\n return (feet * 12) / ratio;\n}\n\n/** Parse a jsonb value into a schematic doc, or null if it isn't one. */\nexport function asModuleSchematic(x: unknown): ModuleSchematicDoc | null {\n if (!x || typeof x !== \"object\") return null;\n const d = x as Record<string, unknown>;\n if (typeof d.version !== \"number\") return null;\n if (!Array.isArray(d.endplates) || !Array.isArray(d.tracks)) return null;\n return d as unknown as ModuleSchematicDoc;\n}\n\n// ---- Editor state (a flatter shape an authoring form binds to) -------------\n\nexport interface EditorTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromPos: number;\n toPos: number;\n /** module_tracks row id (single source of truth), or null for a new track. */\n moduleTrackId: number | null;\n /** Owner's track name → module_tracks.track_name. */\n trackName: string;\n /** Inside the balloon of a loop module (#165). */\n inLoop?: boolean;\n}\n\n/** A module_tracks row as loaded for the editor. */\nexport interface ModuleTrackRow {\n id: number;\n track_name: string | null;\n capacity_scale_feet: number | null;\n}\nexport interface EditorTurnout {\n id: string;\n name: string;\n pos: number;\n onTrack: string;\n divergeTrack: string;\n kind: TurnoutKind;\n}\nexport interface EditorCpSignal {\n id: string;\n pos: number;\n track: string;\n facing: SignalFacing;\n side: SignalSide;\n}\nexport interface EditorControlPoint {\n id: string;\n name: string;\n turnouts: string[]; // turnout ids grouped under this control point\n /** Crossing ids this interlocking protects (#170). */\n crossings?: string[];\n signals: EditorCpSignal[];\n}\nexport interface EditorCrossing {\n id: string;\n name: string;\n pos: number;\n trackA: string;\n trackB: string;\n}\n/** A 3rd+ endplate — a branch/junction connection off the module (#170).\n * A module may have several (e.g. a set carrying a second railroad through:\n * MoPac enters at one branch endplate and leaves at another). */\nexport interface EditorBranch {\n label: string;\n pos: number;\n side: \"up\" | \"down\";\n config: TrackConfig;\n}\n/** Endplate B on a loop module: a standard endplate makes the balloon an\n * INTERCHANGE (a second route connects at the loop, e.g. Seaford); \"none\"\n * makes it a pure turnback. Non-loop modules always have a real B. */\nexport type EndplateBConfig = TrackConfig | \"none\";\n\nexport interface EditorState {\n lengthInches: number;\n /** Balloon loop: the main runs the lead and turns back; positions past the\n * throat are inside the balloon. Endplate B stays independently available —\n * present = interchange loop, \"none\" = pure turnback. */\n loop: boolean;\n /** Where the balloon returns: same main, or Main 2 (directional return on a\n * double-track main — drawn as a U joining the two lanes). */\n loopReturn: \"same\" | \"main2\";\n configA: TrackConfig;\n configB: EndplateBConfig;\n extraTracks: EditorTrack[]; // sidings/spurs/…; the main track is implicit\n turnouts: EditorTurnout[];\n /** Grade crossings / diamonds (#170). */\n crossings: EditorCrossing[];\n /** Branch endplates C, D, … — junction connections (#170); empty = through\n * module. Emitted in order as endplates \"C\", \"D\", \"E\"… */\n branches: EditorBranch[];\n /** Manual endplate pose overrides by endplate id (#175 phase 1b). */\n poseOverrides: Record<string, { x: number; y: number; heading: number }>;\n controlPoints: EditorControlPoint[];\n}\n\n/** Build the empty editor state for a module of the given length. */\nexport function emptyEditorState(lengthInches: number): EditorState {\n return {\n lengthInches: lengthInches > 0 ? lengthInches : 24,\n loop: false,\n loopReturn: \"same\",\n configA: \"single\",\n configB: \"single\",\n extraTracks: [],\n turnouts: [],\n crossings: [],\n branches: [],\n poseOverrides: {},\n controlPoints: [],\n };\n}\n\n/** Main 2's doc track for a non-loop double module. Full length when both\n * endplates are double; on a transition module (one single, one double) it\n * runs between the mainline transition turnout (the one diverging to main2)\n * and the double end. */\n/**\n * A single↔double transition turnout connects Main 1 and Main 2 — where the\n * second main begins/ends. It may be authored either way round: on Main 1\n * diverging to Main 2, or on Main 2 diverging to Main 1 (both describe the same\n * junction). Recognise both so editing the direction doesn't drop the transition.\n */\nexport function isTransitionTurnout(t: {\n onTrack?: string;\n divergeTrack?: string;\n}): boolean {\n return (\n (t.onTrack === MAIN_TRACK_ID && t.divergeTrack === MAIN2_TRACK_ID) ||\n (t.onTrack === MAIN2_TRACK_ID && t.divergeTrack === MAIN_TRACK_ID)\n );\n}\n\nfunction main2Track(state: EditorState): SchematicTrack {\n const bothDouble = state.configA === \"double\" && state.configB === \"double\";\n const sw = state.turnouts.find(isTransitionTurnout);\n // Main 2 runs partial only when IT is the branch that ends (turnout diverges\n // TO Main 2). If the turnout sits ON Main 2 (Main 2 is the surviving through\n // main, #FMN-0043), Main 2 runs full and Main 1 is the one that ends.\n if (bothDouble || !sw || sw.divergeTrack !== MAIN2_TRACK_ID) {\n return { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, from: \"A\", to: \"B\" };\n }\n return state.configA === \"double\"\n ? // Double at A: Main 2 runs from A and ends at the turnout.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: 0, toPos: sw.pos }\n : // Double at B: Main 2 begins at the turnout and runs to B.\n { id: MAIN2_TRACK_ID, role: \"main\", lane: 1, fromPos: sw.pos, toPos: state.lengthInches };\n}\n\n/**\n * Build the single↔double transition as one unit (like buildPassingSiding):\n * the mainline turnout where Main 2 begins/ends, grouped in a control point —\n * an \"End of Double Track\" is a classic CTC interlocking — with signals both\n * directions. Returns items to merge into the editor state.\n */\nexport function buildTransition(state: EditorState): {\n turnout: EditorTurnout;\n controlPoint: EditorControlPoint;\n} | null {\n const aDouble = state.configA === \"double\";\n const bDouble = state.configB === \"double\";\n if (state.loop || aDouble === bDouble) return null; // not a transition module\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.25));\n // The turnout sits toward the single end so the double track carries most\n // of the module; owner adjusts the position afterwards.\n const pos = aDouble ? len - inset : inset;\n\n const swId = nextId(\"sw\", state.turnouts.map((t) => t.id));\n const turnout: EditorTurnout = {\n id: swId,\n name: \"End of Double Track\",\n pos,\n // The turnout sits ON the ending main (Main 2, the upper track) and diverges\n // down to the continuous Main 1 — the modeller's view of the junction. Left\n // hand when the double end is west (Main 2 comes down going east), right\n // when it's east (mirror).\n onTrack: MAIN2_TRACK_ID,\n divergeTrack: MAIN_TRACK_ID,\n kind: aDouble ? \"left\" : \"right\",\n };\n\n const cpId = nextId(\"cp\", state.controlPoints.map((c) => c.id));\n const sig = (facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoint: EditorControlPoint = {\n id: cpId,\n name: \"End of Double Track\",\n turnouts: [swId],\n signals: [sig(\"AtoB\"), sig(\"BtoA\")],\n };\n return { turnout, controlPoint };\n}\n\n/** Attach manual pose overrides (#175 phase 1b) to endplates by id. */\nfunction withPoses(\n endplates: SchematicEndplate[],\n overrides: Record<string, { x: number; y: number; heading: number }>,\n): SchematicEndplate[] {\n return endplates.map((e) =>\n overrides[e.id] ? { ...e, pose: overrides[e.id] } : e,\n );\n}\n\n/** Assemble a spec-conformant doc from the editor state. */\nexport function stateToDoc(\n state: EditorState,\n recordNumber: string,\n): ModuleSchematicDoc {\n return {\n version: 1,\n module: recordNumber,\n lengthInches: state.lengthInches,\n ...(state.loop ? { loop: true } : {}),\n ...(state.loop && state.loopReturn === \"main2\" ? { loopReturn: \"main2\" as const } : {}),\n endplates: withPoses(\n [\n ...(state.loop\n ? // Balloon loop: A is the entry. A standard endplate B on the balloon\n // makes it an INTERCHANGE (second route connects at the loop, e.g.\n // Seaford); configB \"none\" makes it a pure turnback.\n [\n { id: \"A\", label: \"Entry\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n ...(state.configB !== \"none\"\n ? [{ id: \"B\", label: \"Interchange\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }]\n : []),\n ]\n : [\n { id: \"A\", label: \"West\", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },\n // Non-loop modules always have a real B (\"none\" never applies).\n {\n id: \"B\",\n label: \"East\",\n tracks: [\n {\n trackId: MAIN_TRACK_ID,\n lane: 0,\n config: state.configB === \"none\" ? \"single\" : state.configB,\n },\n ],\n },\n ]),\n // Branch endplates C, D, … — junction connections at pos, off one side\n // (#170). A set can carry several (e.g. a second railroad through).\n ...state.branches.map((b, i) => ({\n id: String.fromCharCode(67 + i), // C, D, E…\n label: b.label || `Branch ${i + 1}`,\n tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],\n at: { pos: b.pos, side: b.side },\n })),\n ],\n state.poseOverrides,\n ),\n tracks: [\n state.loop\n ? // The main runs the lead from A and turns back at the balloon.\n { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, fromPos: 0, toPos: state.lengthInches }\n : { id: MAIN_TRACK_ID, role: \"main\" as const, lane: 0, from: \"A\", to: \"B\" },\n // Double track: Main 2 is a real entity so turnouts/signals can attach.\n // On a loop it exists only for a Main 2 directional return (the U joins\n // the two lanes at the balloon); a same-main loop's parallel lead legs\n // are ONE main. On a TRANSITION module (one endplate single, the other\n // double) Main 2 only runs from the mainline turnout to the double end —\n // the turnout that diverges to main2 is the single source of truth for\n // where the transition sits (fd#175 / FMN-0038).\n ...(!state.loop && (state.configA === \"double\" || state.configB === \"double\")\n ? [main2Track(state)]\n : []),\n ...(state.loop && state.loopReturn === \"main2\"\n ? [{ id: MAIN2_TRACK_ID, role: \"main\" as const, lane: 1, fromPos: 0, toPos: state.lengthInches }]\n : []),\n ...state.extraTracks.map((t) => ({\n id: t.id,\n role: t.role,\n lane: t.lane,\n fromPos: t.fromPos,\n toPos: t.toPos,\n moduleTrackId: t.moduleTrackId,\n trackName: t.trackName || undefined,\n capacityFeet: Math.round(inchesToScaleFeet(Math.abs(t.toPos - t.fromPos))),\n ...(state.loop && t.inLoop ? { inLoop: true } : {}),\n })),\n ],\n turnouts: state.turnouts.map((t) => ({\n id: t.id,\n pos: t.pos,\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: t.kind,\n name: t.name || undefined,\n })),\n ...(state.crossings.length > 0\n ? {\n crossings: state.crossings.map((x) => ({\n id: x.id,\n pos: x.pos,\n tracks: [x.trackA, x.trackB] as [string, string],\n name: x.name || undefined,\n })),\n }\n : {}),\n controlPoints: state.controlPoints.map((c) => ({\n id: c.id,\n name: c.name,\n turnouts: c.turnouts,\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: c.signals.map((s) => ({\n id: s.id,\n pos: s.pos,\n track: s.track,\n facing: s.facing,\n kind: \"mast\" as const,\n side: s.side,\n })),\n })),\n };\n}\n\n/**\n * Derive editor state from the doc and the module's Track section rows. Tracks\n * are the single source of truth for name/capacity (module_tracks), while the\n * schematic doc adds geometry (lane, positions). We merge: doc tracks first\n * (they carry geometry + their moduleTrackId link), then any module_tracks not\n * yet positioned in the schematic.\n */\nexport function docToState(\n doc: unknown,\n fallbackLength: number,\n moduleTracks: ModuleTrackRow[] = [],\n): EditorState {\n const base = emptyEditorState(fallbackLength);\n const d =\n doc && typeof doc === \"object\" ? (doc as ModuleSchematicDoc) : null;\n const hasDoc = !!d && typeof d.lengthInches === \"number\" && Array.isArray(d.tracks);\n // The module's length is authoritative (the mainline is the module). If the\n // saved doc used a different length, rescale its feature positions to fit so\n // the mainline always reads as the module's true length.\n const len = fallbackLength > 0 ? fallbackLength : hasDoc ? d!.lengthInches! : 24;\n const docLen = hasDoc && d!.lengthInches! > 0 ? d!.lengthInches! : len;\n const scale = docLen > 0 ? len / docLen : 1;\n const sc = (p: number) => Math.round(p * scale);\n\n const nameOf = (id: number | null | undefined): string => {\n const mt = id != null ? moduleTracks.find((m) => m.id === id) : undefined;\n return mt?.track_name ?? \"\";\n };\n\n const extraTracks: EditorTrack[] = [];\n const usedMt = new Set<number>();\n if (hasDoc) {\n for (const t of d!.tracks) {\n if (t.role === \"main\") continue;\n const moduleTrackId = t.moduleTrackId ?? null;\n if (moduleTrackId != null) usedMt.add(moduleTrackId);\n extraTracks.push({\n id: t.id,\n role: (t.role as TrackRole) ?? \"siding\",\n lane: t.lane ?? 1,\n fromPos: sc(t.fromPos ?? 0),\n toPos: t.toPos != null ? sc(t.toPos) : len,\n moduleTrackId,\n trackName: t.trackName ?? nameOf(moduleTrackId),\n ...(t.inLoop ? { inLoop: true } : {}),\n });\n }\n }\n // Link pre-migration doc tracks (no moduleTrackId yet) to unused module_tracks\n // by order — keeping the doc track's id so turnout/signal references stay\n // valid. Only after that do leftover module_tracks become new tracks.\n const unused = moduleTracks.filter((mt) => !usedMt.has(mt.id));\n let ui = 0;\n for (const et of extraTracks) {\n if (et.moduleTrackId == null && ui < unused.length) {\n const mt = unused[ui++];\n et.moduleTrackId = mt.id;\n if (!et.trackName) et.trackName = mt.track_name ?? \"\";\n usedMt.add(mt.id);\n }\n }\n let lane = Math.max(0, ...extraTracks.map((t) => t.lane));\n for (const mt of moduleTracks) {\n if (usedMt.has(mt.id)) continue;\n lane += 1;\n extraTracks.push({\n id: `mt${mt.id}`,\n role: \"siding\",\n lane,\n fromPos: Math.round(len * 0.2),\n toPos: Math.round(len * 0.8),\n moduleTrackId: mt.id,\n trackName: mt.track_name ?? \"\",\n });\n }\n\n if (!hasDoc) return { ...base, lengthInches: len, extraTracks };\n\n const configOf = (id: string): TrackConfig => {\n const ep = (d!.endplates ?? []).find((e) => e.id === id);\n return ep?.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\";\n };\n const loop = isLoopDoc(d!);\n const hasB = (d!.endplates ?? []).some((e) => e.id === \"B\");\n // Branch endplates C, D, … (junction connections, #170).\n const branchEps = (d!.endplates ?? []).filter(\n (e) => e.id !== \"A\" && e.id !== \"B\" && e.at,\n );\n const poseOverrides = poseOverridesFromDoc(d!);\n return {\n lengthInches: len,\n loop,\n loopReturn: loop && d!.loopReturn === \"main2\" ? \"main2\" : \"same\",\n configA: configOf(\"A\"),\n // On a loop, a missing B means pure turnback; present = interchange loop.\n configB: loop && !hasB ? \"none\" : configOf(\"B\"),\n branches: branchEps.map((ep) => ({\n label: ep.label ?? \"Branch\",\n pos: sc(ep.at!.pos),\n side: ep.at!.side === \"down\" ? \"down\" : \"up\",\n config: ep.tracks?.[0]?.config === \"double\" ? \"double\" : \"single\",\n })),\n poseOverrides,\n crossings: (d!.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n pos: sc(x.pos),\n trackA: x.tracks?.[0] ?? MAIN_TRACK_ID,\n trackB: x.tracks?.[1] ?? MAIN_TRACK_ID,\n })),\n extraTracks,\n turnouts: (d!.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n pos: sc(t.pos),\n onTrack: t.onTrack,\n divergeTrack: t.divergeTrack,\n kind: (t.kind as TurnoutKind) ?? \"right\",\n })),\n controlPoints: readControlPoints(d!, sc),\n };\n}\n\n/** Control points from a doc, migrating pre-grouping flat signals into groups. */\nfunction readControlPoints(\n d: ModuleSchematicDoc,\n sc: (p: number) => number = (p) => p,\n): EditorControlPoint[] {\n if (Array.isArray(d.controlPoints)) {\n return d.controlPoints.map((c) => ({\n id: c.id,\n name: c.name ?? \"\",\n turnouts: c.turnouts ?? [],\n ...(c.crossings?.length ? { crossings: c.crossings } : {}),\n signals: (c.signals ?? []).map((s) => ({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n })),\n }));\n }\n // Back-compat: group old flat signals by their turnout (or standalone).\n const groups = new Map<string, EditorControlPoint>();\n let n = 0;\n for (const s of d.signals ?? []) {\n const key = s.turnout || `blk-${s.id}`;\n let cp = groups.get(key);\n if (!cp) {\n cp = { id: `cp${++n}`, name: s.name ?? \"\", turnouts: s.turnout ? [s.turnout] : [], signals: [] };\n groups.set(key, cp);\n }\n cp.signals.push({\n id: s.id,\n pos: sc(s.pos),\n track: s.track ?? MAIN_TRACK_ID,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: (s.side as SignalSide) ?? \"above\",\n });\n }\n return [...groups.values()];\n}\n\n/** Find an unused `${prefix}${n}` id given the ones already present. */\nexport function nextId(prefix: string, existing: string[]): string {\n let n = 1;\n while (existing.includes(`${prefix}${n}`)) n += 1;\n return `${prefix}${n}`;\n}\n\n/**\n * Build a passing siding as one unit: the siding track, a switch at each end,\n * and control-point signals for both directions at each end (prototype Station\n * Entering Signal). Returns the new items to merge into the editor state.\n */\nexport function buildPassingSiding(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n controlPoints: EditorControlPoint[];\n} {\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const inset = Math.max(6, Math.round(len * 0.08));\n const fromPos = inset;\n const toPos = Math.max(fromPos + 1, len - inset);\n // First free lane above the main(s): lane 1 is Main 2 on double modules.\n const baseLane =\n state.configA === \"double\" || state.configB === \"double\" ? 2 : 1;\n const lane = Math.max(baseLane, ...state.extraTracks.map((t) => t.lane + 1));\n\n const trackIds = [MAIN_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const sidId = nextId(\"sid\", trackIds);\n const track: EditorTrack = {\n id: sidId,\n role: \"siding\",\n lane,\n fromPos,\n toPos,\n moduleTrackId: null,\n trackName: \"Passing siding\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const swW = nextId(\"sw\", swIds);\n const swE = nextId(\"sw\", [...swIds, swW]);\n // A siding above the main throws LEFT at its west turnout (body runs east) and\n // RIGHT at its east turnout (body runs west) — both resolve to the same side,\n // so `kind` and the drawn lane agree (divergeSideForHand / #bug1).\n const turnouts: EditorTurnout[] = [\n { id: swW, name: \"West Siding\", pos: fromPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"left\" },\n { id: swE, name: \"East Siding\", pos: toPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: \"right\" },\n ];\n\n // One control point at each end, each grouping its switch and both-direction\n // signals on the main (prototype Station Entering Signal).\n const cpIds = state.controlPoints.map((c) => c.id);\n const cpW = nextId(\"cp\", cpIds);\n const cpE = nextId(\"cp\", [...cpIds, cpW]);\n const sig = (cpId: string, pos: number, facing: SignalFacing): EditorCpSignal => ({\n id: `${cpId}-${facing}`,\n pos,\n track: MAIN_TRACK_ID,\n facing,\n // opposite directions on opposite sides so they never overlap\n side: facing === \"AtoB\" ? \"above\" : \"below\",\n });\n const controlPoints: EditorControlPoint[] = [\n { id: cpW, name: \"West Siding\", turnouts: [swW], signals: [sig(cpW, fromPos, \"AtoB\"), sig(cpW, fromPos, \"BtoA\")] },\n { id: cpE, name: \"East Siding\", turnouts: [swE], signals: [sig(cpE, toPos, \"AtoB\"), sig(cpE, toPos, \"BtoA\")] },\n ];\n\n return { track, turnouts, controlPoints };\n}\n\n/**\n * Build a crossover as one unit: a short connector track between Main 1 and\n * Main 2, with a turnout on each main. The two turnouts sit on different lanes,\n * so the feature resolver draws it as a diagonal (not a lane-paralleling siding)\n * — this is what connects the two dots (#bug2). Needs a double-track module.\n */\nexport function buildCrossover(state: EditorState): {\n track: EditorTrack;\n turnouts: EditorTurnout[];\n} | null {\n const hasSecond = state.configA === \"double\" || state.configB === \"double\";\n if (state.loop || !hasSecond) return null;\n const len = state.lengthInches > 0 ? state.lengthInches : 24;\n const mid = Math.round(len / 2);\n const gap = Math.max(3, Math.round(len * 0.04));\n const w = Math.round(mid - gap / 2);\n const e = Math.round(mid + gap / 2);\n\n const trackIds = [MAIN_TRACK_ID, MAIN2_TRACK_ID, ...state.extraTracks.map((t) => t.id)];\n const xoId = nextId(\"xo\", trackIds);\n const track: EditorTrack = {\n id: xoId,\n role: \"crossover\",\n lane: 1,\n fromPos: w,\n toPos: e,\n moduleTrackId: null,\n trackName: \"Crossover\",\n };\n\n const swIds = state.turnouts.map((t) => t.id);\n const sw1 = nextId(\"sw\", swIds);\n const sw2 = nextId(\"sw\", [...swIds, sw1]);\n const turnouts: EditorTurnout[] = [\n { id: sw1, name: \"Crossover\", pos: w, onTrack: MAIN_TRACK_ID, divergeTrack: xoId, kind: \"left\" },\n { id: sw2, name: \"Crossover\", pos: e, onTrack: MAIN2_TRACK_ID, divergeTrack: xoId, kind: \"right\" },\n ];\n\n return { track, turnouts };\n}\n\n// ---- Pure feature resolver (both renderers draw these) --------------------\n\nexport interface DrawTrack {\n id: string;\n role: TrackRole;\n lane: number;\n fromFrac: number;\n toFrac: number;\n capacityFeet: number | null;\n /** Lane of the main this track diverges from (via its turnout) — the origin\n * of the diverge diagonal. A spur off Main 2 starts at lane 1, not lane 0;\n * without this, renderers draw what looks like a crossover. */\n divergesFromLane: number;\n /** The end that meets the main at its turnout (throat) and the far stub end,\n * as fractions of length. Direction-preserving: unlike fromFrac/toFrac (always\n * sorted West→East), these keep WHICH end joins, so an east-facing spur draws\n * its throat on the east. A siding meets the main at both ends — there\n * throat=fromFrac, stub=toFrac (the fields are only meaningful for spurs). */\n throatFrac: number;\n stubFrac: number;\n /** Inside the balloon of a loop module (#165). */\n inLoop: boolean;\n}\nexport interface DrawTurnout {\n id: string;\n name: string;\n posFrac: number;\n onLane: number;\n divergeLane: number;\n}\nexport interface DrawSignal {\n id: string;\n name: string;\n posFrac: number;\n lane: number;\n facing: SignalFacing;\n side: SignalSide;\n /** Owning control point's id, when the signal came from a CP group — lets a\n * renderer join the drawn signal back to interlocking-level state (aspects). */\n cp?: string;\n /** 0-based rank among signals that would otherwise land on the exact same\n * spot (same lane + side + position). Renderers offset each further from the\n * track by `stack` so a control point's signals never overlap. */\n stack: number;\n}\n/** A grade crossing / diamond — draw an X spanning the two lanes (#170). */\nexport interface DrawCrossing {\n id: string;\n name: string;\n posFrac: number;\n laneA: number;\n laneB: number;\n}\n/** A crossover — a connector track joining two parallel mains through a turnout\n * on each. Draw a straight diagonal between the two turnout points (unlike a\n * siding, which parallels a lane between its turnouts). Detected structurally:\n * a track whose turnouts sit on two different lanes. */\nexport interface DrawCrossover {\n id: string;\n name: string;\n fromPosFrac: number;\n fromLane: number;\n toPosFrac: number;\n toLane: number;\n}\n/** A branch endplate — draw a connector stub + arrow + label off the given\n * side (the CATS/US&S off-band idiom, #170). */\nexport interface BranchConnector {\n id: string;\n label: string;\n posFrac: number;\n side: \"up\" | \"down\";\n}\nexport interface ModuleFeatures {\n /** Whether either endplate declares a double-track main. */\n doubleMain: boolean;\n /** Balloon loop — draw a terminal bulb at the far end. */\n loop: boolean;\n /** Loop with a standard endplate B on the balloon: an interchange — a\n * second route connects at the loop (draw an endplate branch off the bulb). */\n loopInterchange: boolean;\n /** Where the balloon returns: \"main2\" = directional return on a double-track\n * main — draw a U joining lanes 0 and 1 instead of the bulb (#165). */\n loopReturn: \"same\" | \"main2\";\n /** Rendering override from the doc; renderers may ignore modes they don't\n * implement yet (\"geometric\"). */\n loopRender: \"bulb\" | \"fan\" | \"geometric\" | null;\n /** Non-main tracks (sidings/spurs/yard/crossover). */\n extraTracks: DrawTrack[];\n turnouts: DrawTurnout[];\n signals: DrawSignal[];\n /** Grade crossings / diamonds (#170). */\n crossings: DrawCrossing[];\n /** Crossovers — connector tracks between two mains (drawn as a diagonal). */\n crossovers: DrawCrossover[];\n /** Branch endplates — junction connectors off the module (#170). */\n branchConnectors: BranchConnector[];\n /** Main 2's extent when it doesn't run the full module — a single↔double\n * transition (Main 2 starts/ends at the mainline turnout). Null = full\n * length (or no Main 2). Renderers draw the partial line + its diverge. */\n main2Extent: { fromFrac: number; toFrac: number } | null;\n /** Single↔double transition, fully described (#FMN-0043). The `through` main\n * runs the whole module; the `branch` main exists only on the double side and\n * merges at `atFrac`. EITHER main can be through/branch — the surviving single\n * track follows the transition turnout's onTrack, so the drawn side isn't\n * hard-wired to Main 1. Null when not a transition. */\n transition: {\n throughLane: number;\n branchLane: number;\n atFrac: number;\n doubleSide: \"west\" | \"east\";\n } | null;\n /** Lane extents across every feature (mains included; negative = outside\n * Main 1). Renderers size their vertical space from these. */\n laneMin: number;\n laneMax: number;\n}\n\n/**\n * Which side of the main a diverging track draws on, given the turnout's HAND\n * and the direction the track body runs along the main from the turnout\n * (`stubDir`: +1 = the body extends east / toward B, −1 = west / toward A).\n * Returns +1 (above the main) or −1 (below); 0 for a wye or unset hand (keep the\n * authored side). A left-hand turnout throws its route to the same side its body\n * runs (facing the frog, left is the inside of a body running that way); a\n * right-hand throws to the opposite side. `kind` is the source of truth for the\n * drawn side (#bug1) — the stored lane's sign is reconciled to match it.\n */\nexport function divergeSideForHand(\n kind: TurnoutKind | undefined,\n stubDir: number,\n): -1 | 0 | 1 {\n if (kind !== \"left\" && kind !== \"right\") return 0; // wye / unset → no change\n const s = stubDir >= 0 ? 1 : -1;\n return kind === \"left\" ? (s as 1 | -1) : ((-s) as 1 | -1);\n}\n\n/**\n * Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a\n * fraction of the module length; endplate A = 0, B = length; turnouts sit at\n * their pos. Tracks may carry explicit fromPos/toPos (overriding node lookup).\n * To-scale: a feature renders at its true position, clamped only to the\n * module's own extent — so signals near an end read at their real spot, not\n * bunched at an inset (#122).\n */\nexport function moduleFeatures(doc: ModuleSchematicDoc): ModuleFeatures {\n const len =\n doc.lengthInches && doc.lengthInches > 0\n ? doc.lengthInches\n : Math.max(\n 1,\n ...doc.tracks.map((t) => Math.max(t.fromPos ?? 0, t.toPos ?? 0)),\n ...(doc.turnouts ?? []).map((t) => t.pos),\n );\n const clampFrac = (p: number) => Math.min(1, Math.max(0, p / len));\n\n const trackLane = new Map<string, number>();\n for (const t of doc.tracks) trackLane.set(t.id, t.lane);\n\n // Endplate positions: first endplate = West (0), the rest = East (len).\n const endplatePos = new Map<string, number>();\n doc.endplates.forEach((e, i) => endplatePos.set(e.id, i === 0 ? 0 : len));\n const turnoutPos = new Map<string, number>();\n for (const t of doc.turnouts ?? []) turnoutPos.set(t.id, t.pos);\n const posOf = (nodeId?: string): number | null => {\n if (nodeId == null) return null;\n if (endplatePos.has(nodeId)) return endplatePos.get(nodeId)!;\n if (turnoutPos.has(nodeId)) return turnoutPos.get(nodeId)!;\n return null;\n };\n\n const doubleMain = doc.endplates.some((e) =>\n e.tracks?.some((t) => t.config === \"double\"),\n );\n\n // The lane a track diverges from = the main its turnout sits on.\n const divergeOrigin = (trackId: string): number => {\n const sw = (doc.turnouts ?? []).find((t) => t.divergeTrack === trackId);\n return sw ? (trackLane.get(sw.onTrack) ?? 0) : 0;\n };\n\n // Turnouts grouped by the track they diverge onto (a track's throat/hand).\n const turnoutsByTrack = new Map<string, SchematicTurnout[]>();\n for (const sw of doc.turnouts ?? []) {\n const arr = turnoutsByTrack.get(sw.divergeTrack) ?? [];\n arr.push(sw);\n turnoutsByTrack.set(sw.divergeTrack, arr);\n }\n // A crossover is a connector whose turnouts sit on two different lanes (one on\n // each main) — drawn as a diagonal between them, never as a lane-paralleling\n // siding. A passing siding's two turnouts share one lane, so it isn't one.\n const isCrossover = (trackId: string): boolean => {\n const sws = turnoutsByTrack.get(trackId) ?? [];\n if (sws.length < 2) return false;\n return new Set(sws.map((s) => trackLane.get(s.onTrack) ?? 0)).size >= 2;\n };\n // The in-inches extent of a track (explicit fromPos/toPos or node lookup).\n const extentOf = (t: SchematicTrack): [number, number] | null => {\n const from = t.fromPos ?? posOf(t.from);\n const to = t.toPos ?? posOf(t.to);\n return from == null || to == null ? null : [from, to];\n };\n\n // Reconcile each diverging track's drawn SIDE from its turnout's hand — `kind`\n // is the source of truth (#bug1) — resolved TOPOLOGICALLY. The hand only picks\n // a side for a turnout sitting on the main CENTERLINE (lane 0); a track\n // diverging off any other track (Main 2, or a ladder rung) stays on its\n // PARENT's side and stacks outward — a ladder turnout's hand is relative to\n // its parent, not the main, so it must never flip a rung across the main. The\n // stored lane's magnitude (stacking slot) is always kept.\n const trackById = new Map(doc.tracks.map((t) => [t.id, t]));\n const resolvedLanes = new Map<string, number>();\n const resolving = new Set<string>();\n const resolveLane = (id: string): number => {\n const trk = trackById.get(id);\n if (!trk) return 0;\n if (trk.role === \"main\") return trk.lane; // mains are fixed (0, 1, …)\n if (resolvedLanes.has(id)) return resolvedLanes.get(id)!;\n if (resolving.has(id)) return trk.lane; // cycle guard\n resolving.add(id);\n let lane = trk.lane;\n const sw = turnoutsByTrack.get(id)?.[0];\n const ext = extentOf(trk);\n if (sw && (sw.kind === \"left\" || sw.kind === \"right\") && !isCrossover(id) && ext) {\n const parentLane = resolveLane(sw.onTrack);\n let sign: number;\n if (parentLane === 0) {\n // On the main centerline — the hand chooses above/below.\n const [from, to] = ext;\n const far = Math.abs(to - sw.pos) >= Math.abs(from - sw.pos) ? to : from;\n const s = divergeSideForHand(sw.kind, far - sw.pos);\n sign = s !== 0 ? s : Math.sign(trk.lane) || 1;\n } else {\n // Off Main 2 / a ladder rung — follow the parent's side.\n sign = Math.sign(parentLane) || 1;\n }\n lane = sign * Math.abs(trk.lane);\n }\n resolving.delete(id);\n resolvedLanes.set(id, lane);\n return lane;\n };\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue;\n trackLane.set(t.id, resolveLane(t.id));\n }\n\n const extraTracks: DrawTrack[] = [];\n for (const t of doc.tracks) {\n if (t.role === \"main\") continue; // the spine draws mains\n if (isCrossover(t.id)) continue; // crossovers draw as diagonals, below\n const ext = extentOf(t);\n if (!ext) continue; // can't place it\n const [from, to] = ext;\n // Throat = the end nearest this track's turnout (the join to the main);\n // with no turnout, keep entry order (from = throat) so the author's chosen\n // joining end is honoured. This is what makes an east-facing spur draw its\n // throat on the east instead of always west (#bug3).\n const sw = turnoutsByTrack.get(t.id)?.[0];\n const throatAtTo = sw != null && Math.abs(to - sw.pos) < Math.abs(from - sw.pos);\n const throat = throatAtTo ? to : from;\n const stub = throatAtTo ? from : to;\n extraTracks.push({\n id: t.id,\n role: t.role,\n lane: trackLane.get(t.id) ?? t.lane,\n fromFrac: clampFrac(Math.min(from, to)),\n toFrac: clampFrac(Math.max(from, to)),\n throatFrac: clampFrac(throat),\n stubFrac: clampFrac(stub),\n capacityFeet: t.capacityFeet ?? null,\n divergesFromLane: divergeOrigin(t.id),\n inLoop: t.inLoop === true,\n });\n }\n\n // Crossovers — a diagonal between two parallel mains. Two shapes are drawn:\n const crossovers: DrawCrossover[] = [];\n // (1) a dedicated connector track (buildCrossover) with a turnout on each main.\n for (const t of doc.tracks) {\n if (!isCrossover(t.id)) continue;\n const [s1, s2] = turnoutsByTrack.get(t.id)!;\n crossovers.push({\n id: t.id,\n name: t.trackName ?? \"\",\n fromPosFrac: clampFrac(s1.pos),\n fromLane: trackLane.get(s1.onTrack) ?? 0,\n toPosFrac: clampFrac(s2.pos),\n toLane: trackLane.get(s2.onTrack) ?? 1,\n });\n }\n // (2) modelled as two turnouts each diverging onto the OTHER main (no connector\n // track). Pair a leg with its return leg and connect the two turnout points.\n // A single↔double TRANSITION turnout also connects the two mains but isn't a\n // crossover (Main 2 ends there) — exclude it, and require a matching return\n // leg so a lone main-to-main turnout is never drawn as a crossover.\n const isMainId = (id?: string) => !!id && trackById.get(id)?.role === \"main\";\n const epDouble = (id: string) =>\n (doc.endplates.find((e) => e.id === id)?.tracks ?? []).some(\n (t) => t.config === \"double\",\n );\n const aDbl = epDouble(\"A\");\n const bDbl = epDouble(\"B\");\n const transitionSw =\n aDbl !== bDbl ? (doc.turnouts ?? []).find(isTransitionTurnout) : undefined;\n const m2m = (doc.turnouts ?? []).filter(\n (sw) =>\n sw !== transitionSw &&\n isMainId(sw.onTrack) &&\n isMainId(sw.divergeTrack) &&\n (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1),\n );\n const usedLegs = new Set<string>();\n for (const t1 of m2m) {\n if (usedLegs.has(t1.id)) continue;\n const t2 = m2m.find(\n (x) =>\n !usedLegs.has(x.id) &&\n x.id !== t1.id &&\n x.onTrack === t1.divergeTrack &&\n x.divergeTrack === t1.onTrack,\n );\n if (!t2) continue; // lone leg → a transition/junction, not a crossover\n usedLegs.add(t1.id);\n usedLegs.add(t2.id);\n crossovers.push({\n id: `${t1.id}-${t2.id}`,\n name: t1.name ?? t2.name ?? \"\",\n fromPosFrac: clampFrac(t1.pos),\n fromLane: trackLane.get(t1.onTrack) ?? 0,\n toPosFrac: clampFrac(t2.pos),\n toLane: trackLane.get(t2.onTrack) ?? 1,\n });\n }\n\n const turnouts: DrawTurnout[] = (doc.turnouts ?? []).map((t) => ({\n id: t.id,\n name: t.name ?? \"\",\n posFrac: clampFrac(t.pos),\n onLane: trackLane.get(t.onTrack) ?? 0,\n divergeLane: trackLane.get(t.divergeTrack) ?? 1,\n }));\n\n const drawSignal = (\n s: SchematicSignal,\n name: string,\n cp?: string,\n ): DrawSignal => ({\n id: s.id,\n name,\n posFrac: clampFrac(s.pos),\n lane: s.track ? (trackLane.get(s.track) ?? 0) : 0,\n facing: (s.facing as SignalFacing) ?? \"AtoB\",\n side: s.side === \"below\" ? \"below\" : \"above\",\n ...(cp ? { cp } : {}),\n stack: 0,\n });\n // Signals come from control-point groups; fall back to pre-grouping flat\n // signals for docs authored before the model changed.\n const signals: DrawSignal[] = Array.isArray(doc.controlPoints)\n ? doc.controlPoints.flatMap((c) =>\n (c.signals ?? []).map((s) => drawSignal(s, c.name ?? \"\", c.id)),\n )\n : (doc.signals ?? []).map((s) => drawSignal(s, s.name ?? \"\"));\n // De-collide: signals landing on the exact same lane+side+position get a\n // rising stack rank so a renderer can fan them out (a control point often\n // carries several signals at one interlocking).\n const stackCount = new Map<string, number>();\n for (const s of signals) {\n const key = `${s.lane}|${s.side}|${Math.round(s.posFrac * 1000)}`;\n const n = stackCount.get(key) ?? 0;\n s.stack = n;\n stackCount.set(key, n + 1);\n }\n\n const crossings: DrawCrossing[] = (doc.crossings ?? []).map((x) => ({\n id: x.id,\n name: x.name ?? \"\",\n posFrac: clampFrac(x.pos),\n laneA: trackLane.get(x.tracks?.[0] ?? \"\") ?? 0,\n laneB: trackLane.get(x.tracks?.[1] ?? \"\") ?? 1,\n }));\n\n // Branch endplates (any beyond A/B with a placement) → connector arrows.\n const branchConnectors: BranchConnector[] = doc.endplates\n .filter((e) => e.id !== \"A\" && e.id !== \"B\" && e.at)\n .map((e) => ({\n id: e.id,\n label: e.label ?? e.id,\n posFrac: clampFrac(e.at!.pos),\n side: e.at!.side === \"down\" ? \"down\" : \"up\",\n }));\n\n const allLanes = [\n 0,\n doubleMain ? 1 : 0,\n ...extraTracks.map((t) => t.lane),\n ...signals.map((s) => s.lane),\n ...crossings.flatMap((x) => [x.laneA, x.laneB]),\n ...crossovers.flatMap((x) => [x.fromLane, x.toLane]),\n ];\n const loop = isLoopDoc(doc);\n // A positioned Main 2 = a transition module (partial second main).\n const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);\n const main2Positioned =\n !!main2 && (main2.fromPos != null || main2.toPos != null) && !loop;\n let main2Extent: { fromFrac: number; toFrac: number } | null = null;\n if (main2Positioned) {\n main2Extent = {\n fromFrac: clampFrac(main2!.fromPos ?? 0),\n toFrac: clampFrac(main2!.toPos ?? len),\n };\n } else if (\n main2 &&\n transitionSw &&\n transitionSw.divergeTrack === MAIN2_TRACK_ID &&\n !loop\n ) {\n // Main 2 is the branch but stored full-length — derive its extent from the\n // junction. (When Main 2 is the through main, it stays full: extent null.)\n main2Extent = aDbl\n ? { fromFrac: 0, toFrac: clampFrac(transitionSw.pos) } // double at west\n : { fromFrac: clampFrac(transitionSw.pos), toFrac: 1 }; // double at east\n }\n // Full transition descriptor: the surviving (through) main follows the\n // turnout's onTrack, so either main can be the one that ends (#FMN-0043).\n const transition =\n transitionSw && aDbl !== bDbl && !loop\n ? {\n throughLane: trackLane.get(transitionSw.onTrack) ?? 0,\n branchLane: trackLane.get(transitionSw.divergeTrack) ?? 1,\n atFrac: clampFrac(transitionSw.pos),\n doubleSide: (aDbl ? \"west\" : \"east\") as \"west\" | \"east\",\n }\n : null;\n return {\n doubleMain,\n loop,\n main2Extent,\n transition,\n loopInterchange: loop && doc.endplates.filter((e) => !e.at).length >= 2,\n loopReturn: loop && doc.loopReturn === \"main2\" ? \"main2\" : \"same\",\n loopRender: loop ? (doc.loopRender ?? null) : null,\n extraTracks,\n turnouts,\n signals,\n crossings,\n crossovers,\n branchConnectors,\n laneMin: Math.min(...allLanes),\n laneMax: Math.max(...allLanes),\n };\n}\n\n// ---- Endplate geometry & poses (#175) --------------------------------------\n//\n// Free-moN endplates are one standard interface; a single (x, y, heading) at\n// the track-crossing point fully determines mating (any endplate ↔ any\n// compatible endplate). Poses are in MODULE-LOCAL inches: endplate A's track\n// point at the origin, its OUTWARD normal pointing west (180°), so the module\n// body runs toward +X. A layout composes modules by walking joins and stacking\n// each pose's rigid transform (rotation + optional reflection for flips).\n//\n// Poses are DERIVED from the simple fields owners already enter — length +\n// geometry type/degrees/offset for the two axial endplates, plus the schematic\n// doc's branch endplates (#170). Complex shapes (wye, loop, other) fall back to\n// hand-entered overrides. Nothing is stored unless overridden.\n\nexport type GeometryType =\n | \"straight\"\n | \"corner_45\"\n | \"corner_90\"\n | \"curve\"\n | \"offset\"\n | \"dead_end\"\n | \"wye\"\n | \"other\";\n\nexport interface EndplatePose {\n /** Endplate id — \"A\"/\"B\" axial, \"C\"/\"D\"… branch. */\n id: string;\n /** Module-local inches; endplate A's track point is the origin. */\n x: number;\n y: number;\n /** Outward normal in degrees (0 = +X east, 90 = +Y north). A neighbour mates\n * facing the opposite heading. */\n heading: number;\n trackConfig: \"single\" | \"double\";\n /** Lateral track offsets from the crossing anchor (0 = centred single). */\n trackOffsets: number[];\n /** True when the pose was hand-entered, not derived (wye/loop/other). */\n manual?: boolean;\n}\n\nexport interface ModuleGeometryInput {\n lengthInches: number;\n geometryType?: string | null;\n geometryDegrees?: number | null;\n geometryOffsetInches?: number | null;\n /** Axial endplate configs (A first, then B). Missing → single. */\n endplateConfigs?: (\"single\" | \"double\" | null | undefined)[];\n /** Branch endplates (from the schematic doc, #170), positioned along the\n * mainline axis. */\n branches?: {\n id: string;\n atPos: number;\n side: \"up\" | \"down\";\n config?: \"single\" | \"double\" | null;\n }[];\n /** Hand-entered pose overrides by endplate id — win over derivation. */\n poseOverrides?: Record<string, { x: number; y: number; heading: number }>;\n /** Half the spacing between the two tracks of a double endplate (Free-mo ≈ 1\",\n * Free-moN ≈ 9/16\"). */\n trackHalfSpacingInches?: number;\n}\n\n/** Signed turn a module applies to the through track (CCW/left positive). */\nexport function geometryTurnDegrees(\n geometryType?: string | null,\n geometryDegrees?: number | null,\n): number {\n switch (geometryType) {\n case \"corner_45\":\n return 45;\n case \"corner_90\":\n return 90;\n case \"curve\":\n return geometryDegrees ?? 0;\n default:\n return 0;\n }\n}\n\nconst DEG = Math.PI / 180;\nconst norm360 = (d: number) => ((d % 360) + 360) % 360;\n\nfunction offsetsFor(\n config: \"single\" | \"double\",\n half: number,\n): number[] {\n return config === \"double\" ? [-half, half] : [0];\n}\n\n/**\n * Derive every endplate's module-local pose. A at the origin facing west; B\n * placed by the module's geometry (straight/offset/corner/curve via a\n * constant-radius arc = arc-length `lengthInches`, turning by the geometry\n * angle); branch endplates positioned along the mainline axis facing out their\n * side. A `dead_end`/loop module has no B. Overrides replace any derived pose.\n */\nexport function deriveEndplatePoses(geo: ModuleGeometryInput): EndplatePose[] {\n const L = geo.lengthInches > 0 ? geo.lengthInches : 24;\n const half = geo.trackHalfSpacingInches ?? 1;\n const cfg = (i: number): \"single\" | \"double\" =>\n geo.endplateConfigs?.[i] === \"double\" ? \"double\" : \"single\";\n const withOverride = (p: EndplatePose): EndplatePose => {\n const o = geo.poseOverrides?.[p.id];\n return o ? { ...p, x: o.x, y: o.y, heading: norm360(o.heading), manual: true } : p;\n };\n\n const poses: EndplatePose[] = [];\n\n // Endplate A — origin, outward normal west.\n poses.push(\n withOverride({\n id: \"A\",\n x: 0,\n y: 0,\n heading: 180,\n trackConfig: cfg(0),\n trackOffsets: offsetsFor(cfg(0), half),\n }),\n );\n\n // Endplate B — unless the module is a dead end / turnback (single endplate).\n const noB = geo.geometryType === \"dead_end\";\n if (!noB) {\n const turn = geometryTurnDegrees(geo.geometryType, geo.geometryDegrees);\n let bx: number;\n let by: number;\n let bHeading: number;\n if (geo.geometryType === \"offset\") {\n // Parallel endplates, jogged sideways over the run.\n bx = L;\n by = geo.geometryOffsetInches ?? 0;\n bHeading = 0;\n } else if (turn === 0) {\n bx = L;\n by = 0;\n bHeading = 0;\n } else {\n // Constant-radius arc of arc-length L turning `turn` (CCW/left).\n const t = turn * DEG;\n const r = L / t;\n bx = r * Math.sin(t);\n by = r * (1 - Math.cos(t));\n bHeading = turn;\n }\n poses.push(\n withOverride({\n id: \"B\",\n x: bx,\n y: by,\n heading: norm360(bHeading),\n trackConfig: cfg(1),\n trackOffsets: offsetsFor(cfg(1), half),\n }),\n );\n }\n\n // Branch endplates — at their along-axis position, facing out their side.\n // Position along the (possibly curved) mainline is approximated on the A→B\n // chord; the join solver refines with overrides where a module needs it.\n for (const b of geo.branches ?? []) {\n const frac = L > 0 ? Math.min(1, Math.max(0, b.atPos / L)) : 0;\n const px = frac * L;\n const config = b.config === \"double\" ? \"double\" : \"single\";\n poses.push(\n withOverride({\n id: b.id,\n x: px,\n y: 0,\n heading: b.side === \"down\" ? 270 : 90,\n trackConfig: config,\n trackOffsets: offsetsFor(config, half),\n }),\n );\n }\n\n return poses;\n}\n\n/** Whether a module shape's poses are fully derivable, or need manual entry\n * (a helpful cue for the authoring UI). */\nexport function poseNeedsManual(geometryType?: string | null): boolean {\n return geometryType === \"wye\" || geometryType === \"other\";\n}\n\n/** Extract manual pose overrides from a schematic doc's endplates (#175 phase\n * 1b) — the map deriveEndplatePoses / a footprint solver feeds as overrides. */\nexport function poseOverridesFromDoc(\n doc: ModuleSchematicDoc,\n): Record<string, { x: number; y: number; heading: number }> {\n const out: Record<string, { x: number; y: number; heading: number }> = {};\n for (const e of doc.endplates ?? []) {\n if (\n e.id &&\n e.pose &&\n typeof e.pose.x === \"number\" &&\n typeof e.pose.y === \"number\" &&\n typeof e.pose.heading === \"number\"\n ) {\n out[e.id] = { x: e.pose.x, y: e.pose.y, heading: e.pose.heading };\n }\n }\n return out;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willcgage/module-schematic",
3
- "version": "0.13.1",
3
+ "version": "0.13.3",
4
4
  "description": "Shared FreeMo module operations-schematic (track-graph): doc types, parser, feature resolver, N-scale helpers, and the editor state machine. Authored by the Module Repository, imported by Free-Dispatcher.",
5
5
  "keywords": [
6
6
  "freemo",