@willcgage/module-schematic 0.89.0 → 0.91.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 +87 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -1
- package/dist/index.d.ts +54 -1
- package/dist/index.js +86 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2248,6 +2248,54 @@ function partGeometry(part, library = BUILT_IN_TRACK_PARTS) {
|
|
|
2248
2248
|
divergingEndMeasured: de.measured
|
|
2249
2249
|
};
|
|
2250
2250
|
}
|
|
2251
|
+
function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
|
|
2252
|
+
const part = library.find((p) => p.id === piece.partId);
|
|
2253
|
+
if (!part) return [];
|
|
2254
|
+
const geo = partGeometry(part, library);
|
|
2255
|
+
if (!geo) return [];
|
|
2256
|
+
const joints = placedJoints([piece], library);
|
|
2257
|
+
const at = (id) => joints.find((j) => j.joint === id);
|
|
2258
|
+
const RAD = Math.PI / 180;
|
|
2259
|
+
const c = Math.cos(piece.rotationDeg * RAD);
|
|
2260
|
+
const s = Math.sin(piece.rotationDeg * RAD);
|
|
2261
|
+
const place = (x, y) => {
|
|
2262
|
+
const ly = piece.flipped ? -y : y;
|
|
2263
|
+
return { x: piece.x + x * c - ly * s, y: piece.y + x * s + ly * c };
|
|
2264
|
+
};
|
|
2265
|
+
const out = [];
|
|
2266
|
+
for (const route of geo.routes) {
|
|
2267
|
+
const a = at(route[0]);
|
|
2268
|
+
const b = at(route[1]);
|
|
2269
|
+
if (!a || !b) continue;
|
|
2270
|
+
const ends = [{ x: a.x, y: a.y }, { x: b.x, y: b.y }];
|
|
2271
|
+
const diverging = route.some((r) => r === "diverge" || r === "legA" || r === "legB");
|
|
2272
|
+
if (!diverging || part.kind === "flex") {
|
|
2273
|
+
out.push({ route, points: ends });
|
|
2274
|
+
continue;
|
|
2275
|
+
}
|
|
2276
|
+
const lead = part.lead?.inches ?? 0;
|
|
2277
|
+
const pts0 = part.pointsOffset?.inches ?? 0;
|
|
2278
|
+
const far = geo.joints.find((j) => j.id === route[1]) ?? geo.joints.find((j) => j.id === route[0]);
|
|
2279
|
+
if (!far || !(lead > 0)) {
|
|
2280
|
+
out.push({ route, points: ends });
|
|
2281
|
+
continue;
|
|
2282
|
+
}
|
|
2283
|
+
const isWye = part.kind === "wye";
|
|
2284
|
+
const closure = turnoutClosure(isWye ? part.frogNumber * 2 : part.frogNumber, {
|
|
2285
|
+
leadInches: lead
|
|
2286
|
+
});
|
|
2287
|
+
const mirror = route.includes("legB") ? -1 : 1;
|
|
2288
|
+
const pts = [place(0, 0), place(pts0, 0)];
|
|
2289
|
+
const steps = 12;
|
|
2290
|
+
for (let i = 1; i <= steps; i++) {
|
|
2291
|
+
const x = pts0 + (far.x - pts0) * i / steps;
|
|
2292
|
+
pts.push(place(x, mirror * closure.offsetAt(x - pts0)));
|
|
2293
|
+
}
|
|
2294
|
+
pts[pts.length - 1] = { x: b.x, y: b.y };
|
|
2295
|
+
out.push({ route, points: route[0] === "throat" ? pts : pts.slice().reverse() });
|
|
2296
|
+
}
|
|
2297
|
+
return out;
|
|
2298
|
+
}
|
|
2251
2299
|
function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
|
|
2252
2300
|
const placeable = [];
|
|
2253
2301
|
const blocked = [];
|
|
@@ -2332,6 +2380,37 @@ function buildTrackGraph(pieces, library = BUILT_IN_TRACK_PARTS, snapInches = JO
|
|
|
2332
2380
|
}
|
|
2333
2381
|
return { joints, connections, open, conflicts, unplaceable };
|
|
2334
2382
|
}
|
|
2383
|
+
function snapPiece(moving, others, library = BUILT_IN_TRACK_PARTS, withinInches = 0.5) {
|
|
2384
|
+
const mine = placedJoints([moving], library);
|
|
2385
|
+
if (!mine.length) return null;
|
|
2386
|
+
const graph = buildTrackGraph(others, library);
|
|
2387
|
+
const taken = new Set(graph.connections.flatMap((c2) => [c2.a, c2.b]));
|
|
2388
|
+
const open = graph.joints.filter((j) => !taken.has(j.key));
|
|
2389
|
+
let best = null;
|
|
2390
|
+
for (const m of mine)
|
|
2391
|
+
for (const t of open) {
|
|
2392
|
+
const d = Math.hypot(t.x - m.x, t.y - m.y);
|
|
2393
|
+
if (d <= withinInches && (!best || d < best.d)) best = { m, t, d };
|
|
2394
|
+
}
|
|
2395
|
+
if (!best) return null;
|
|
2396
|
+
const RAD = Math.PI / 180;
|
|
2397
|
+
const dRot = norm360(best.t.headingDeg + 180 - best.m.headingDeg);
|
|
2398
|
+
const c = Math.cos(dRot * RAD);
|
|
2399
|
+
const s = Math.sin(dRot * RAD);
|
|
2400
|
+
const ox = moving.x - best.m.x;
|
|
2401
|
+
const oy = moving.y - best.m.y;
|
|
2402
|
+
return {
|
|
2403
|
+
piece: {
|
|
2404
|
+
...moving,
|
|
2405
|
+
rotationDeg: norm360(moving.rotationDeg + dRot),
|
|
2406
|
+
x: best.t.x + ox * c - oy * s,
|
|
2407
|
+
y: best.t.y + ox * s + oy * c
|
|
2408
|
+
},
|
|
2409
|
+
from: best.m.key,
|
|
2410
|
+
to: best.t.key
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
|
|
2335
2414
|
function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
|
|
2336
2415
|
const byKey = new Map(graph.joints.map((j) => [j.key, j]));
|
|
2337
2416
|
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
@@ -2352,6 +2431,7 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2352
2431
|
const routes = [];
|
|
2353
2432
|
const turnouts = [];
|
|
2354
2433
|
const warnings = [];
|
|
2434
|
+
const danglingDiverges = [];
|
|
2355
2435
|
const queued = /* @__PURE__ */ new Set();
|
|
2356
2436
|
const pending = [];
|
|
2357
2437
|
const walk = (startKey, startPos, id, bornAt) => {
|
|
@@ -2442,7 +2522,8 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2442
2522
|
const b = pending.shift();
|
|
2443
2523
|
const start = link.get(b.joint);
|
|
2444
2524
|
if (!start) {
|
|
2445
|
-
warnings.push(
|
|
2525
|
+
warnings.push(danglingDivergeWarning(b.from));
|
|
2526
|
+
danglingDiverges.push(b.from);
|
|
2446
2527
|
continue;
|
|
2447
2528
|
}
|
|
2448
2529
|
const already = routes.find((r2) => r2.pieces.includes(byKey.get(start).piece));
|
|
@@ -2463,7 +2544,7 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2463
2544
|
if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id))
|
|
2464
2545
|
warnings.push(`${p.id} is not reachable from the endplate \u2014 nothing connects it`);
|
|
2465
2546
|
for (const c of graph.conflicts) warnings.push(c.reason);
|
|
2466
|
-
return { routes, turnouts, warnings };
|
|
2547
|
+
return { routes, turnouts, warnings, danglingDiverges };
|
|
2467
2548
|
}
|
|
2468
2549
|
function resolveGraphAnchor(anchor, walk, pieces, library = BUILT_IN_TRACK_PARTS) {
|
|
2469
2550
|
const piece = pieces.find((p) => p.id === anchor.piece);
|
|
@@ -2484,7 +2565,8 @@ function graphToDoc(pieces, input) {
|
|
|
2484
2565
|
const library = input.library ?? BUILT_IN_TRACK_PARTS;
|
|
2485
2566
|
const graph = buildTrackGraph(pieces, library);
|
|
2486
2567
|
const walk = walkTrackGraph(graph, pieces, input.startAt, library);
|
|
2487
|
-
const
|
|
2568
|
+
const dangling = new Set(walk.danglingDiverges.map(danglingDivergeWarning));
|
|
2569
|
+
const warnings = walk.warnings.filter((w) => !dangling.has(w));
|
|
2488
2570
|
const round = (n) => Math.round(n * 100) / 100;
|
|
2489
2571
|
const base = input.base ?? {};
|
|
2490
2572
|
const endplates = base.endplates && base.endplates.length ? base.endplates : [{ id: "A", label: "West" }, { id: "B", label: "East" }];
|
|
@@ -3327,6 +3409,7 @@ exports.partOutlineAtFrog = partOutlineAtFrog;
|
|
|
3327
3409
|
exports.partsPlaceable = partsPlaceable;
|
|
3328
3410
|
exports.pastFrogInchesForSize = pastFrogInchesForSize;
|
|
3329
3411
|
exports.pathLengthInches = pathLengthInches;
|
|
3412
|
+
exports.pieceRoutePaths = pieceRoutePaths;
|
|
3330
3413
|
exports.placedJoints = placedJoints;
|
|
3331
3414
|
exports.poseNeedsManual = poseNeedsManual;
|
|
3332
3415
|
exports.poseOverridesFromDoc = poseOverridesFromDoc;
|
|
@@ -3349,6 +3432,7 @@ exports.sectionSpansOrWhole = sectionSpansOrWhole;
|
|
|
3349
3432
|
exports.sectionedCenterline = sectionedCenterline;
|
|
3350
3433
|
exports.sectionedEndPose = sectionedEndPose;
|
|
3351
3434
|
exports.sliceCenterline = sliceCenterline;
|
|
3435
|
+
exports.snapPiece = snapPiece;
|
|
3352
3436
|
exports.spanOverhang = spanOverhang;
|
|
3353
3437
|
exports.stateToDoc = stateToDoc;
|
|
3354
3438
|
exports.storedPartToTrackPart = storedPartToTrackPart;
|