@willcgage/module-schematic 0.89.0 → 0.90.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 +80 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +79 -1
- 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,36 @@ 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
|
+
}
|
|
2335
2413
|
function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
|
|
2336
2414
|
const byKey = new Map(graph.joints.map((j) => [j.key, j]));
|
|
2337
2415
|
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
@@ -3327,6 +3405,7 @@ exports.partOutlineAtFrog = partOutlineAtFrog;
|
|
|
3327
3405
|
exports.partsPlaceable = partsPlaceable;
|
|
3328
3406
|
exports.pastFrogInchesForSize = pastFrogInchesForSize;
|
|
3329
3407
|
exports.pathLengthInches = pathLengthInches;
|
|
3408
|
+
exports.pieceRoutePaths = pieceRoutePaths;
|
|
3330
3409
|
exports.placedJoints = placedJoints;
|
|
3331
3410
|
exports.poseNeedsManual = poseNeedsManual;
|
|
3332
3411
|
exports.poseOverridesFromDoc = poseOverridesFromDoc;
|
|
@@ -3349,6 +3428,7 @@ exports.sectionSpansOrWhole = sectionSpansOrWhole;
|
|
|
3349
3428
|
exports.sectionedCenterline = sectionedCenterline;
|
|
3350
3429
|
exports.sectionedEndPose = sectionedEndPose;
|
|
3351
3430
|
exports.sliceCenterline = sliceCenterline;
|
|
3431
|
+
exports.snapPiece = snapPiece;
|
|
3352
3432
|
exports.spanOverhang = spanOverhang;
|
|
3353
3433
|
exports.stateToDoc = stateToDoc;
|
|
3354
3434
|
exports.storedPartToTrackPart = storedPartToTrackPart;
|