@willcgage/module-schematic 0.38.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -269,6 +269,11 @@ interface ModuleSchematicDoc {
269
269
  * Present = the owner drew the real shape; absent = derive from geometry.
270
270
  * Physical view only — the operations view stays derived (#2d-track). */
271
271
  mainPath?: BenchworkPoint[] | null;
272
+ /** Authored centre-line for MAIN 2 on a double-track module (module-local
273
+ * inches, open path with arcs). Present = the owner bent Main 2 to its real
274
+ * shape; absent = derive it as a lane offset from Main 1. Physical view only
275
+ * (#131). */
276
+ main2Path?: BenchworkPoint[] | null;
272
277
  }
273
278
  /** A benchwork-outline vertex, module-local inches. The edge from this vertex
274
279
  * to the NEXT one is a straight line, unless `bulge` is set — then it's a
@@ -783,6 +788,8 @@ interface EditorState {
783
788
  /** Authored mainline centre-line (module-local inches) — empty = derive from
784
789
  * geometry. The owner-drawn real shape (#2d-track, physical view only). */
785
790
  mainPath: BenchworkPoint[];
791
+ /** Authored Main 2 centre-line (double-track only) — empty = lane offset (#131). */
792
+ main2Path: BenchworkPoint[];
786
793
  }
787
794
  /** Build the empty editor state for a module of the given length. */
788
795
  declare function emptyEditorState(lengthInches: number): EditorState;
package/dist/index.d.ts CHANGED
@@ -269,6 +269,11 @@ interface ModuleSchematicDoc {
269
269
  * Present = the owner drew the real shape; absent = derive from geometry.
270
270
  * Physical view only — the operations view stays derived (#2d-track). */
271
271
  mainPath?: BenchworkPoint[] | null;
272
+ /** Authored centre-line for MAIN 2 on a double-track module (module-local
273
+ * inches, open path with arcs). Present = the owner bent Main 2 to its real
274
+ * shape; absent = derive it as a lane offset from Main 1. Physical view only
275
+ * (#131). */
276
+ main2Path?: BenchworkPoint[] | null;
272
277
  }
273
278
  /** A benchwork-outline vertex, module-local inches. The edge from this vertex
274
279
  * to the NEXT one is a straight line, unless `bulge` is set — then it's a
@@ -783,6 +788,8 @@ interface EditorState {
783
788
  /** Authored mainline centre-line (module-local inches) — empty = derive from
784
789
  * geometry. The owner-drawn real shape (#2d-track, physical view only). */
785
790
  mainPath: BenchworkPoint[];
791
+ /** Authored Main 2 centre-line (double-track only) — empty = lane offset (#131). */
792
+ main2Path: BenchworkPoint[];
786
793
  }
787
794
  /** Build the empty editor state for a module of the given length. */
788
795
  declare function emptyEditorState(lengthInches: number): EditorState;
package/dist/index.js CHANGED
@@ -559,41 +559,38 @@ function emptyEditorState(lengthInches) {
559
559
  sections: [],
560
560
  controlPoints: [],
561
561
  industries: [],
562
- mainPath: []
562
+ mainPath: [],
563
+ main2Path: []
563
564
  };
564
565
  }
565
566
  function isTransitionTurnout(t) {
566
567
  return t.onTrack === MAIN_TRACK_ID && t.divergeTrack === MAIN2_TRACK_ID || t.onTrack === MAIN2_TRACK_ID && t.divergeTrack === MAIN_TRACK_ID;
567
568
  }
568
569
  function main1Track(state) {
569
- const bothDouble = state.configA === "double" && state.configB === "double";
570
- const isDouble = state.configA === "double" || state.configB === "double";
571
570
  const sw = state.turnouts.find(isTransitionTurnout);
572
- const lane = state.mainsSwapped && isDouble ? 1 : 0;
573
- if (!isDouble || bothDouble || !sw || sw.onTrack !== MAIN2_TRACK_ID) {
574
- return { id: MAIN_TRACK_ID, role: "main", lane, from: "A", to: "B" };
571
+ const legacyThroughMain2 = !!sw && sw.onTrack === MAIN2_TRACK_ID && sw.divergeTrack === MAIN_TRACK_ID;
572
+ const isDouble = state.configA === "double" || state.configB === "double";
573
+ const bothDouble = state.configA === "double" && state.configB === "double";
574
+ if (!isDouble || bothDouble || !legacyThroughMain2) {
575
+ return { id: MAIN_TRACK_ID, role: "main", lane: 0, from: "A", to: "B" };
575
576
  }
576
- return state.configA === "double" ? (
577
- // Double at A: Main 1 runs from A and ends at the turnout.
578
- { id: MAIN_TRACK_ID, role: "main", lane, fromPos: 0, toPos: sw.pos }
579
- ) : (
580
- // Double at B: Main 1 begins at the turnout and runs to B.
581
- { id: MAIN_TRACK_ID, role: "main", lane, fromPos: sw.pos, toPos: state.lengthInches }
582
- );
577
+ return state.configA === "double" ? { id: MAIN_TRACK_ID, role: "main", lane: 0, fromPos: 0, toPos: sw.pos } : { id: MAIN_TRACK_ID, role: "main", lane: 0, fromPos: sw.pos, toPos: state.lengthInches };
583
578
  }
584
579
  function main2Track(state) {
585
580
  const bothDouble = state.configA === "double" && state.configB === "double";
586
581
  const sws = state.turnouts.filter((t) => isTransitionTurnout(t) && t.divergeTrack === MAIN2_TRACK_ID).sort((a, b) => a.pos - b.pos);
587
- const lane = state.mainsSwapped ? 0 : 1;
582
+ const lane = state.mainsSwapped ? -1 : 1;
583
+ const authored = state.main2Path.length >= 2 ? { path: state.main2Path } : {};
588
584
  if (bothDouble || !sws.length) {
589
- return { id: MAIN2_TRACK_ID, role: "main", lane, from: "A", to: "B" };
585
+ return { id: MAIN2_TRACK_ID, role: "main", lane, from: "A", to: "B", ...authored };
590
586
  }
591
587
  const track = (fromPos, toPos) => ({
592
588
  id: MAIN2_TRACK_ID,
593
589
  role: "main",
594
590
  lane,
595
591
  fromPos,
596
- toPos
592
+ toPos,
593
+ ...authored
597
594
  });
598
595
  if (state.configA === "double") return track(0, sws[0].pos);
599
596
  if (state.configB === "double") return track(sws[0].pos, state.lengthInches);
@@ -612,12 +609,12 @@ function buildTransition(state) {
612
609
  id: swId,
613
610
  name: "End of Double Track",
614
611
  pos,
615
- // The turnout sits ON the ending main (Main 2, the upper track) and diverges
616
- // down to the continuous Main 1 — the modeller's view of the junction. Left
617
- // hand when the double end is west (Main 2 comes down going east), right
618
- // when it's east (mirror).
619
- onTrack: MAIN2_TRACK_ID,
620
- divergeTrack: MAIN_TRACK_ID,
612
+ // The turnout sits ON the through mainline (Main 1) and diverges TO Main 2,
613
+ // the second main being added to start the double track — the modeller's
614
+ // view of the junction, and the direction an owner authors it (Steve
615
+ // Branton, #131). Main 1 runs the full module; Main 2 is the branch.
616
+ onTrack: MAIN_TRACK_ID,
617
+ divergeTrack: MAIN2_TRACK_ID,
621
618
  kind: aDouble ? "left" : "right"
622
619
  };
623
620
  const cpId = nextId("cp", state.controlPoints.map((c) => c.id));
@@ -794,7 +791,8 @@ function stateToDoc(state, recordNumber) {
794
791
  // that never used them keeps exactly the doc it had before (#96 phase 2).
795
792
  ...state.sections.length ? { sections: moduleSections({ sections: state.sections }) } : {},
796
793
  // Authored mainline path (module-local inches); only when it's a real path.
797
- ...state.mainPath.length >= 2 ? { mainPath: state.mainPath } : {}
794
+ ...state.mainPath.length >= 2 ? { mainPath: state.mainPath } : {},
795
+ ...state.main2Path.length >= 2 ? { main2Path: state.main2Path } : {}
798
796
  };
799
797
  }
800
798
  function docToState(doc, fallbackLength, moduleTracks = []) {
@@ -879,6 +877,8 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
879
877
  ...Number.isFinite(p.bulge) && p.bulge ? { bulge: p.bulge } : {}
880
878
  }));
881
879
  const mainPath = trackPath(d.mainPath) ?? [];
880
+ const main2Track_ = (d.tracks ?? []).find((t) => t.id === MAIN2_TRACK_ID);
881
+ const main2Path = trackPath(d.main2Path ?? main2Track_?.path) ?? [];
882
882
  return {
883
883
  lengthInches: len,
884
884
  loop,
@@ -900,6 +900,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
900
900
  sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
901
901
  sections: moduleSections(d),
902
902
  mainPath,
903
+ main2Path,
903
904
  crossings: (d.crossings ?? []).map((x) => ({
904
905
  id: x.id,
905
906
  name: x.name ?? "",