@willcgage/module-schematic 0.84.0 → 0.86.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 +305 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +206 -1
- package/dist/index.d.ts +206 -1
- package/dist/index.js +301 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -871,6 +871,10 @@ function withPoses(endplates, overrides) {
|
|
|
871
871
|
(e) => overrides[e.id] ? { ...e, pose: overrides[e.id], poseAuthored: true } : e
|
|
872
872
|
);
|
|
873
873
|
}
|
|
874
|
+
function withEdges(endplates, edges) {
|
|
875
|
+
if (!edges) return endplates;
|
|
876
|
+
return endplates.map((e) => edges[e.id] ? { ...e, edge: edges[e.id] } : e);
|
|
877
|
+
}
|
|
874
878
|
function withWidths(endplates, widths, offsets = {}) {
|
|
875
879
|
return endplates.map((e) => {
|
|
876
880
|
const w = widths[e.id];
|
|
@@ -902,46 +906,49 @@ function stateToDoc(state, recordNumber) {
|
|
|
902
906
|
...state.loop && state.loopReturn === "main2" ? { loopReturn: "main2" } : {},
|
|
903
907
|
...state.mainsSwapped ? { mainsSwapped: true } : {},
|
|
904
908
|
endplates: withWidths(
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
909
|
+
withEdges(
|
|
910
|
+
withPoses(
|
|
911
|
+
[
|
|
912
|
+
...state.loop ? (
|
|
913
|
+
// Balloon loop: A is the entry. A standard endplate B on the balloon
|
|
914
|
+
// makes it an INTERCHANGE (second route connects at the loop, e.g.
|
|
915
|
+
// Seaford); configB "none" makes it a pure turnback.
|
|
916
|
+
[
|
|
917
|
+
{ id: "A", label: "Entry", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
|
|
918
|
+
...state.configB !== "none" ? [{ id: "B", label: "Interchange", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }] : []
|
|
919
|
+
]
|
|
920
|
+
) : [
|
|
921
|
+
{ id: "A", label: "West", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
|
|
922
|
+
// ⚠️ `configB: "none"` means the module HAS NO FAR ENDPLATE, and
|
|
923
|
+
// that is not loop-only (#184). An *end of the line* or a *pocket*
|
|
924
|
+
// presents one conforming face and the track simply stops. The
|
|
925
|
+
// standard governs the ends a module offers for joining; it never
|
|
926
|
+
// required a module to offer two. This used to coerce "none" to
|
|
927
|
+
// "single" and emit a B regardless, so a single-ended module was
|
|
928
|
+
// impossible to author.
|
|
929
|
+
...state.configB !== "none" ? [
|
|
930
|
+
{
|
|
931
|
+
id: "B",
|
|
932
|
+
label: "East",
|
|
933
|
+
tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }]
|
|
934
|
+
}
|
|
935
|
+
] : []
|
|
936
|
+
],
|
|
937
|
+
// Branch endplates C, D, … — junction connections at pos, off one side
|
|
938
|
+
// (#170). A set can carry several (e.g. a second railroad through).
|
|
939
|
+
...state.branches.map((b, i) => ({
|
|
940
|
+
id: String.fromCharCode(67 + i),
|
|
941
|
+
// C, D, E…
|
|
942
|
+
label: b.label || `Branch ${i + 1}`,
|
|
943
|
+
tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],
|
|
944
|
+
at: { pos: b.pos, side: b.side },
|
|
945
|
+
kind: b.kind ?? "branch",
|
|
946
|
+
...b.trackId ? { trackId: b.trackId } : {}
|
|
947
|
+
}))
|
|
931
948
|
],
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
id: String.fromCharCode(67 + i),
|
|
936
|
-
// C, D, E…
|
|
937
|
-
label: b.label || `Branch ${i + 1}`,
|
|
938
|
-
tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],
|
|
939
|
-
at: { pos: b.pos, side: b.side },
|
|
940
|
-
kind: b.kind ?? "branch",
|
|
941
|
-
...b.trackId ? { trackId: b.trackId } : {}
|
|
942
|
-
}))
|
|
943
|
-
],
|
|
944
|
-
state.poseOverrides
|
|
949
|
+
state.poseOverrides
|
|
950
|
+
),
|
|
951
|
+
state.endplateEdges
|
|
945
952
|
),
|
|
946
953
|
state.endplateWidths,
|
|
947
954
|
state.endplateTrackOffsets
|
|
@@ -1137,7 +1144,15 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
1137
1144
|
const poseOverrides = poseOverridesFromDoc(d);
|
|
1138
1145
|
const endplateWidths = {};
|
|
1139
1146
|
const endplateTrackOffsets = {};
|
|
1147
|
+
const endplateEdges = {};
|
|
1140
1148
|
for (const e of d.endplates ?? []) {
|
|
1149
|
+
if (e.edge && Number.isFinite(e.edge.index) && e.edge.index >= 0)
|
|
1150
|
+
endplateEdges[e.id] = {
|
|
1151
|
+
index: Math.trunc(e.edge.index),
|
|
1152
|
+
...e.edge.section ? { section: e.edge.section } : {},
|
|
1153
|
+
...Number.isFinite(e.edge.fromT) ? { fromT: e.edge.fromT } : {},
|
|
1154
|
+
...Number.isFinite(e.edge.toT) ? { toT: e.edge.toT } : {}
|
|
1155
|
+
};
|
|
1141
1156
|
if (typeof e.widthInches === "number" && e.widthInches > 0)
|
|
1142
1157
|
endplateWidths[e.id] = e.widthInches;
|
|
1143
1158
|
if (typeof e.trackOffsetInches === "number" && Number.isFinite(e.trackOffsetInches))
|
|
@@ -1188,6 +1203,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
1188
1203
|
flexByTrack,
|
|
1189
1204
|
endplateWidths,
|
|
1190
1205
|
endplateTrackOffsets,
|
|
1206
|
+
endplateEdges,
|
|
1191
1207
|
outline,
|
|
1192
1208
|
outlineInner,
|
|
1193
1209
|
sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
|
|
@@ -2229,6 +2245,197 @@ function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
|
|
|
2229
2245
|
}
|
|
2230
2246
|
return { placeable, blocked };
|
|
2231
2247
|
}
|
|
2248
|
+
var JOINT_SNAP_INCHES = 0.01;
|
|
2249
|
+
function placedJoints(pieces, library = BUILT_IN_TRACK_PARTS) {
|
|
2250
|
+
const out = [];
|
|
2251
|
+
const RAD = Math.PI / 180;
|
|
2252
|
+
for (const p of pieces) {
|
|
2253
|
+
const part = library.find((x) => x.id === p.partId);
|
|
2254
|
+
if (!part) continue;
|
|
2255
|
+
const geo = partGeometry(part, library);
|
|
2256
|
+
if (!geo) continue;
|
|
2257
|
+
const c = Math.cos(p.rotationDeg * RAD);
|
|
2258
|
+
const s = Math.sin(p.rotationDeg * RAD);
|
|
2259
|
+
for (const j of geo.joints) {
|
|
2260
|
+
const lx = part.kind === "flex" && j.id === "b" ? p.lengthInches ?? 0 : j.x;
|
|
2261
|
+
const ly = p.flipped ? -j.y : j.y;
|
|
2262
|
+
const h = (p.flipped ? -j.angleDeg : j.angleDeg) + p.rotationDeg;
|
|
2263
|
+
out.push({
|
|
2264
|
+
key: `${p.id}.${j.id}`,
|
|
2265
|
+
piece: p.id,
|
|
2266
|
+
joint: j.id,
|
|
2267
|
+
role: j.role,
|
|
2268
|
+
x: p.x + lx * c - ly * s,
|
|
2269
|
+
y: p.y + lx * s + ly * c,
|
|
2270
|
+
headingDeg: norm360(h)
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
return out;
|
|
2275
|
+
}
|
|
2276
|
+
function buildTrackGraph(pieces, library = BUILT_IN_TRACK_PARTS, snapInches = JOINT_SNAP_INCHES) {
|
|
2277
|
+
const joints = placedJoints(pieces, library);
|
|
2278
|
+
const unplaceable = [];
|
|
2279
|
+
for (const p of pieces) {
|
|
2280
|
+
const part = library.find((x) => x.id === p.partId);
|
|
2281
|
+
if (!part) {
|
|
2282
|
+
unplaceable.push({ piece: p.id, partId: p.partId, why: "no such part in the library" });
|
|
2283
|
+
continue;
|
|
2284
|
+
}
|
|
2285
|
+
const why = partGeometryGap(part);
|
|
2286
|
+
if (why) unplaceable.push({ piece: p.id, partId: p.partId, why });
|
|
2287
|
+
}
|
|
2288
|
+
const groups = [];
|
|
2289
|
+
const taken = /* @__PURE__ */ new Set();
|
|
2290
|
+
for (const j of joints) {
|
|
2291
|
+
if (taken.has(j.key)) continue;
|
|
2292
|
+
const g = [j];
|
|
2293
|
+
taken.add(j.key);
|
|
2294
|
+
for (const k of joints) {
|
|
2295
|
+
if (taken.has(k.key) || k.piece === j.piece) continue;
|
|
2296
|
+
if (Math.hypot(k.x - j.x, k.y - j.y) <= snapInches) {
|
|
2297
|
+
g.push(k);
|
|
2298
|
+
taken.add(k.key);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
groups.push(g);
|
|
2302
|
+
}
|
|
2303
|
+
const connections = [];
|
|
2304
|
+
const open = [];
|
|
2305
|
+
const conflicts = [];
|
|
2306
|
+
for (const g of groups) {
|
|
2307
|
+
if (g.length === 1) open.push(g[0].key);
|
|
2308
|
+
else if (g.length === 2) connections.push({ a: g[0].key, b: g[1].key });
|
|
2309
|
+
else {
|
|
2310
|
+
conflicts.push({
|
|
2311
|
+
x: g[0].x,
|
|
2312
|
+
y: g[0].y,
|
|
2313
|
+
joints: g.map((j) => j.key),
|
|
2314
|
+
reason: `${g.length} track ends are stacked in one place. Rail has two ends, so a junction of three is a turnout, not a joint \u2014 none of these are joined until one is moved.`
|
|
2315
|
+
});
|
|
2316
|
+
for (const j of g) open.push(j.key);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
return { joints, connections, open, conflicts, unplaceable };
|
|
2320
|
+
}
|
|
2321
|
+
function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
|
|
2322
|
+
const byKey = new Map(graph.joints.map((j) => [j.key, j]));
|
|
2323
|
+
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
2324
|
+
const link = /* @__PURE__ */ new Map();
|
|
2325
|
+
for (const c of graph.connections) {
|
|
2326
|
+
link.set(c.a, c.b);
|
|
2327
|
+
link.set(c.b, c.a);
|
|
2328
|
+
}
|
|
2329
|
+
const partOf = (pid) => {
|
|
2330
|
+
const p = pieceById.get(pid);
|
|
2331
|
+
return p ? library.find((x) => x.id === p.partId) : void 0;
|
|
2332
|
+
};
|
|
2333
|
+
const geoOf = (pid) => {
|
|
2334
|
+
const part = partOf(pid);
|
|
2335
|
+
return part ? partGeometry(part, library) : null;
|
|
2336
|
+
};
|
|
2337
|
+
const gap = (a, b) => a && b ? Math.hypot(a.x - b.x, a.y - b.y) : 0;
|
|
2338
|
+
const routes = [];
|
|
2339
|
+
const turnouts = [];
|
|
2340
|
+
const warnings = [];
|
|
2341
|
+
const queued = /* @__PURE__ */ new Set();
|
|
2342
|
+
const pending = [];
|
|
2343
|
+
const walk = (startKey, startPos, id, bornAt) => {
|
|
2344
|
+
const route = {
|
|
2345
|
+
id,
|
|
2346
|
+
fromPos: startPos,
|
|
2347
|
+
toPos: startPos,
|
|
2348
|
+
bornAt,
|
|
2349
|
+
endsAt: null,
|
|
2350
|
+
pieces: [],
|
|
2351
|
+
lateral: 0
|
|
2352
|
+
};
|
|
2353
|
+
let cur = startKey;
|
|
2354
|
+
let pos = startPos;
|
|
2355
|
+
const guard = /* @__PURE__ */ new Set();
|
|
2356
|
+
while (cur && !guard.has(cur)) {
|
|
2357
|
+
guard.add(cur);
|
|
2358
|
+
const here = byKey.get(cur);
|
|
2359
|
+
if (!here) break;
|
|
2360
|
+
const geo = geoOf(here.piece);
|
|
2361
|
+
if (!geo) break;
|
|
2362
|
+
const opts = geo.routes.filter((r) => r.includes(here.joint));
|
|
2363
|
+
if (!opts.length) break;
|
|
2364
|
+
const pick = opts.find((r) => r.includes("through")) ?? opts[0];
|
|
2365
|
+
const exitJoint = pick[0] === here.joint ? pick[1] : pick[0];
|
|
2366
|
+
const exit = byKey.get(`${here.piece}.${exitJoint}`);
|
|
2367
|
+
const part = partOf(here.piece);
|
|
2368
|
+
if (part && (part.kind === "turnout" || part.kind === "wye")) {
|
|
2369
|
+
const throat = byKey.get(`${here.piece}.throat`);
|
|
2370
|
+
const through = byKey.get(`${here.piece}.through`) ?? byKey.get(`${here.piece}.legA`);
|
|
2371
|
+
const body = gap(throat, through);
|
|
2372
|
+
const lead = part.lead?.inches ?? body / 2;
|
|
2373
|
+
const dvj = byKey.get(`${here.piece}.diverge`) ?? byKey.get(`${here.piece}.legB`);
|
|
2374
|
+
const frogPt = throat && body > 0 && dvj ? {
|
|
2375
|
+
x: throat.x + (dvj.x - throat.x) * lead / body,
|
|
2376
|
+
y: throat.y + (dvj.y - throat.y) * lead / body
|
|
2377
|
+
} : null;
|
|
2378
|
+
const toFrog = here.joint === "throat" ? lead : here.joint === "diverge" || here.joint === "legB" ? frogPt && dvj ? Math.hypot(dvj.x - frogPt.x, dvj.y - frogPt.y) : Math.max(0, body - lead) : Math.max(0, body - lead);
|
|
2379
|
+
const frogPos = pos + toFrog;
|
|
2380
|
+
if (here.joint === "diverge" || here.joint === "legB") {
|
|
2381
|
+
route.endsAt = here.piece;
|
|
2382
|
+
const known = turnouts.find((t) => t.id === here.piece);
|
|
2383
|
+
route.toPos = known ? known.pos : frogPos;
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
turnouts.push({ id: here.piece, pos: frogPos, onRoute: id, divergeRoute: null });
|
|
2387
|
+
for (const dj of ["diverge", "legB"]) {
|
|
2388
|
+
const dk = `${here.piece}.${dj}`;
|
|
2389
|
+
const d = byKey.get(dk);
|
|
2390
|
+
if (!d || queued.has(dk)) continue;
|
|
2391
|
+
queued.add(dk);
|
|
2392
|
+
const fp = throat && body > 0 ? {
|
|
2393
|
+
x: throat.x + (d.x - throat.x) * lead / body,
|
|
2394
|
+
y: throat.y + (d.y - throat.y) * lead / body
|
|
2395
|
+
} : null;
|
|
2396
|
+
pending.push({
|
|
2397
|
+
from: here.piece,
|
|
2398
|
+
at: frogPos,
|
|
2399
|
+
joint: dk,
|
|
2400
|
+
skew: fp ? Math.hypot(d.x - fp.x, d.y - fp.y) : 0
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
route.pieces.push(here.piece);
|
|
2405
|
+
for (const j of graph.joints)
|
|
2406
|
+
if (j.piece === here.piece && Math.abs(j.y) > Math.abs(route.lateral))
|
|
2407
|
+
route.lateral = j.y;
|
|
2408
|
+
pos += gap(here, exit);
|
|
2409
|
+
route.toPos = pos;
|
|
2410
|
+
const next = exit ? link.get(exit.key) : void 0;
|
|
2411
|
+
if (!next) break;
|
|
2412
|
+
cur = next;
|
|
2413
|
+
}
|
|
2414
|
+
return route;
|
|
2415
|
+
};
|
|
2416
|
+
routes.push(walk(`${startAt.piece}.${startAt.joint}`, 0, "main", null));
|
|
2417
|
+
let n = 0;
|
|
2418
|
+
while (pending.length) {
|
|
2419
|
+
const b = pending.shift();
|
|
2420
|
+
const start = link.get(b.joint);
|
|
2421
|
+
if (!start) {
|
|
2422
|
+
warnings.push(`the route diverging at ${b.from} is not connected to anything`);
|
|
2423
|
+
continue;
|
|
2424
|
+
}
|
|
2425
|
+
n += 1;
|
|
2426
|
+
const r = walk(start, b.at + b.skew, `route${n}`, b.from);
|
|
2427
|
+
r.fromPos = b.at;
|
|
2428
|
+
routes.push(r);
|
|
2429
|
+
const sw = turnouts.find((t) => t.id === b.from);
|
|
2430
|
+
if (sw && !sw.divergeRoute) sw.divergeRoute = r.id;
|
|
2431
|
+
}
|
|
2432
|
+
const reached = new Set(routes.flatMap((r) => r.pieces));
|
|
2433
|
+
for (const p of pieces)
|
|
2434
|
+
if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id))
|
|
2435
|
+
warnings.push(`${p.id} is not reachable from the endplate \u2014 nothing connects it`);
|
|
2436
|
+
for (const c of graph.conflicts) warnings.push(c.reason);
|
|
2437
|
+
return { routes, turnouts, warnings };
|
|
2438
|
+
}
|
|
2232
2439
|
function frogCasting(cl, opts = {}) {
|
|
2233
2440
|
const g = opts.gaugeInches ?? RAIL_GAUGE_INCHES;
|
|
2234
2441
|
const w = opts.reachInches ?? g * 1.5;
|
|
@@ -2534,6 +2741,45 @@ function moduleFeatures(doc) {
|
|
|
2534
2741
|
laneMax: Math.max(...allLanes)
|
|
2535
2742
|
};
|
|
2536
2743
|
}
|
|
2744
|
+
function endplateEdgePose(outline, edge) {
|
|
2745
|
+
if (!outline || outline.length < 3) return null;
|
|
2746
|
+
const n = outline.length;
|
|
2747
|
+
const i = Math.trunc(edge.index);
|
|
2748
|
+
if (!Number.isFinite(i) || i < 0 || i >= n) return null;
|
|
2749
|
+
const p0 = outline[i];
|
|
2750
|
+
const p1 = outline[(i + 1) % n];
|
|
2751
|
+
if (p0.bulge) return null;
|
|
2752
|
+
const t0 = Math.max(0, Math.min(1, edge.fromT ?? 0));
|
|
2753
|
+
const t1 = Math.max(0, Math.min(1, edge.toT ?? 1));
|
|
2754
|
+
const a = { x: p0.x + (p1.x - p0.x) * Math.min(t0, t1), y: p0.y + (p1.y - p0.y) * Math.min(t0, t1) };
|
|
2755
|
+
const b = { x: p0.x + (p1.x - p0.x) * Math.max(t0, t1), y: p0.y + (p1.y - p0.y) * Math.max(t0, t1) };
|
|
2756
|
+
const w = Math.hypot(b.x - a.x, b.y - a.y);
|
|
2757
|
+
if (!(w > 0)) return null;
|
|
2758
|
+
let cx = 0;
|
|
2759
|
+
let cy = 0;
|
|
2760
|
+
for (const v of outline) {
|
|
2761
|
+
cx += v.x;
|
|
2762
|
+
cy += v.y;
|
|
2763
|
+
}
|
|
2764
|
+
cx /= n;
|
|
2765
|
+
cy /= n;
|
|
2766
|
+
const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
2767
|
+
const ex = (b.x - a.x) / w;
|
|
2768
|
+
const ey = (b.y - a.y) / w;
|
|
2769
|
+
let nx = ey;
|
|
2770
|
+
let ny = -ex;
|
|
2771
|
+
if ((mid.x - cx) * nx + (mid.y - cy) * ny < 0) {
|
|
2772
|
+
nx = -nx;
|
|
2773
|
+
ny = -ny;
|
|
2774
|
+
}
|
|
2775
|
+
return {
|
|
2776
|
+
x: mid.x,
|
|
2777
|
+
y: mid.y,
|
|
2778
|
+
heading: norm360(Math.atan2(ny, nx) * 180 / Math.PI),
|
|
2779
|
+
widthInches: w,
|
|
2780
|
+
face: [a, b]
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2537
2783
|
function returnLoop(shape, opts) {
|
|
2538
2784
|
const L = Math.max(1, opts.leadInches);
|
|
2539
2785
|
const R = Math.max(1, opts.radius);
|
|
@@ -2637,6 +2883,21 @@ function deriveEndplatePoses(geo) {
|
|
|
2637
2883
|
const half = geo.trackHalfSpacingInches ?? 1;
|
|
2638
2884
|
const cfg = (i) => geo.endplateConfigs?.[i] === "double" ? "double" : "single";
|
|
2639
2885
|
const withOverride = (p) => {
|
|
2886
|
+
const bound = geo.endplateEdges?.[p.id];
|
|
2887
|
+
if (bound) {
|
|
2888
|
+
const outline = (bound.section ? geo.sections?.find((s) => s.id === bound.section)?.outline : geo.outline) ?? geo.outline;
|
|
2889
|
+
const e = endplateEdgePose(outline, bound);
|
|
2890
|
+
if (e)
|
|
2891
|
+
return {
|
|
2892
|
+
...p,
|
|
2893
|
+
x: e.x,
|
|
2894
|
+
y: e.y,
|
|
2895
|
+
heading: e.heading,
|
|
2896
|
+
widthInches: e.widthInches,
|
|
2897
|
+
face: e.face,
|
|
2898
|
+
boundToEdge: true
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2640
2901
|
const o = geo.poseOverrides?.[p.id];
|
|
2641
2902
|
return o ? { ...p, x: o.x, y: o.y, heading: norm360(o.heading), manual: true } : p;
|
|
2642
2903
|
};
|
|
@@ -2822,6 +3083,7 @@ exports.FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = FREEMO_ENDPLATE_TRACK_FA
|
|
|
2822
3083
|
exports.FREEMO_ENDPLATE_WIDTH_MIN_INCHES = FREEMO_ENDPLATE_WIDTH_MIN_INCHES;
|
|
2823
3084
|
exports.FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
2824
3085
|
exports.FREEMO_TRACK_SPACING_INCHES = FREEMO_TRACK_SPACING_INCHES;
|
|
3086
|
+
exports.JOINT_SNAP_INCHES = JOINT_SNAP_INCHES;
|
|
2825
3087
|
exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
|
|
2826
3088
|
exports.MAIN_TRACK_ID = MAIN_TRACK_ID;
|
|
2827
3089
|
exports.N_CAR_LENGTH_INCHES = N_CAR_LENGTH_INCHES;
|
|
@@ -2838,6 +3100,7 @@ exports.benchworkBand = benchworkBand;
|
|
|
2838
3100
|
exports.benchworkOutline = benchworkOutline;
|
|
2839
3101
|
exports.buildCrossover = buildCrossover;
|
|
2840
3102
|
exports.buildPassingSiding = buildPassingSiding;
|
|
3103
|
+
exports.buildTrackGraph = buildTrackGraph;
|
|
2841
3104
|
exports.buildTransition = buildTransition;
|
|
2842
3105
|
exports.carCapacity = carCapacity;
|
|
2843
3106
|
exports.checkEndplateWidth = checkEndplateWidth;
|
|
@@ -2848,6 +3111,7 @@ exports.divergeSideForHand = divergeSideForHand;
|
|
|
2848
3111
|
exports.docToState = docToState;
|
|
2849
3112
|
exports.emptyEditorState = emptyEditorState;
|
|
2850
3113
|
exports.endplateCentreOffsetInches = endplateCentreOffsetInches;
|
|
3114
|
+
exports.endplateEdgePose = endplateEdgePose;
|
|
2851
3115
|
exports.endplateFaceSegments = endplateFaceSegments;
|
|
2852
3116
|
exports.endplateLead = endplateLead;
|
|
2853
3117
|
exports.endplateTrackOffsetInches = endplateTrackOffsetInches;
|
|
@@ -2885,6 +3149,7 @@ exports.partOutlineAtFrog = partOutlineAtFrog;
|
|
|
2885
3149
|
exports.partsPlaceable = partsPlaceable;
|
|
2886
3150
|
exports.pastFrogInchesForSize = pastFrogInchesForSize;
|
|
2887
3151
|
exports.pathLengthInches = pathLengthInches;
|
|
3152
|
+
exports.placedJoints = placedJoints;
|
|
2888
3153
|
exports.poseNeedsManual = poseNeedsManual;
|
|
2889
3154
|
exports.poseOverridesFromDoc = poseOverridesFromDoc;
|
|
2890
3155
|
exports.remapPos = remapPos;
|
|
@@ -2917,5 +3182,6 @@ exports.turnoutFacing = turnoutFacing;
|
|
|
2917
3182
|
exports.turnoutOccupiedSpan = turnoutOccupiedSpan;
|
|
2918
3183
|
exports.turnoutPartForSize = turnoutPartForSize;
|
|
2919
3184
|
exports.usableCapacity = usableCapacity;
|
|
3185
|
+
exports.walkTrackGraph = walkTrackGraph;
|
|
2920
3186
|
//# sourceMappingURL=index.cjs.map
|
|
2921
3187
|
//# sourceMappingURL=index.cjs.map
|