@willcgage/module-schematic 0.92.1 → 0.93.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.cjs CHANGED
@@ -2443,6 +2443,7 @@ function snapPiece(moving, others, library = BUILT_IN_TRACK_PARTS, withinInches
2443
2443
  };
2444
2444
  }
2445
2445
  var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
2446
+ var unreachedWarning = (id) => `${id} is not reachable from the endplate \u2014 nothing connects it`;
2446
2447
  function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
2447
2448
  const byKey = new Map(graph.joints.map((j) => [j.key, j]));
2448
2449
  const pieceById = new Map(pieces.map((p) => [p.id, p]));
@@ -2580,11 +2581,14 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
2580
2581
  if (sw && !sw.divergeRoute) sw.divergeRoute = r.id;
2581
2582
  }
2582
2583
  const reached = new Set(routes.flatMap((r) => r.pieces));
2584
+ const unreached = [];
2583
2585
  for (const p of pieces)
2584
- if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id))
2585
- warnings.push(`${p.id} is not reachable from the endplate \u2014 nothing connects it`);
2586
+ if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id)) {
2587
+ warnings.push(unreachedWarning(p.id));
2588
+ unreached.push(p.id);
2589
+ }
2586
2590
  for (const c of graph.conflicts) warnings.push(c.reason);
2587
- return { routes, turnouts, warnings, danglingDiverges };
2591
+ return { routes, turnouts, warnings, danglingDiverges, unreached };
2588
2592
  }
2589
2593
  function resolveGraphAnchor(anchor, walk, pieces, library = BUILT_IN_TRACK_PARTS) {
2590
2594
  const piece = pieces.find((p) => p.id === anchor.piece);
@@ -2613,14 +2617,29 @@ function graphToDoc(pieces, input) {
2613
2617
  const epA = endplates[0];
2614
2618
  const epB = endplates[1];
2615
2619
  const main = walk.routes.find((r) => r.id === "main");
2616
- const lengthInches = round(main.toPos);
2617
- const branches = walk.routes.filter((r) => r.id !== "main" && r.pieces.length);
2620
+ const walk2 = input.start2 ? walkTrackGraph(graph, pieces, input.start2, library) : null;
2621
+ const main2 = walk2?.routes.find((r) => r.id === "main") ?? null;
2622
+ const claimed = new Set(walk.routes.flatMap((r) => r.pieces));
2623
+ if (main2 && main2.pieces.some((p) => claimed.has(p))) {
2624
+ warnings.push(
2625
+ "Main 2 starts on track that Main 1 already runs along \u2014 they are one run, not two"
2626
+ );
2627
+ }
2628
+ const twoMains = !!main2 && !main2.pieces.some((p) => claimed.has(p));
2629
+ const main2Lane = twoMains ? (graph.joints.find((j) => j.key === `${input.start2.piece}.${input.start2.joint}`)?.y ?? 1) >= 0 ? 1 : -1 : 0;
2630
+ const lengthInches = round(Math.max(main.toPos, twoMains ? main2.toPos : 0));
2631
+ const branches = [
2632
+ ...walk.routes.filter((r) => r.id !== "main" && r.pieces.length),
2633
+ ...twoMains ? walk2.routes.filter((r) => r.id !== "main" && r.pieces.length).map((r) => ({ ...r, id: `main2:${r.id}`, bornAt: r.bornAt })) : []
2634
+ ];
2618
2635
  const trackIdOf = /* @__PURE__ */ new Map([["main", MAIN_TRACK_ID]]);
2636
+ if (twoMains) trackIdOf.set("main2:main", MAIN2_TRACK_ID);
2619
2637
  for (const r of branches) trackIdOf.set(r.id, r.pieces[0]);
2620
2638
  const laneOf = (r) => {
2621
2639
  const side = Math.sign(r.lateral) || 1;
2622
2640
  const sameSide = branches.filter((x) => (Math.sign(x.lateral) || 1) === side).sort((a, b) => Math.abs(a.lateral) - Math.abs(b.lateral));
2623
- return side * (sameSide.indexOf(r) + 1);
2641
+ const taken = side === main2Lane ? 1 : 0;
2642
+ return side * (sameSide.indexOf(r) + 1 + taken);
2624
2643
  };
2625
2644
  const tracks = [
2626
2645
  {
@@ -2629,7 +2648,20 @@ function graphToDoc(pieces, input) {
2629
2648
  lane: 0,
2630
2649
  from: epA?.id ?? "A",
2631
2650
  ...epB ? { to: epB.id } : {}
2632
- }
2651
+ },
2652
+ ...twoMains ? [
2653
+ {
2654
+ id: MAIN2_TRACK_ID,
2655
+ role: "main",
2656
+ lane: main2Lane,
2657
+ from: epA?.id ?? "A",
2658
+ ...epB ? { to: epB.id } : {},
2659
+ // ⚠️ Its own extent, because a second main need not run the whole
2660
+ // module: on a transition module it starts or stops partway.
2661
+ fromPos: round(Math.min(main2.fromPos, main2.toPos)),
2662
+ toPos: round(Math.max(main2.fromPos, main2.toPos))
2663
+ }
2664
+ ] : []
2633
2665
  ];
2634
2666
  for (const r of branches) {
2635
2667
  const id = trackIdOf.get(r.id);
@@ -2647,9 +2679,29 @@ function graphToDoc(pieces, input) {
2647
2679
  ...meta.moduleTrackId != null ? { moduleTrackId: meta.moduleTrackId } : {}
2648
2680
  });
2649
2681
  }
2682
+ if (walk2) {
2683
+ const seen = /* @__PURE__ */ new Set([
2684
+ ...walk.routes.flatMap((r) => r.pieces),
2685
+ ...walk2.routes.flatMap((r) => r.pieces)
2686
+ ]);
2687
+ const covered = new Set([...walk.unreached, ...walk2.unreached].filter((id) => seen.has(id)));
2688
+ const drop = new Set([...covered].map(unreachedWarning));
2689
+ const d2 = new Set(walk2.danglingDiverges.map(danglingDivergeWarning));
2690
+ for (let i = warnings.length - 1; i >= 0; i--) if (drop.has(warnings[i])) warnings.splice(i, 1);
2691
+ for (const w of walk2.warnings)
2692
+ if (!d2.has(w) && !drop.has(w) && !warnings.includes(w)) warnings.push(w);
2693
+ }
2650
2694
  const pieceById = new Map(pieces.map((p) => [p.id, p]));
2651
2695
  const turnouts = [];
2652
- for (const t of walk.turnouts) {
2696
+ const allTurnouts = [
2697
+ ...walk.turnouts,
2698
+ ...twoMains ? walk2.turnouts.map((t) => ({
2699
+ ...t,
2700
+ onRoute: t.onRoute === "main" ? "main2:main" : `main2:${t.onRoute}`,
2701
+ divergeRoute: t.divergeRoute ? `main2:${t.divergeRoute}` : null
2702
+ })) : []
2703
+ ];
2704
+ for (const t of allTurnouts) {
2653
2705
  const diverge = t.divergeRoute ? trackIdOf.get(t.divergeRoute) : void 0;
2654
2706
  if (!diverge) {
2655
2707
  warnings.push(
@@ -2731,7 +2783,13 @@ function deriveGraphDoc(doc, library = BUILT_IN_TRACK_PARTS) {
2731
2783
  ...t.moduleTrackId != null ? { moduleTrackId: t.moduleTrackId } : {}
2732
2784
  };
2733
2785
  }
2734
- const out = graphToDoc(g.pieces, { startAt: g.startAt, base: doc, meta, library });
2786
+ const out = graphToDoc(g.pieces, {
2787
+ startAt: g.startAt,
2788
+ start2: g.start2 ?? null,
2789
+ base: doc,
2790
+ meta,
2791
+ library
2792
+ });
2735
2793
  return { doc: out.doc, warnings: out.warnings };
2736
2794
  }
2737
2795
  function frogCasting(cl, opts = {}) {