@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 +67 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +67 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -459,6 +459,16 @@ interface ModuleSchematicDoc {
|
|
|
459
459
|
piece: string;
|
|
460
460
|
joint: string;
|
|
461
461
|
};
|
|
462
|
+
/** Where MAIN 2 starts, on a double-track module — the joint the endplate's
|
|
463
|
+
* SECOND track arrives at. Absent = single track.
|
|
464
|
+
*
|
|
465
|
+
* Two starts rather than a list because a document has exactly two mains,
|
|
466
|
+
* `main` and `main2`. This mirrors the thing downstream instead of inventing
|
|
467
|
+
* a generality nothing can read. */
|
|
468
|
+
start2?: {
|
|
469
|
+
piece: string;
|
|
470
|
+
joint: string;
|
|
471
|
+
} | null;
|
|
462
472
|
} | null;
|
|
463
473
|
}
|
|
464
474
|
/** A benchwork-outline vertex, module-local inches. The edge from this vertex
|
|
@@ -2644,6 +2654,11 @@ interface GraphWalk {
|
|
|
2644
2654
|
* sentence, so a caller can act on it (and say it ONCE) instead of matching
|
|
2645
2655
|
* on prose. */
|
|
2646
2656
|
danglingDiverges: string[];
|
|
2657
|
+
/** Pieces this walk never got to. ⚠️ "Unreached BY THIS WALK" is not the same
|
|
2658
|
+
* as "unreachable": a double-track module's second main is unreached by the
|
|
2659
|
+
* first walk and perfectly well connected. Ids, so a caller running more than
|
|
2660
|
+
* one walk can tell the difference. */
|
|
2661
|
+
unreached: string[];
|
|
2647
2662
|
}
|
|
2648
2663
|
/**
|
|
2649
2664
|
* Walk the graph from an endplate and read the topology off it.
|
|
@@ -2686,6 +2701,11 @@ interface GraphDocInput {
|
|
|
2686
2701
|
piece: string;
|
|
2687
2702
|
joint: string;
|
|
2688
2703
|
};
|
|
2704
|
+
/** Where MAIN 2 begins, on a double-track module. Absent = single track. */
|
|
2705
|
+
start2?: {
|
|
2706
|
+
piece: string;
|
|
2707
|
+
joint: string;
|
|
2708
|
+
} | null;
|
|
2689
2709
|
/**
|
|
2690
2710
|
* The rest of the document — module id, endplate identities, the benchwork,
|
|
2691
2711
|
* industries and signals. Merged UNDERNEATH the derived keys, so the graph
|
package/dist/index.d.ts
CHANGED
|
@@ -459,6 +459,16 @@ interface ModuleSchematicDoc {
|
|
|
459
459
|
piece: string;
|
|
460
460
|
joint: string;
|
|
461
461
|
};
|
|
462
|
+
/** Where MAIN 2 starts, on a double-track module — the joint the endplate's
|
|
463
|
+
* SECOND track arrives at. Absent = single track.
|
|
464
|
+
*
|
|
465
|
+
* Two starts rather than a list because a document has exactly two mains,
|
|
466
|
+
* `main` and `main2`. This mirrors the thing downstream instead of inventing
|
|
467
|
+
* a generality nothing can read. */
|
|
468
|
+
start2?: {
|
|
469
|
+
piece: string;
|
|
470
|
+
joint: string;
|
|
471
|
+
} | null;
|
|
462
472
|
} | null;
|
|
463
473
|
}
|
|
464
474
|
/** A benchwork-outline vertex, module-local inches. The edge from this vertex
|
|
@@ -2644,6 +2654,11 @@ interface GraphWalk {
|
|
|
2644
2654
|
* sentence, so a caller can act on it (and say it ONCE) instead of matching
|
|
2645
2655
|
* on prose. */
|
|
2646
2656
|
danglingDiverges: string[];
|
|
2657
|
+
/** Pieces this walk never got to. ⚠️ "Unreached BY THIS WALK" is not the same
|
|
2658
|
+
* as "unreachable": a double-track module's second main is unreached by the
|
|
2659
|
+
* first walk and perfectly well connected. Ids, so a caller running more than
|
|
2660
|
+
* one walk can tell the difference. */
|
|
2661
|
+
unreached: string[];
|
|
2647
2662
|
}
|
|
2648
2663
|
/**
|
|
2649
2664
|
* Walk the graph from an endplate and read the topology off it.
|
|
@@ -2686,6 +2701,11 @@ interface GraphDocInput {
|
|
|
2686
2701
|
piece: string;
|
|
2687
2702
|
joint: string;
|
|
2688
2703
|
};
|
|
2704
|
+
/** Where MAIN 2 begins, on a double-track module. Absent = single track. */
|
|
2705
|
+
start2?: {
|
|
2706
|
+
piece: string;
|
|
2707
|
+
joint: string;
|
|
2708
|
+
} | null;
|
|
2689
2709
|
/**
|
|
2690
2710
|
* The rest of the document — module id, endplate identities, the benchwork,
|
|
2691
2711
|
* industries and signals. Merged UNDERNEATH the derived keys, so the graph
|
package/dist/index.js
CHANGED
|
@@ -2441,6 +2441,7 @@ function snapPiece(moving, others, library = BUILT_IN_TRACK_PARTS, withinInches
|
|
|
2441
2441
|
};
|
|
2442
2442
|
}
|
|
2443
2443
|
var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
|
|
2444
|
+
var unreachedWarning = (id) => `${id} is not reachable from the endplate \u2014 nothing connects it`;
|
|
2444
2445
|
function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
|
|
2445
2446
|
const byKey = new Map(graph.joints.map((j) => [j.key, j]));
|
|
2446
2447
|
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
@@ -2578,11 +2579,14 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2578
2579
|
if (sw && !sw.divergeRoute) sw.divergeRoute = r.id;
|
|
2579
2580
|
}
|
|
2580
2581
|
const reached = new Set(routes.flatMap((r) => r.pieces));
|
|
2582
|
+
const unreached = [];
|
|
2581
2583
|
for (const p of pieces)
|
|
2582
|
-
if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id))
|
|
2583
|
-
warnings.push(
|
|
2584
|
+
if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id)) {
|
|
2585
|
+
warnings.push(unreachedWarning(p.id));
|
|
2586
|
+
unreached.push(p.id);
|
|
2587
|
+
}
|
|
2584
2588
|
for (const c of graph.conflicts) warnings.push(c.reason);
|
|
2585
|
-
return { routes, turnouts, warnings, danglingDiverges };
|
|
2589
|
+
return { routes, turnouts, warnings, danglingDiverges, unreached };
|
|
2586
2590
|
}
|
|
2587
2591
|
function resolveGraphAnchor(anchor, walk, pieces, library = BUILT_IN_TRACK_PARTS) {
|
|
2588
2592
|
const piece = pieces.find((p) => p.id === anchor.piece);
|
|
@@ -2611,14 +2615,29 @@ function graphToDoc(pieces, input) {
|
|
|
2611
2615
|
const epA = endplates[0];
|
|
2612
2616
|
const epB = endplates[1];
|
|
2613
2617
|
const main = walk.routes.find((r) => r.id === "main");
|
|
2614
|
-
const
|
|
2615
|
-
const
|
|
2618
|
+
const walk2 = input.start2 ? walkTrackGraph(graph, pieces, input.start2, library) : null;
|
|
2619
|
+
const main2 = walk2?.routes.find((r) => r.id === "main") ?? null;
|
|
2620
|
+
const claimed = new Set(walk.routes.flatMap((r) => r.pieces));
|
|
2621
|
+
if (main2 && main2.pieces.some((p) => claimed.has(p))) {
|
|
2622
|
+
warnings.push(
|
|
2623
|
+
"Main 2 starts on track that Main 1 already runs along \u2014 they are one run, not two"
|
|
2624
|
+
);
|
|
2625
|
+
}
|
|
2626
|
+
const twoMains = !!main2 && !main2.pieces.some((p) => claimed.has(p));
|
|
2627
|
+
const main2Lane = twoMains ? (graph.joints.find((j) => j.key === `${input.start2.piece}.${input.start2.joint}`)?.y ?? 1) >= 0 ? 1 : -1 : 0;
|
|
2628
|
+
const lengthInches = round(Math.max(main.toPos, twoMains ? main2.toPos : 0));
|
|
2629
|
+
const branches = [
|
|
2630
|
+
...walk.routes.filter((r) => r.id !== "main" && r.pieces.length),
|
|
2631
|
+
...twoMains ? walk2.routes.filter((r) => r.id !== "main" && r.pieces.length).map((r) => ({ ...r, id: `main2:${r.id}`, bornAt: r.bornAt })) : []
|
|
2632
|
+
];
|
|
2616
2633
|
const trackIdOf = /* @__PURE__ */ new Map([["main", MAIN_TRACK_ID]]);
|
|
2634
|
+
if (twoMains) trackIdOf.set("main2:main", MAIN2_TRACK_ID);
|
|
2617
2635
|
for (const r of branches) trackIdOf.set(r.id, r.pieces[0]);
|
|
2618
2636
|
const laneOf = (r) => {
|
|
2619
2637
|
const side = Math.sign(r.lateral) || 1;
|
|
2620
2638
|
const sameSide = branches.filter((x) => (Math.sign(x.lateral) || 1) === side).sort((a, b) => Math.abs(a.lateral) - Math.abs(b.lateral));
|
|
2621
|
-
|
|
2639
|
+
const taken = side === main2Lane ? 1 : 0;
|
|
2640
|
+
return side * (sameSide.indexOf(r) + 1 + taken);
|
|
2622
2641
|
};
|
|
2623
2642
|
const tracks = [
|
|
2624
2643
|
{
|
|
@@ -2627,7 +2646,20 @@ function graphToDoc(pieces, input) {
|
|
|
2627
2646
|
lane: 0,
|
|
2628
2647
|
from: epA?.id ?? "A",
|
|
2629
2648
|
...epB ? { to: epB.id } : {}
|
|
2630
|
-
}
|
|
2649
|
+
},
|
|
2650
|
+
...twoMains ? [
|
|
2651
|
+
{
|
|
2652
|
+
id: MAIN2_TRACK_ID,
|
|
2653
|
+
role: "main",
|
|
2654
|
+
lane: main2Lane,
|
|
2655
|
+
from: epA?.id ?? "A",
|
|
2656
|
+
...epB ? { to: epB.id } : {},
|
|
2657
|
+
// ⚠️ Its own extent, because a second main need not run the whole
|
|
2658
|
+
// module: on a transition module it starts or stops partway.
|
|
2659
|
+
fromPos: round(Math.min(main2.fromPos, main2.toPos)),
|
|
2660
|
+
toPos: round(Math.max(main2.fromPos, main2.toPos))
|
|
2661
|
+
}
|
|
2662
|
+
] : []
|
|
2631
2663
|
];
|
|
2632
2664
|
for (const r of branches) {
|
|
2633
2665
|
const id = trackIdOf.get(r.id);
|
|
@@ -2645,9 +2677,29 @@ function graphToDoc(pieces, input) {
|
|
|
2645
2677
|
...meta.moduleTrackId != null ? { moduleTrackId: meta.moduleTrackId } : {}
|
|
2646
2678
|
});
|
|
2647
2679
|
}
|
|
2680
|
+
if (walk2) {
|
|
2681
|
+
const seen = /* @__PURE__ */ new Set([
|
|
2682
|
+
...walk.routes.flatMap((r) => r.pieces),
|
|
2683
|
+
...walk2.routes.flatMap((r) => r.pieces)
|
|
2684
|
+
]);
|
|
2685
|
+
const covered = new Set([...walk.unreached, ...walk2.unreached].filter((id) => seen.has(id)));
|
|
2686
|
+
const drop = new Set([...covered].map(unreachedWarning));
|
|
2687
|
+
const d2 = new Set(walk2.danglingDiverges.map(danglingDivergeWarning));
|
|
2688
|
+
for (let i = warnings.length - 1; i >= 0; i--) if (drop.has(warnings[i])) warnings.splice(i, 1);
|
|
2689
|
+
for (const w of walk2.warnings)
|
|
2690
|
+
if (!d2.has(w) && !drop.has(w) && !warnings.includes(w)) warnings.push(w);
|
|
2691
|
+
}
|
|
2648
2692
|
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
2649
2693
|
const turnouts = [];
|
|
2650
|
-
|
|
2694
|
+
const allTurnouts = [
|
|
2695
|
+
...walk.turnouts,
|
|
2696
|
+
...twoMains ? walk2.turnouts.map((t) => ({
|
|
2697
|
+
...t,
|
|
2698
|
+
onRoute: t.onRoute === "main" ? "main2:main" : `main2:${t.onRoute}`,
|
|
2699
|
+
divergeRoute: t.divergeRoute ? `main2:${t.divergeRoute}` : null
|
|
2700
|
+
})) : []
|
|
2701
|
+
];
|
|
2702
|
+
for (const t of allTurnouts) {
|
|
2651
2703
|
const diverge = t.divergeRoute ? trackIdOf.get(t.divergeRoute) : void 0;
|
|
2652
2704
|
if (!diverge) {
|
|
2653
2705
|
warnings.push(
|
|
@@ -2729,7 +2781,13 @@ function deriveGraphDoc(doc, library = BUILT_IN_TRACK_PARTS) {
|
|
|
2729
2781
|
...t.moduleTrackId != null ? { moduleTrackId: t.moduleTrackId } : {}
|
|
2730
2782
|
};
|
|
2731
2783
|
}
|
|
2732
|
-
const out = graphToDoc(g.pieces, {
|
|
2784
|
+
const out = graphToDoc(g.pieces, {
|
|
2785
|
+
startAt: g.startAt,
|
|
2786
|
+
start2: g.start2 ?? null,
|
|
2787
|
+
base: doc,
|
|
2788
|
+
meta,
|
|
2789
|
+
library
|
|
2790
|
+
});
|
|
2733
2791
|
return { doc: out.doc, warnings: out.warnings };
|
|
2734
2792
|
}
|
|
2735
2793
|
function frogCasting(cl, opts = {}) {
|