@willcgage/module-schematic 0.97.0 → 0.98.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 +89 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -1
- package/dist/index.d.ts +36 -1
- package/dist/index.js +89 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -229,7 +229,7 @@ function moduleCenterline(input) {
|
|
|
229
229
|
if (drawn) return samplePath(drawn);
|
|
230
230
|
const chained = sectionedCenterline(input);
|
|
231
231
|
if (chained.length >= 2) return chained;
|
|
232
|
-
if (!input.geometryType) return [];
|
|
232
|
+
if (!input.geometryType && !input.hasPlacedTrack) return [];
|
|
233
233
|
const L = input.lengthInches > 0 ? input.lengthInches : 24;
|
|
234
234
|
const gt = input.geometryType;
|
|
235
235
|
if (gt === "dead_end") return [{ x: 0, y: 0 }];
|
|
@@ -2313,6 +2313,16 @@ function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
|
|
|
2313
2313
|
const len = part.overallLength?.inches ?? BUMPER_DRAWN_INCHES;
|
|
2314
2314
|
return [{ route: ["a", "a"], points: [{ x: at2.x, y: at2.y }, place(len, 0)] }];
|
|
2315
2315
|
}
|
|
2316
|
+
if (part.kind === "curve") {
|
|
2317
|
+
const a = joints.find((j) => j.joint === "a");
|
|
2318
|
+
const b = joints.find((j) => j.joint === "b");
|
|
2319
|
+
if (!a || !b) return out;
|
|
2320
|
+
const pts = flexRunPoints(sectionalArcInches(part), part.radius.inches).map(
|
|
2321
|
+
(q) => place(q.x, q.y)
|
|
2322
|
+
);
|
|
2323
|
+
pts[pts.length - 1] = { x: b.x, y: b.y };
|
|
2324
|
+
return [{ route: ["a", "b"], points: pts }];
|
|
2325
|
+
}
|
|
2316
2326
|
for (const route of geo.routes) {
|
|
2317
2327
|
const a = at(route[0]);
|
|
2318
2328
|
const b = at(route[1]);
|
|
@@ -2543,6 +2553,83 @@ function fitFlexBetween(piece, others, library = BUILT_IN_TRACK_PARTS, withinInc
|
|
|
2543
2553
|
lengthInches: Math.round(len * 1e3) / 1e3
|
|
2544
2554
|
};
|
|
2545
2555
|
}
|
|
2556
|
+
function insertIntoRun(pieces, fresh, at, library = BUILT_IN_TRACK_PARTS, withinInches = 1) {
|
|
2557
|
+
let host = null;
|
|
2558
|
+
let bestD = Infinity;
|
|
2559
|
+
for (const piece of pieces) {
|
|
2560
|
+
const part = library.find((x) => x.id === piece.partId);
|
|
2561
|
+
if (part?.kind !== "flex") continue;
|
|
2562
|
+
for (const { points } of pieceRoutePaths(piece, library))
|
|
2563
|
+
for (let i = 1; i < points.length; i++) {
|
|
2564
|
+
const d = distanceToSegment(at, points[i - 1], points[i]);
|
|
2565
|
+
if (d < bestD) {
|
|
2566
|
+
bestD = d;
|
|
2567
|
+
host = piece;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
if (!host || bestD > withinInches) return null;
|
|
2572
|
+
const L = host.lengthInches ?? 0;
|
|
2573
|
+
const R = host.radiusInches;
|
|
2574
|
+
const steps = 200;
|
|
2575
|
+
let s = 0;
|
|
2576
|
+
let closest = Infinity;
|
|
2577
|
+
for (let i = 0; i <= steps; i++) {
|
|
2578
|
+
const t = L * i / steps;
|
|
2579
|
+
const local = flexRunEnd(t, R);
|
|
2580
|
+
const world = placeLocal(host, local.x, local.y);
|
|
2581
|
+
const d = Math.hypot(world.x - at.x, world.y - at.y);
|
|
2582
|
+
if (d < closest) {
|
|
2583
|
+
closest = d;
|
|
2584
|
+
s = t;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
const cutLocal = flexRunEnd(s, R);
|
|
2588
|
+
const cut = placeLocal(host, cutLocal.x, cutLocal.y);
|
|
2589
|
+
const heading = norm360(host.rotationDeg + (host.flipped ? -cutLocal.headingDeg : cutLocal.headingDeg));
|
|
2590
|
+
const placed = { ...fresh, x: cut.x, y: cut.y, rotationDeg: heading };
|
|
2591
|
+
const js = placedJoints([placed], library);
|
|
2592
|
+
const entry = js.find((j) => j.joint === "throat" || j.joint === "a");
|
|
2593
|
+
const exit = js.find((j) => j.joint === "through" || j.joint === "b");
|
|
2594
|
+
if (!entry || !exit) return null;
|
|
2595
|
+
const body = Math.hypot(exit.x - entry.x, exit.y - entry.y);
|
|
2596
|
+
const rest = L - s - body;
|
|
2597
|
+
if (s < 0 || rest < 0) return null;
|
|
2598
|
+
const ids = new Set(pieces.map((p) => p.id));
|
|
2599
|
+
let n = 1;
|
|
2600
|
+
while (ids.has(`${host.id}b${n}`)) n += 1;
|
|
2601
|
+
const tail = {
|
|
2602
|
+
...host,
|
|
2603
|
+
id: `${host.id}b${n}`,
|
|
2604
|
+
x: exit.x,
|
|
2605
|
+
y: exit.y,
|
|
2606
|
+
rotationDeg: exit.headingDeg,
|
|
2607
|
+
lengthInches: rest
|
|
2608
|
+
};
|
|
2609
|
+
const out = pieces.map((p) => p.id === host.id ? { ...p, lengthInches: s } : p);
|
|
2610
|
+
const kept = out.filter((p) => p.id !== host.id || s > 0);
|
|
2611
|
+
return {
|
|
2612
|
+
pieces: [...kept, placed, ...rest > 0 ? [tail] : []],
|
|
2613
|
+
hostId: host.id,
|
|
2614
|
+
insertedId: placed.id
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
function placeLocal(piece, x, y) {
|
|
2618
|
+
const rad = piece.rotationDeg * Math.PI / 180;
|
|
2619
|
+
const ly = piece.flipped ? -y : y;
|
|
2620
|
+
return {
|
|
2621
|
+
x: piece.x + x * Math.cos(rad) - ly * Math.sin(rad),
|
|
2622
|
+
y: piece.y + x * Math.sin(rad) + ly * Math.cos(rad)
|
|
2623
|
+
};
|
|
2624
|
+
}
|
|
2625
|
+
function distanceToSegment(p, a, b) {
|
|
2626
|
+
const vx = b.x - a.x;
|
|
2627
|
+
const vy = b.y - a.y;
|
|
2628
|
+
const len2 = vx * vx + vy * vy;
|
|
2629
|
+
if (len2 === 0) return Math.hypot(p.x - a.x, p.y - a.y);
|
|
2630
|
+
const t = Math.max(0, Math.min(1, ((p.x - a.x) * vx + (p.y - a.y) * vy) / len2));
|
|
2631
|
+
return Math.hypot(p.x - (a.x + t * vx), p.y - (a.y + t * vy));
|
|
2632
|
+
}
|
|
2546
2633
|
var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
|
|
2547
2634
|
var unreachedWarning = (id) => `${id} is not reachable from the endplate \u2014 nothing connects it`;
|
|
2548
2635
|
function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
|
|
@@ -3620,6 +3707,7 @@ exports.graphToDoc = graphToDoc;
|
|
|
3620
3707
|
exports.hasNoFarEndplate = hasNoFarEndplate;
|
|
3621
3708
|
exports.importedPartToTrackPart = importedPartToTrackPart;
|
|
3622
3709
|
exports.inchesToScaleFeet = inchesToScaleFeet;
|
|
3710
|
+
exports.insertIntoRun = insertIntoRun;
|
|
3623
3711
|
exports.isLoopDoc = isLoopDoc;
|
|
3624
3712
|
exports.isTransitionTurnout = isTransitionTurnout;
|
|
3625
3713
|
exports.laneOffsetAt = laneOffsetAt;
|