@willcgage/module-schematic 0.60.0 → 0.62.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 +59 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -3
- package/dist/index.d.ts +57 -3
- package/dist/index.js +59 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -117,6 +117,15 @@ function samplePath(pts, segsPerArc = 20) {
|
|
|
117
117
|
out.push({ x: pts[n - 1].x, y: pts[n - 1].y });
|
|
118
118
|
return out;
|
|
119
119
|
}
|
|
120
|
+
function pathLengthInches(path) {
|
|
121
|
+
const pts = trackPath(path);
|
|
122
|
+
if (!pts) return 0;
|
|
123
|
+
const poly = samplePath(pts);
|
|
124
|
+
let total = 0;
|
|
125
|
+
for (let i = 1; i < poly.length; i++)
|
|
126
|
+
total += Math.hypot(poly[i].x - poly[i - 1].x, poly[i].y - poly[i - 1].y);
|
|
127
|
+
return total;
|
|
128
|
+
}
|
|
120
129
|
function trackPath(path) {
|
|
121
130
|
const pts = (path ?? []).filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)).map((p) => ({
|
|
122
131
|
x: p.x,
|
|
@@ -660,7 +669,7 @@ function buildTransition(state) {
|
|
|
660
669
|
}
|
|
661
670
|
function withPoses(endplates, overrides) {
|
|
662
671
|
return endplates.map(
|
|
663
|
-
(e) => overrides[e.id] ? { ...e, pose: overrides[e.id] } : e
|
|
672
|
+
(e) => overrides[e.id] ? { ...e, pose: overrides[e.id], poseAuthored: true } : e
|
|
664
673
|
);
|
|
665
674
|
}
|
|
666
675
|
function withWidths(endplates, widths, offsets = {}) {
|
|
@@ -1682,15 +1691,43 @@ function moduleFeatures(doc) {
|
|
|
1682
1691
|
laneA: trackLane.get(x.tracks?.[0] ?? "") ?? 0,
|
|
1683
1692
|
laneB: trackLane.get(x.tracks?.[1] ?? "") ?? 1
|
|
1684
1693
|
}));
|
|
1694
|
+
const main2Lane = trackLane.has(MAIN2_TRACK_ID) ? trackLane.get(MAIN2_TRACK_ID) : null;
|
|
1695
|
+
const baseLanes = [
|
|
1696
|
+
0,
|
|
1697
|
+
main2Lane ?? (doubleMain ? 1 : 0),
|
|
1698
|
+
...extraTracks.map((t) => t.lane),
|
|
1699
|
+
...signals.map((s) => s.lane),
|
|
1700
|
+
...crossings.flatMap((x) => [x.laneA, x.laneB]),
|
|
1701
|
+
...crossovers.flatMap((x) => [x.fromLane, x.toLane])
|
|
1702
|
+
];
|
|
1703
|
+
let upLane = Math.max(...baseLanes, 0);
|
|
1704
|
+
let downLane = Math.min(...baseLanes, 0);
|
|
1685
1705
|
const branchConnectors = doc.endplates.filter(
|
|
1686
1706
|
(e) => e.id !== "A" && e.id !== "B" && e.at && !!e.trackId && (doc.tracks ?? []).some((t) => t.id === e.trackId)
|
|
1687
1707
|
).map((e) => {
|
|
1708
|
+
const trk = (doc.tracks ?? []).find((t) => t.id === e.trackId);
|
|
1688
1709
|
const sw = (doc.turnouts ?? []).find((t) => t.divergeTrack === e.trackId);
|
|
1710
|
+
const startPos = sw ? sw.pos : e.at.pos;
|
|
1711
|
+
const side = e.at.side === "down" ? "down" : "up";
|
|
1712
|
+
const lane = side === "down" ? --downLane : ++upLane;
|
|
1713
|
+
const runInches = pathLengthInches(trk.path) || Math.abs(e.at.pos - startPos) || FREEMO_ENDPLATE_WIDTH_MIN_INCHES;
|
|
1714
|
+
const toB = e.at.pos >= startPos;
|
|
1715
|
+
const fits = (p) => p >= 0 && p <= len;
|
|
1716
|
+
const ahead = startPos + runInches;
|
|
1717
|
+
const behind = startPos - runInches;
|
|
1718
|
+
const endPos = toB && fits(ahead) ? ahead : !toB && fits(behind) ? behind : fits(toB ? behind : ahead) ? toB ? behind : ahead : ahead;
|
|
1689
1719
|
return {
|
|
1690
1720
|
id: e.id,
|
|
1691
1721
|
label: e.label ?? e.id,
|
|
1692
|
-
|
|
1693
|
-
|
|
1722
|
+
name: trk.trackName ?? "",
|
|
1723
|
+
trackId: trk.id,
|
|
1724
|
+
kind: e.kind === "main" ? "main" : "branch",
|
|
1725
|
+
posFrac: clampFrac(startPos),
|
|
1726
|
+
fromLane: sw ? trackLane.get(sw.onTrack) ?? 0 : 0,
|
|
1727
|
+
side,
|
|
1728
|
+
lane,
|
|
1729
|
+
endFrac: clampFrac(endPos),
|
|
1730
|
+
lengthInches: runInches
|
|
1694
1731
|
};
|
|
1695
1732
|
});
|
|
1696
1733
|
const industries = (doc.industries ?? []).flatMap((ind) => {
|
|
@@ -1715,15 +1752,7 @@ function moduleFeatures(doc) {
|
|
|
1715
1752
|
};
|
|
1716
1753
|
});
|
|
1717
1754
|
});
|
|
1718
|
-
const
|
|
1719
|
-
const allLanes = [
|
|
1720
|
-
0,
|
|
1721
|
-
main2Lane ?? (doubleMain ? 1 : 0),
|
|
1722
|
-
...extraTracks.map((t) => t.lane),
|
|
1723
|
-
...signals.map((s) => s.lane),
|
|
1724
|
-
...crossings.flatMap((x) => [x.laneA, x.laneB]),
|
|
1725
|
-
...crossovers.flatMap((x) => [x.fromLane, x.toLane])
|
|
1726
|
-
];
|
|
1755
|
+
const allLanes = [...baseLanes, ...branchConnectors.map((b) => b.lane)];
|
|
1727
1756
|
const loop = isLoopDoc(doc);
|
|
1728
1757
|
const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);
|
|
1729
1758
|
const main2Positioned = !!main2 && (main2.fromPos != null || main2.toPos != null) && !loop;
|
|
@@ -2004,10 +2033,26 @@ function trackMeetsEndplateIssues(path, pose, opts) {
|
|
|
2004
2033
|
function poseNeedsManual(geometryType) {
|
|
2005
2034
|
return geometryType === "wye" || geometryType === "other";
|
|
2006
2035
|
}
|
|
2036
|
+
function isAxialPose(p) {
|
|
2037
|
+
const h = norm360(p.heading);
|
|
2038
|
+
return Math.abs(p.y) < 1e-6 && (Math.abs(h) < 1e-6 || Math.abs(h - 180) < 1e-6);
|
|
2039
|
+
}
|
|
2007
2040
|
function poseOverridesFromDoc(doc) {
|
|
2008
2041
|
const out = {};
|
|
2009
2042
|
for (const e of doc.endplates ?? []) {
|
|
2010
|
-
if (e.id && e.pose && typeof e.pose.x === "number" && typeof e.pose.y === "number" && typeof e.pose.heading === "number")
|
|
2043
|
+
if (e.id && e.pose && typeof e.pose.x === "number" && typeof e.pose.y === "number" && typeof e.pose.heading === "number" && // Only an AUTHORED pose overrides derivation (#182) — but docs written
|
|
2044
|
+
// before the flag existed have to keep working, so authorship is inferred
|
|
2045
|
+
// for the two cases that can only BE authored:
|
|
2046
|
+
// · a placed branch endplate (`at`) — dropping it on the board is the
|
|
2047
|
+
// gesture, and it has no derivable position at all;
|
|
2048
|
+
// · an OFF-AXIS pose on A/B — a hand-placed plate is off-axis by
|
|
2049
|
+
// definition (that's why it needed hand placing, e.g. a wye's B).
|
|
2050
|
+
// What's left is an axial A/B pose, which is exactly the shape plain
|
|
2051
|
+
// derivation produces — residue. Honouring it silently pins the plate so
|
|
2052
|
+
// it stops following the module and goes stale (FMN-0068's B sat at 48 on
|
|
2053
|
+
// a 47.9″ board). Anything saved from now on carries the flag, so this
|
|
2054
|
+
// guesswork only ever applies to old docs.
|
|
2055
|
+
(e.poseAuthored === true || !!e.at || !isAxialPose(e.pose))) {
|
|
2011
2056
|
out[e.id] = { x: e.pose.x, y: e.pose.y, heading: e.pose.heading };
|
|
2012
2057
|
}
|
|
2013
2058
|
}
|
|
@@ -2065,6 +2110,7 @@ exports.moduleSections = moduleSections;
|
|
|
2065
2110
|
exports.nextId = nextId;
|
|
2066
2111
|
exports.parseXtpLibrary = parseXtpLibrary;
|
|
2067
2112
|
exports.partOutlineAtFrog = partOutlineAtFrog;
|
|
2113
|
+
exports.pathLengthInches = pathLengthInches;
|
|
2068
2114
|
exports.poseNeedsManual = poseNeedsManual;
|
|
2069
2115
|
exports.poseOverridesFromDoc = poseOverridesFromDoc;
|
|
2070
2116
|
exports.remapPos = remapPos;
|