@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.js
CHANGED
|
@@ -869,6 +869,10 @@ function withPoses(endplates, overrides) {
|
|
|
869
869
|
(e) => overrides[e.id] ? { ...e, pose: overrides[e.id], poseAuthored: true } : e
|
|
870
870
|
);
|
|
871
871
|
}
|
|
872
|
+
function withEdges(endplates, edges) {
|
|
873
|
+
if (!edges) return endplates;
|
|
874
|
+
return endplates.map((e) => edges[e.id] ? { ...e, edge: edges[e.id] } : e);
|
|
875
|
+
}
|
|
872
876
|
function withWidths(endplates, widths, offsets = {}) {
|
|
873
877
|
return endplates.map((e) => {
|
|
874
878
|
const w = widths[e.id];
|
|
@@ -900,46 +904,49 @@ function stateToDoc(state, recordNumber) {
|
|
|
900
904
|
...state.loop && state.loopReturn === "main2" ? { loopReturn: "main2" } : {},
|
|
901
905
|
...state.mainsSwapped ? { mainsSwapped: true } : {},
|
|
902
906
|
endplates: withWidths(
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
907
|
+
withEdges(
|
|
908
|
+
withPoses(
|
|
909
|
+
[
|
|
910
|
+
...state.loop ? (
|
|
911
|
+
// Balloon loop: A is the entry. A standard endplate B on the balloon
|
|
912
|
+
// makes it an INTERCHANGE (second route connects at the loop, e.g.
|
|
913
|
+
// Seaford); configB "none" makes it a pure turnback.
|
|
914
|
+
[
|
|
915
|
+
{ id: "A", label: "Entry", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
|
|
916
|
+
...state.configB !== "none" ? [{ id: "B", label: "Interchange", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }] : []
|
|
917
|
+
]
|
|
918
|
+
) : [
|
|
919
|
+
{ id: "A", label: "West", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
|
|
920
|
+
// ⚠️ `configB: "none"` means the module HAS NO FAR ENDPLATE, and
|
|
921
|
+
// that is not loop-only (#184). An *end of the line* or a *pocket*
|
|
922
|
+
// presents one conforming face and the track simply stops. The
|
|
923
|
+
// standard governs the ends a module offers for joining; it never
|
|
924
|
+
// required a module to offer two. This used to coerce "none" to
|
|
925
|
+
// "single" and emit a B regardless, so a single-ended module was
|
|
926
|
+
// impossible to author.
|
|
927
|
+
...state.configB !== "none" ? [
|
|
928
|
+
{
|
|
929
|
+
id: "B",
|
|
930
|
+
label: "East",
|
|
931
|
+
tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }]
|
|
932
|
+
}
|
|
933
|
+
] : []
|
|
934
|
+
],
|
|
935
|
+
// Branch endplates C, D, … — junction connections at pos, off one side
|
|
936
|
+
// (#170). A set can carry several (e.g. a second railroad through).
|
|
937
|
+
...state.branches.map((b, i) => ({
|
|
938
|
+
id: String.fromCharCode(67 + i),
|
|
939
|
+
// C, D, E…
|
|
940
|
+
label: b.label || `Branch ${i + 1}`,
|
|
941
|
+
tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],
|
|
942
|
+
at: { pos: b.pos, side: b.side },
|
|
943
|
+
kind: b.kind ?? "branch",
|
|
944
|
+
...b.trackId ? { trackId: b.trackId } : {}
|
|
945
|
+
}))
|
|
929
946
|
],
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
id: String.fromCharCode(67 + i),
|
|
934
|
-
// C, D, E…
|
|
935
|
-
label: b.label || `Branch ${i + 1}`,
|
|
936
|
-
tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],
|
|
937
|
-
at: { pos: b.pos, side: b.side },
|
|
938
|
-
kind: b.kind ?? "branch",
|
|
939
|
-
...b.trackId ? { trackId: b.trackId } : {}
|
|
940
|
-
}))
|
|
941
|
-
],
|
|
942
|
-
state.poseOverrides
|
|
947
|
+
state.poseOverrides
|
|
948
|
+
),
|
|
949
|
+
state.endplateEdges
|
|
943
950
|
),
|
|
944
951
|
state.endplateWidths,
|
|
945
952
|
state.endplateTrackOffsets
|
|
@@ -1135,7 +1142,15 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
1135
1142
|
const poseOverrides = poseOverridesFromDoc(d);
|
|
1136
1143
|
const endplateWidths = {};
|
|
1137
1144
|
const endplateTrackOffsets = {};
|
|
1145
|
+
const endplateEdges = {};
|
|
1138
1146
|
for (const e of d.endplates ?? []) {
|
|
1147
|
+
if (e.edge && Number.isFinite(e.edge.index) && e.edge.index >= 0)
|
|
1148
|
+
endplateEdges[e.id] = {
|
|
1149
|
+
index: Math.trunc(e.edge.index),
|
|
1150
|
+
...e.edge.section ? { section: e.edge.section } : {},
|
|
1151
|
+
...Number.isFinite(e.edge.fromT) ? { fromT: e.edge.fromT } : {},
|
|
1152
|
+
...Number.isFinite(e.edge.toT) ? { toT: e.edge.toT } : {}
|
|
1153
|
+
};
|
|
1139
1154
|
if (typeof e.widthInches === "number" && e.widthInches > 0)
|
|
1140
1155
|
endplateWidths[e.id] = e.widthInches;
|
|
1141
1156
|
if (typeof e.trackOffsetInches === "number" && Number.isFinite(e.trackOffsetInches))
|
|
@@ -1186,6 +1201,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
1186
1201
|
flexByTrack,
|
|
1187
1202
|
endplateWidths,
|
|
1188
1203
|
endplateTrackOffsets,
|
|
1204
|
+
endplateEdges,
|
|
1189
1205
|
outline,
|
|
1190
1206
|
outlineInner,
|
|
1191
1207
|
sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
|
|
@@ -2227,6 +2243,197 @@ function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
|
|
|
2227
2243
|
}
|
|
2228
2244
|
return { placeable, blocked };
|
|
2229
2245
|
}
|
|
2246
|
+
var JOINT_SNAP_INCHES = 0.01;
|
|
2247
|
+
function placedJoints(pieces, library = BUILT_IN_TRACK_PARTS) {
|
|
2248
|
+
const out = [];
|
|
2249
|
+
const RAD = Math.PI / 180;
|
|
2250
|
+
for (const p of pieces) {
|
|
2251
|
+
const part = library.find((x) => x.id === p.partId);
|
|
2252
|
+
if (!part) continue;
|
|
2253
|
+
const geo = partGeometry(part, library);
|
|
2254
|
+
if (!geo) continue;
|
|
2255
|
+
const c = Math.cos(p.rotationDeg * RAD);
|
|
2256
|
+
const s = Math.sin(p.rotationDeg * RAD);
|
|
2257
|
+
for (const j of geo.joints) {
|
|
2258
|
+
const lx = part.kind === "flex" && j.id === "b" ? p.lengthInches ?? 0 : j.x;
|
|
2259
|
+
const ly = p.flipped ? -j.y : j.y;
|
|
2260
|
+
const h = (p.flipped ? -j.angleDeg : j.angleDeg) + p.rotationDeg;
|
|
2261
|
+
out.push({
|
|
2262
|
+
key: `${p.id}.${j.id}`,
|
|
2263
|
+
piece: p.id,
|
|
2264
|
+
joint: j.id,
|
|
2265
|
+
role: j.role,
|
|
2266
|
+
x: p.x + lx * c - ly * s,
|
|
2267
|
+
y: p.y + lx * s + ly * c,
|
|
2268
|
+
headingDeg: norm360(h)
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
return out;
|
|
2273
|
+
}
|
|
2274
|
+
function buildTrackGraph(pieces, library = BUILT_IN_TRACK_PARTS, snapInches = JOINT_SNAP_INCHES) {
|
|
2275
|
+
const joints = placedJoints(pieces, library);
|
|
2276
|
+
const unplaceable = [];
|
|
2277
|
+
for (const p of pieces) {
|
|
2278
|
+
const part = library.find((x) => x.id === p.partId);
|
|
2279
|
+
if (!part) {
|
|
2280
|
+
unplaceable.push({ piece: p.id, partId: p.partId, why: "no such part in the library" });
|
|
2281
|
+
continue;
|
|
2282
|
+
}
|
|
2283
|
+
const why = partGeometryGap(part);
|
|
2284
|
+
if (why) unplaceable.push({ piece: p.id, partId: p.partId, why });
|
|
2285
|
+
}
|
|
2286
|
+
const groups = [];
|
|
2287
|
+
const taken = /* @__PURE__ */ new Set();
|
|
2288
|
+
for (const j of joints) {
|
|
2289
|
+
if (taken.has(j.key)) continue;
|
|
2290
|
+
const g = [j];
|
|
2291
|
+
taken.add(j.key);
|
|
2292
|
+
for (const k of joints) {
|
|
2293
|
+
if (taken.has(k.key) || k.piece === j.piece) continue;
|
|
2294
|
+
if (Math.hypot(k.x - j.x, k.y - j.y) <= snapInches) {
|
|
2295
|
+
g.push(k);
|
|
2296
|
+
taken.add(k.key);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
groups.push(g);
|
|
2300
|
+
}
|
|
2301
|
+
const connections = [];
|
|
2302
|
+
const open = [];
|
|
2303
|
+
const conflicts = [];
|
|
2304
|
+
for (const g of groups) {
|
|
2305
|
+
if (g.length === 1) open.push(g[0].key);
|
|
2306
|
+
else if (g.length === 2) connections.push({ a: g[0].key, b: g[1].key });
|
|
2307
|
+
else {
|
|
2308
|
+
conflicts.push({
|
|
2309
|
+
x: g[0].x,
|
|
2310
|
+
y: g[0].y,
|
|
2311
|
+
joints: g.map((j) => j.key),
|
|
2312
|
+
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.`
|
|
2313
|
+
});
|
|
2314
|
+
for (const j of g) open.push(j.key);
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
return { joints, connections, open, conflicts, unplaceable };
|
|
2318
|
+
}
|
|
2319
|
+
function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
|
|
2320
|
+
const byKey = new Map(graph.joints.map((j) => [j.key, j]));
|
|
2321
|
+
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
2322
|
+
const link = /* @__PURE__ */ new Map();
|
|
2323
|
+
for (const c of graph.connections) {
|
|
2324
|
+
link.set(c.a, c.b);
|
|
2325
|
+
link.set(c.b, c.a);
|
|
2326
|
+
}
|
|
2327
|
+
const partOf = (pid) => {
|
|
2328
|
+
const p = pieceById.get(pid);
|
|
2329
|
+
return p ? library.find((x) => x.id === p.partId) : void 0;
|
|
2330
|
+
};
|
|
2331
|
+
const geoOf = (pid) => {
|
|
2332
|
+
const part = partOf(pid);
|
|
2333
|
+
return part ? partGeometry(part, library) : null;
|
|
2334
|
+
};
|
|
2335
|
+
const gap = (a, b) => a && b ? Math.hypot(a.x - b.x, a.y - b.y) : 0;
|
|
2336
|
+
const routes = [];
|
|
2337
|
+
const turnouts = [];
|
|
2338
|
+
const warnings = [];
|
|
2339
|
+
const queued = /* @__PURE__ */ new Set();
|
|
2340
|
+
const pending = [];
|
|
2341
|
+
const walk = (startKey, startPos, id, bornAt) => {
|
|
2342
|
+
const route = {
|
|
2343
|
+
id,
|
|
2344
|
+
fromPos: startPos,
|
|
2345
|
+
toPos: startPos,
|
|
2346
|
+
bornAt,
|
|
2347
|
+
endsAt: null,
|
|
2348
|
+
pieces: [],
|
|
2349
|
+
lateral: 0
|
|
2350
|
+
};
|
|
2351
|
+
let cur = startKey;
|
|
2352
|
+
let pos = startPos;
|
|
2353
|
+
const guard = /* @__PURE__ */ new Set();
|
|
2354
|
+
while (cur && !guard.has(cur)) {
|
|
2355
|
+
guard.add(cur);
|
|
2356
|
+
const here = byKey.get(cur);
|
|
2357
|
+
if (!here) break;
|
|
2358
|
+
const geo = geoOf(here.piece);
|
|
2359
|
+
if (!geo) break;
|
|
2360
|
+
const opts = geo.routes.filter((r) => r.includes(here.joint));
|
|
2361
|
+
if (!opts.length) break;
|
|
2362
|
+
const pick = opts.find((r) => r.includes("through")) ?? opts[0];
|
|
2363
|
+
const exitJoint = pick[0] === here.joint ? pick[1] : pick[0];
|
|
2364
|
+
const exit = byKey.get(`${here.piece}.${exitJoint}`);
|
|
2365
|
+
const part = partOf(here.piece);
|
|
2366
|
+
if (part && (part.kind === "turnout" || part.kind === "wye")) {
|
|
2367
|
+
const throat = byKey.get(`${here.piece}.throat`);
|
|
2368
|
+
const through = byKey.get(`${here.piece}.through`) ?? byKey.get(`${here.piece}.legA`);
|
|
2369
|
+
const body = gap(throat, through);
|
|
2370
|
+
const lead = part.lead?.inches ?? body / 2;
|
|
2371
|
+
const dvj = byKey.get(`${here.piece}.diverge`) ?? byKey.get(`${here.piece}.legB`);
|
|
2372
|
+
const frogPt = throat && body > 0 && dvj ? {
|
|
2373
|
+
x: throat.x + (dvj.x - throat.x) * lead / body,
|
|
2374
|
+
y: throat.y + (dvj.y - throat.y) * lead / body
|
|
2375
|
+
} : null;
|
|
2376
|
+
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);
|
|
2377
|
+
const frogPos = pos + toFrog;
|
|
2378
|
+
if (here.joint === "diverge" || here.joint === "legB") {
|
|
2379
|
+
route.endsAt = here.piece;
|
|
2380
|
+
const known = turnouts.find((t) => t.id === here.piece);
|
|
2381
|
+
route.toPos = known ? known.pos : frogPos;
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
turnouts.push({ id: here.piece, pos: frogPos, onRoute: id, divergeRoute: null });
|
|
2385
|
+
for (const dj of ["diverge", "legB"]) {
|
|
2386
|
+
const dk = `${here.piece}.${dj}`;
|
|
2387
|
+
const d = byKey.get(dk);
|
|
2388
|
+
if (!d || queued.has(dk)) continue;
|
|
2389
|
+
queued.add(dk);
|
|
2390
|
+
const fp = throat && body > 0 ? {
|
|
2391
|
+
x: throat.x + (d.x - throat.x) * lead / body,
|
|
2392
|
+
y: throat.y + (d.y - throat.y) * lead / body
|
|
2393
|
+
} : null;
|
|
2394
|
+
pending.push({
|
|
2395
|
+
from: here.piece,
|
|
2396
|
+
at: frogPos,
|
|
2397
|
+
joint: dk,
|
|
2398
|
+
skew: fp ? Math.hypot(d.x - fp.x, d.y - fp.y) : 0
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
route.pieces.push(here.piece);
|
|
2403
|
+
for (const j of graph.joints)
|
|
2404
|
+
if (j.piece === here.piece && Math.abs(j.y) > Math.abs(route.lateral))
|
|
2405
|
+
route.lateral = j.y;
|
|
2406
|
+
pos += gap(here, exit);
|
|
2407
|
+
route.toPos = pos;
|
|
2408
|
+
const next = exit ? link.get(exit.key) : void 0;
|
|
2409
|
+
if (!next) break;
|
|
2410
|
+
cur = next;
|
|
2411
|
+
}
|
|
2412
|
+
return route;
|
|
2413
|
+
};
|
|
2414
|
+
routes.push(walk(`${startAt.piece}.${startAt.joint}`, 0, "main", null));
|
|
2415
|
+
let n = 0;
|
|
2416
|
+
while (pending.length) {
|
|
2417
|
+
const b = pending.shift();
|
|
2418
|
+
const start = link.get(b.joint);
|
|
2419
|
+
if (!start) {
|
|
2420
|
+
warnings.push(`the route diverging at ${b.from} is not connected to anything`);
|
|
2421
|
+
continue;
|
|
2422
|
+
}
|
|
2423
|
+
n += 1;
|
|
2424
|
+
const r = walk(start, b.at + b.skew, `route${n}`, b.from);
|
|
2425
|
+
r.fromPos = b.at;
|
|
2426
|
+
routes.push(r);
|
|
2427
|
+
const sw = turnouts.find((t) => t.id === b.from);
|
|
2428
|
+
if (sw && !sw.divergeRoute) sw.divergeRoute = r.id;
|
|
2429
|
+
}
|
|
2430
|
+
const reached = new Set(routes.flatMap((r) => r.pieces));
|
|
2431
|
+
for (const p of pieces)
|
|
2432
|
+
if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id))
|
|
2433
|
+
warnings.push(`${p.id} is not reachable from the endplate \u2014 nothing connects it`);
|
|
2434
|
+
for (const c of graph.conflicts) warnings.push(c.reason);
|
|
2435
|
+
return { routes, turnouts, warnings };
|
|
2436
|
+
}
|
|
2230
2437
|
function frogCasting(cl, opts = {}) {
|
|
2231
2438
|
const g = opts.gaugeInches ?? RAIL_GAUGE_INCHES;
|
|
2232
2439
|
const w = opts.reachInches ?? g * 1.5;
|
|
@@ -2532,6 +2739,45 @@ function moduleFeatures(doc) {
|
|
|
2532
2739
|
laneMax: Math.max(...allLanes)
|
|
2533
2740
|
};
|
|
2534
2741
|
}
|
|
2742
|
+
function endplateEdgePose(outline, edge) {
|
|
2743
|
+
if (!outline || outline.length < 3) return null;
|
|
2744
|
+
const n = outline.length;
|
|
2745
|
+
const i = Math.trunc(edge.index);
|
|
2746
|
+
if (!Number.isFinite(i) || i < 0 || i >= n) return null;
|
|
2747
|
+
const p0 = outline[i];
|
|
2748
|
+
const p1 = outline[(i + 1) % n];
|
|
2749
|
+
if (p0.bulge) return null;
|
|
2750
|
+
const t0 = Math.max(0, Math.min(1, edge.fromT ?? 0));
|
|
2751
|
+
const t1 = Math.max(0, Math.min(1, edge.toT ?? 1));
|
|
2752
|
+
const a = { x: p0.x + (p1.x - p0.x) * Math.min(t0, t1), y: p0.y + (p1.y - p0.y) * Math.min(t0, t1) };
|
|
2753
|
+
const b = { x: p0.x + (p1.x - p0.x) * Math.max(t0, t1), y: p0.y + (p1.y - p0.y) * Math.max(t0, t1) };
|
|
2754
|
+
const w = Math.hypot(b.x - a.x, b.y - a.y);
|
|
2755
|
+
if (!(w > 0)) return null;
|
|
2756
|
+
let cx = 0;
|
|
2757
|
+
let cy = 0;
|
|
2758
|
+
for (const v of outline) {
|
|
2759
|
+
cx += v.x;
|
|
2760
|
+
cy += v.y;
|
|
2761
|
+
}
|
|
2762
|
+
cx /= n;
|
|
2763
|
+
cy /= n;
|
|
2764
|
+
const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
2765
|
+
const ex = (b.x - a.x) / w;
|
|
2766
|
+
const ey = (b.y - a.y) / w;
|
|
2767
|
+
let nx = ey;
|
|
2768
|
+
let ny = -ex;
|
|
2769
|
+
if ((mid.x - cx) * nx + (mid.y - cy) * ny < 0) {
|
|
2770
|
+
nx = -nx;
|
|
2771
|
+
ny = -ny;
|
|
2772
|
+
}
|
|
2773
|
+
return {
|
|
2774
|
+
x: mid.x,
|
|
2775
|
+
y: mid.y,
|
|
2776
|
+
heading: norm360(Math.atan2(ny, nx) * 180 / Math.PI),
|
|
2777
|
+
widthInches: w,
|
|
2778
|
+
face: [a, b]
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2535
2781
|
function returnLoop(shape, opts) {
|
|
2536
2782
|
const L = Math.max(1, opts.leadInches);
|
|
2537
2783
|
const R = Math.max(1, opts.radius);
|
|
@@ -2635,6 +2881,21 @@ function deriveEndplatePoses(geo) {
|
|
|
2635
2881
|
const half = geo.trackHalfSpacingInches ?? 1;
|
|
2636
2882
|
const cfg = (i) => geo.endplateConfigs?.[i] === "double" ? "double" : "single";
|
|
2637
2883
|
const withOverride = (p) => {
|
|
2884
|
+
const bound = geo.endplateEdges?.[p.id];
|
|
2885
|
+
if (bound) {
|
|
2886
|
+
const outline = (bound.section ? geo.sections?.find((s) => s.id === bound.section)?.outline : geo.outline) ?? geo.outline;
|
|
2887
|
+
const e = endplateEdgePose(outline, bound);
|
|
2888
|
+
if (e)
|
|
2889
|
+
return {
|
|
2890
|
+
...p,
|
|
2891
|
+
x: e.x,
|
|
2892
|
+
y: e.y,
|
|
2893
|
+
heading: e.heading,
|
|
2894
|
+
widthInches: e.widthInches,
|
|
2895
|
+
face: e.face,
|
|
2896
|
+
boundToEdge: true
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2638
2899
|
const o = geo.poseOverrides?.[p.id];
|
|
2639
2900
|
return o ? { ...p, x: o.x, y: o.y, heading: norm360(o.heading), manual: true } : p;
|
|
2640
2901
|
};
|
|
@@ -2806,6 +3067,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
2806
3067
|
return out;
|
|
2807
3068
|
}
|
|
2808
3069
|
|
|
2809
|
-
export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, PINCH_EASE_INCHES, RAIL_GAUGE_INCHES, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity };
|
|
3070
|
+
export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, JOINT_SNAP_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, PINCH_EASE_INCHES, RAIL_GAUGE_INCHES, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTrackGraph, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateEdgePose, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
|
|
2810
3071
|
//# sourceMappingURL=index.js.map
|
|
2811
3072
|
//# sourceMappingURL=index.js.map
|